@codyswann/lisa 2.94.0 → 2.95.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -82,7 +82,7 @@
82
82
  "lodash": ">=4.18.1"
83
83
  },
84
84
  "name": "@codyswann/lisa",
85
- "version": "2.94.0",
85
+ "version": "2.95.0",
86
86
  "description": "Claude Code governance framework that applies guardrails, guidance, and automated enforcement to projects",
87
87
  "main": "dist/index.js",
88
88
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "2.94.0",
3
+ "version": "2.95.0",
4
4
  "description": "Universal governance — agents, skills, commands, hooks, and rules for all projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "2.94.0",
3
+ "version": "2.95.0",
4
4
  "description": "Universal governance: agents, skills, commands, hooks, and rules for all projects.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -0,0 +1,157 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Shared queue-health classification helpers for `/lisa:queue-status`.
4
+ *
5
+ * Queue readers normalize vendor-specific lifecycle data into the small set of
6
+ * signals below so queue-status can distinguish quiet, healthy, stuck, and
7
+ * misconfigured queues without inventing a second lifecycle vocabulary.
8
+ */
9
+
10
+ export const QUEUE_HEALTH_VERDICTS = [
11
+ "IDLE",
12
+ "HEALTHY",
13
+ "ATTENTION_NEEDED",
14
+ "MISCONFIGURED",
15
+ ];
16
+
17
+ /**
18
+ * @typedef {"IDLE" | "HEALTHY" | "ATTENTION_NEEDED" | "MISCONFIGURED"} QueueHealthVerdict
19
+ *
20
+ * @typedef {{
21
+ * readonly queueResolved?: boolean
22
+ * readonly namespaceAdopted?: boolean
23
+ * readonly readyCount?: number
24
+ * readonly activeCount?: number
25
+ * readonly blockedCount?: number
26
+ * readonly stalledCount?: number
27
+ * readonly resolutionError?: string | null
28
+ * }} QueueHealthInput
29
+ */
30
+
31
+ /**
32
+ * Classify a queue using the same high-level concepts intake and repair-intake
33
+ * already rely on:
34
+ * - queue must resolve from config;
35
+ * - lifecycle namespace must be adopted/present;
36
+ * - blocked or stalled work means operator attention is needed;
37
+ * - otherwise ready or active work is healthy;
38
+ * - otherwise the queue is truly idle.
39
+ *
40
+ * @param {QueueHealthInput} input
41
+ * @returns {{
42
+ * readonly verdict: QueueHealthVerdict
43
+ * readonly reasons: readonly string[]
44
+ * readonly counts: Readonly<{
45
+ * ready: number
46
+ * active: number
47
+ * blocked: number
48
+ * stalled: number
49
+ * attentionNeeded: number
50
+ * }>
51
+ * }}
52
+ */
53
+ export function classifyQueueHealth(input = {}) {
54
+ const counts = {
55
+ ready: normalizeCount(input.readyCount),
56
+ active: normalizeCount(input.activeCount),
57
+ blocked: normalizeCount(input.blockedCount),
58
+ stalled: normalizeCount(input.stalledCount),
59
+ };
60
+ const attentionNeeded = counts.blocked + counts.stalled;
61
+
62
+ if (input.queueResolved === false || hasContent(input.resolutionError)) {
63
+ return {
64
+ verdict: "MISCONFIGURED",
65
+ reasons: ["queue-unresolved"],
66
+ counts: {
67
+ ...counts,
68
+ attentionNeeded,
69
+ },
70
+ };
71
+ }
72
+
73
+ if (input.namespaceAdopted === false) {
74
+ return {
75
+ verdict: "MISCONFIGURED",
76
+ reasons: ["lifecycle-namespace-absent"],
77
+ counts: {
78
+ ...counts,
79
+ attentionNeeded,
80
+ },
81
+ };
82
+ }
83
+
84
+ if (attentionNeeded > 0) {
85
+ return {
86
+ verdict: "ATTENTION_NEEDED",
87
+ reasons: [
88
+ counts.blocked > 0 ? "blocked-work-present" : null,
89
+ counts.stalled > 0 ? "stalled-work-present" : null,
90
+ ].filter(Boolean),
91
+ counts: {
92
+ ...counts,
93
+ attentionNeeded,
94
+ },
95
+ };
96
+ }
97
+
98
+ if (counts.ready > 0 || counts.active > 0) {
99
+ return {
100
+ verdict: "HEALTHY",
101
+ reasons: [
102
+ counts.ready > 0 ? "ready-work-present" : null,
103
+ counts.active > 0 ? "active-work-in-flight" : null,
104
+ ].filter(Boolean),
105
+ counts: {
106
+ ...counts,
107
+ attentionNeeded,
108
+ },
109
+ };
110
+ }
111
+
112
+ return {
113
+ verdict: "IDLE",
114
+ reasons: ["no-actionable-work"],
115
+ counts: {
116
+ ...counts,
117
+ attentionNeeded,
118
+ },
119
+ };
120
+ }
121
+
122
+ /**
123
+ * Combine individual queue verdicts into the overall queue-status verdict.
124
+ *
125
+ * @param {readonly { verdict: QueueHealthVerdict }[]} sections
126
+ * @returns {QueueHealthVerdict}
127
+ */
128
+ export function computeOverallQueueVerdict(sections) {
129
+ const verdicts = sections.map(section => section.verdict);
130
+
131
+ if (verdicts.includes("MISCONFIGURED")) {
132
+ return "MISCONFIGURED";
133
+ }
134
+ if (verdicts.includes("ATTENTION_NEEDED")) {
135
+ return "ATTENTION_NEEDED";
136
+ }
137
+ if (verdicts.includes("HEALTHY")) {
138
+ return "HEALTHY";
139
+ }
140
+ return "IDLE";
141
+ }
142
+
143
+ /**
144
+ * @param {number | null | undefined} value
145
+ * @returns {number}
146
+ */
147
+ function normalizeCount(value) {
148
+ return Number.isFinite(value) && value > 0 ? Math.trunc(value) : 0;
149
+ }
150
+
151
+ /**
152
+ * @param {string | null | undefined} value
153
+ * @returns {boolean}
154
+ */
155
+ function hasContent(value) {
156
+ return typeof value === "string" && value.trim().length > 0;
157
+ }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "2.94.0",
3
+ "version": "2.95.0",
4
4
  "description": "AWS CDK-specific plugin",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "2.94.0",
3
+ "version": "2.95.0",
4
4
  "description": "AWS CDK-specific Lisa plugin.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-expo",
3
- "version": "2.94.0",
3
+ "version": "2.95.0",
4
4
  "description": "Expo/React Native-specific skills, agents, rules, and MCP servers",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-expo",
3
- "version": "2.94.0",
3
+ "version": "2.95.0",
4
4
  "description": "Expo and React Native-specific skills, agents, rules, and MCP servers.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-harper-fabric",
3
- "version": "2.94.0",
3
+ "version": "2.95.0",
4
4
  "description": "Harper/Fabric-specific rules for TypeScript component apps",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-harper-fabric",
3
- "version": "2.94.0",
3
+ "version": "2.95.0",
4
4
  "description": "Harper/Fabric-specific Lisa rules for TypeScript component apps.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-nestjs",
3
- "version": "2.94.0",
3
+ "version": "2.95.0",
4
4
  "description": "NestJS-specific skills (GraphQL, TypeORM) and hooks (migration write-protection)",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-nestjs",
3
- "version": "2.94.0",
3
+ "version": "2.95.0",
4
4
  "description": "NestJS-specific skills and migration write-protection hooks.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-openclaw",
3
- "version": "2.94.0",
3
+ "version": "2.95.0",
4
4
  "description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-openclaw",
3
- "version": "2.94.0",
3
+ "version": "2.95.0",
4
4
  "description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, across Claude and Codex.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-rails",
3
- "version": "2.94.0",
3
+ "version": "2.95.0",
4
4
  "description": "Ruby on Rails-specific hooks — RuboCop linting/formatting and ast-grep scanning on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-rails",
3
- "version": "2.94.0",
3
+ "version": "2.95.0",
4
4
  "description": "Ruby on Rails-specific skills and hooks for RuboCop and ast-grep scanning on edit.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-typescript",
3
- "version": "2.94.0",
3
+ "version": "2.95.0",
4
4
  "description": "TypeScript-specific hooks — Prettier formatting, ESLint linting, and ast-grep scanning on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-typescript",
3
- "version": "2.94.0",
3
+ "version": "2.95.0",
4
4
  "description": "TypeScript-specific hooks for formatting, linting, and ast-grep scanning on edit.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-wiki",
3
- "version": "2.94.0",
3
+ "version": "2.95.0",
4
4
  "description": "LLM Wiki — a distributable, git-native markdown knowledge base for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-wiki",
3
- "version": "2.94.0",
3
+ "version": "2.95.0",
4
4
  "description": "Distributable LLM Wiki kernel — ingest, query, lint, and maintain a git-native markdown knowledge base across Claude and Codex.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -0,0 +1,157 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Shared queue-health classification helpers for `/lisa:queue-status`.
4
+ *
5
+ * Queue readers normalize vendor-specific lifecycle data into the small set of
6
+ * signals below so queue-status can distinguish quiet, healthy, stuck, and
7
+ * misconfigured queues without inventing a second lifecycle vocabulary.
8
+ */
9
+
10
+ export const QUEUE_HEALTH_VERDICTS = [
11
+ "IDLE",
12
+ "HEALTHY",
13
+ "ATTENTION_NEEDED",
14
+ "MISCONFIGURED",
15
+ ];
16
+
17
+ /**
18
+ * @typedef {"IDLE" | "HEALTHY" | "ATTENTION_NEEDED" | "MISCONFIGURED"} QueueHealthVerdict
19
+ *
20
+ * @typedef {{
21
+ * readonly queueResolved?: boolean
22
+ * readonly namespaceAdopted?: boolean
23
+ * readonly readyCount?: number
24
+ * readonly activeCount?: number
25
+ * readonly blockedCount?: number
26
+ * readonly stalledCount?: number
27
+ * readonly resolutionError?: string | null
28
+ * }} QueueHealthInput
29
+ */
30
+
31
+ /**
32
+ * Classify a queue using the same high-level concepts intake and repair-intake
33
+ * already rely on:
34
+ * - queue must resolve from config;
35
+ * - lifecycle namespace must be adopted/present;
36
+ * - blocked or stalled work means operator attention is needed;
37
+ * - otherwise ready or active work is healthy;
38
+ * - otherwise the queue is truly idle.
39
+ *
40
+ * @param {QueueHealthInput} input
41
+ * @returns {{
42
+ * readonly verdict: QueueHealthVerdict
43
+ * readonly reasons: readonly string[]
44
+ * readonly counts: Readonly<{
45
+ * ready: number
46
+ * active: number
47
+ * blocked: number
48
+ * stalled: number
49
+ * attentionNeeded: number
50
+ * }>
51
+ * }}
52
+ */
53
+ export function classifyQueueHealth(input = {}) {
54
+ const counts = {
55
+ ready: normalizeCount(input.readyCount),
56
+ active: normalizeCount(input.activeCount),
57
+ blocked: normalizeCount(input.blockedCount),
58
+ stalled: normalizeCount(input.stalledCount),
59
+ };
60
+ const attentionNeeded = counts.blocked + counts.stalled;
61
+
62
+ if (input.queueResolved === false || hasContent(input.resolutionError)) {
63
+ return {
64
+ verdict: "MISCONFIGURED",
65
+ reasons: ["queue-unresolved"],
66
+ counts: {
67
+ ...counts,
68
+ attentionNeeded,
69
+ },
70
+ };
71
+ }
72
+
73
+ if (input.namespaceAdopted === false) {
74
+ return {
75
+ verdict: "MISCONFIGURED",
76
+ reasons: ["lifecycle-namespace-absent"],
77
+ counts: {
78
+ ...counts,
79
+ attentionNeeded,
80
+ },
81
+ };
82
+ }
83
+
84
+ if (attentionNeeded > 0) {
85
+ return {
86
+ verdict: "ATTENTION_NEEDED",
87
+ reasons: [
88
+ counts.blocked > 0 ? "blocked-work-present" : null,
89
+ counts.stalled > 0 ? "stalled-work-present" : null,
90
+ ].filter(Boolean),
91
+ counts: {
92
+ ...counts,
93
+ attentionNeeded,
94
+ },
95
+ };
96
+ }
97
+
98
+ if (counts.ready > 0 || counts.active > 0) {
99
+ return {
100
+ verdict: "HEALTHY",
101
+ reasons: [
102
+ counts.ready > 0 ? "ready-work-present" : null,
103
+ counts.active > 0 ? "active-work-in-flight" : null,
104
+ ].filter(Boolean),
105
+ counts: {
106
+ ...counts,
107
+ attentionNeeded,
108
+ },
109
+ };
110
+ }
111
+
112
+ return {
113
+ verdict: "IDLE",
114
+ reasons: ["no-actionable-work"],
115
+ counts: {
116
+ ...counts,
117
+ attentionNeeded,
118
+ },
119
+ };
120
+ }
121
+
122
+ /**
123
+ * Combine individual queue verdicts into the overall queue-status verdict.
124
+ *
125
+ * @param {readonly { verdict: QueueHealthVerdict }[]} sections
126
+ * @returns {QueueHealthVerdict}
127
+ */
128
+ export function computeOverallQueueVerdict(sections) {
129
+ const verdicts = sections.map(section => section.verdict);
130
+
131
+ if (verdicts.includes("MISCONFIGURED")) {
132
+ return "MISCONFIGURED";
133
+ }
134
+ if (verdicts.includes("ATTENTION_NEEDED")) {
135
+ return "ATTENTION_NEEDED";
136
+ }
137
+ if (verdicts.includes("HEALTHY")) {
138
+ return "HEALTHY";
139
+ }
140
+ return "IDLE";
141
+ }
142
+
143
+ /**
144
+ * @param {number | null | undefined} value
145
+ * @returns {number}
146
+ */
147
+ function normalizeCount(value) {
148
+ return Number.isFinite(value) && value > 0 ? Math.trunc(value) : 0;
149
+ }
150
+
151
+ /**
152
+ * @param {string | null | undefined} value
153
+ * @returns {boolean}
154
+ */
155
+ function hasContent(value) {
156
+ return typeof value === "string" && value.trim().length > 0;
157
+ }