@codyswann/lisa 2.265.0 → 2.266.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.
Files changed (138) hide show
  1. package/dist/core/lisa-plugin-selection.d.ts +11 -0
  2. package/dist/core/lisa-plugin-selection.d.ts.map +1 -1
  3. package/dist/core/lisa-plugin-selection.js +11 -1
  4. package/dist/core/lisa-plugin-selection.js.map +1 -1
  5. package/dist/core/lisa.d.ts +0 -41
  6. package/dist/core/lisa.d.ts.map +1 -1
  7. package/dist/core/lisa.js +23 -104
  8. package/dist/core/lisa.js.map +1 -1
  9. package/dist/core/template-ownership.d.ts +46 -0
  10. package/dist/core/template-ownership.d.ts.map +1 -0
  11. package/dist/core/template-ownership.js +123 -0
  12. package/dist/core/template-ownership.js.map +1 -0
  13. package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
  14. package/dist/core/upstream-evidence-manifest.js +20 -1
  15. package/dist/core/upstream-evidence-manifest.js.map +1 -1
  16. package/dist/health/deadline.d.ts +25 -0
  17. package/dist/health/deadline.d.ts.map +1 -0
  18. package/dist/health/deadline.js +50 -0
  19. package/dist/health/deadline.js.map +1 -0
  20. package/dist/health/deterministic.d.ts +34 -0
  21. package/dist/health/deterministic.d.ts.map +1 -0
  22. package/dist/health/deterministic.js +206 -0
  23. package/dist/health/deterministic.js.map +1 -0
  24. package/dist/health/finding-utils.d.ts +17 -0
  25. package/dist/health/finding-utils.d.ts.map +1 -0
  26. package/dist/health/finding-utils.js +25 -0
  27. package/dist/health/finding-utils.js.map +1 -0
  28. package/dist/health/governance-probes.d.ts +51 -0
  29. package/dist/health/governance-probes.d.ts.map +1 -0
  30. package/dist/health/governance-probes.js +94 -0
  31. package/dist/health/governance-probes.js.map +1 -0
  32. package/dist/health/hook-inspection.d.ts +35 -0
  33. package/dist/health/hook-inspection.d.ts.map +1 -0
  34. package/dist/health/hook-inspection.js +123 -0
  35. package/dist/health/hook-inspection.js.map +1 -0
  36. package/dist/health/index.d.ts +1 -0
  37. package/dist/health/index.d.ts.map +1 -1
  38. package/dist/health/index.js +1 -0
  39. package/dist/health/index.js.map +1 -1
  40. package/dist/health/plugin-inspection.d.ts +37 -0
  41. package/dist/health/plugin-inspection.d.ts.map +1 -0
  42. package/dist/health/plugin-inspection.js +128 -0
  43. package/dist/health/plugin-inspection.js.map +1 -0
  44. package/dist/health/project-probes.d.ts +34 -0
  45. package/dist/health/project-probes.d.ts.map +1 -0
  46. package/dist/health/project-probes.js +104 -0
  47. package/dist/health/project-probes.js.map +1 -0
  48. package/dist/health/read-only-fs.d.ts +54 -0
  49. package/dist/health/read-only-fs.d.ts.map +1 -0
  50. package/dist/health/read-only-fs.js +165 -0
  51. package/dist/health/read-only-fs.js.map +1 -0
  52. package/dist/health/ruleset-inspection.d.ts +57 -0
  53. package/dist/health/ruleset-inspection.d.ts.map +1 -0
  54. package/dist/health/ruleset-inspection.js +315 -0
  55. package/dist/health/ruleset-inspection.js.map +1 -0
  56. package/dist/health/template-inspection.d.ts +67 -0
  57. package/dist/health/template-inspection.d.ts.map +1 -0
  58. package/dist/health/template-inspection.js +469 -0
  59. package/dist/health/template-inspection.js.map +1 -0
  60. package/dist/strategies/copy-contents.d.ts +7 -25
  61. package/dist/strategies/copy-contents.d.ts.map +1 -1
  62. package/dist/strategies/copy-contents.js +32 -63
  63. package/dist/strategies/copy-contents.js.map +1 -1
  64. package/dist/strategies/merge.d.ts +7 -0
  65. package/dist/strategies/merge.d.ts.map +1 -1
  66. package/dist/strategies/merge.js +10 -1
  67. package/dist/strategies/merge.js.map +1 -1
  68. package/dist/strategies/package-lisa.d.ts +10 -1
  69. package/dist/strategies/package-lisa.d.ts.map +1 -1
  70. package/dist/strategies/package-lisa.js +18 -20
  71. package/dist/strategies/package-lisa.js.map +1 -1
  72. package/dist/strategies/tagged-merge.d.ts +7 -0
  73. package/dist/strategies/tagged-merge.d.ts.map +1 -1
  74. package/dist/strategies/tagged-merge.js +10 -1
  75. package/dist/strategies/tagged-merge.js.map +1 -1
  76. package/dist/sync/config-sync.d.ts +7 -0
  77. package/dist/sync/config-sync.d.ts.map +1 -1
  78. package/dist/sync/config-sync.js +23 -16
  79. package/dist/sync/config-sync.js.map +1 -1
  80. package/dist/utils/ignore-patterns.d.ts +18 -0
  81. package/dist/utils/ignore-patterns.d.ts.map +1 -1
  82. package/dist/utils/ignore-patterns.js +2 -2
  83. package/dist/utils/ignore-patterns.js.map +1 -1
  84. package/package.json +3 -2
  85. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  86. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  87. package/plugins/lisa-agy/plugin.json +1 -1
  88. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  89. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  90. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  91. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  92. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  93. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  94. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  95. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  96. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  97. package/plugins/lisa-expo-agy/plugin.json +1 -1
  98. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  99. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  100. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  101. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  102. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  103. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  104. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  105. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  106. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  107. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  108. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  109. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  110. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  111. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  112. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  113. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  114. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  115. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  116. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  117. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  118. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  119. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  120. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  121. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  122. package/plugins/lisa-rails-agy/plugin.json +1 -1
  123. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  124. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  125. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  126. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  127. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  128. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  129. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  130. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  131. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  132. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  133. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  134. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  135. package/scripts/detect-stale-workflow-inputs.mjs +21 -148
  136. package/scripts/lib/reusable-workflow-contract.d.mts +24 -0
  137. package/scripts/lib/reusable-workflow-contract.mjs +123 -0
  138. package/scripts/verify-health-deterministic-built.mjs +499 -0
@@ -42,11 +42,26 @@ import fs from "node:fs";
42
42
  import path from "node:path";
43
43
  import process from "node:process";
44
44
  import { fileURLToPath, pathToFileURL } from "node:url";
45
+ import {
46
+ diffStaleKeys,
47
+ extractCallerJobs,
48
+ extractDeclaredInputs,
49
+ parseReusableReference,
50
+ } from "./lib/reusable-workflow-contract.mjs";
51
+
52
+ export {
53
+ diffStaleKeys,
54
+ extractCallerJobs,
55
+ extractDeclaredInputs,
56
+ parseReusableReference,
57
+ };
45
58
 
46
59
  const REPO_ROOT = path.resolve(
47
60
  path.dirname(fileURLToPath(import.meta.url)),
48
61
  ".."
49
62
  );
63
+ const LISA_OWNER = "codyswanngt";
64
+ const LISA_REPO = "lisa";
50
65
 
51
66
  /**
52
67
  * Usage error — thrown by `parseArgs` for an invalid invocation so `main` can
@@ -68,153 +83,6 @@ function isDirectory(target) {
68
83
  }
69
84
  }
70
85
 
71
- /**
72
- * Leading-whitespace width of a line (its indentation level).
73
- *
74
- * @param {string} line - a single source line.
75
- * @returns {number} number of leading whitespace characters.
76
- */
77
- function indentOf(line) {
78
- return line.length - line.trimStart().length;
79
- }
80
-
81
- /**
82
- * Collect the immediate child `key:` names nested directly under a block
83
- * opener line, given the opener's own indentation. Stops at the first line
84
- * whose indentation is `<= parentIndent` (blank lines are skipped).
85
- *
86
- * @param {readonly string[]} lines - full file, split into lines.
87
- * @param {number} openerIndex - index of the block-opener line (e.g. `with:`).
88
- * @param {number} parentIndent - indentation width of the opener line itself.
89
- * @returns {string[]} the immediate child key names, in file order.
90
- */
91
- function collectImmediateChildKeys(lines, openerIndex, parentIndent) {
92
- const keys = [];
93
- for (let k = openerIndex + 1; k < lines.length; k++) {
94
- const line = lines[k];
95
- if (line.trim() === "") {
96
- continue;
97
- }
98
- const lineIndent = indentOf(line);
99
- if (lineIndent <= parentIndent) {
100
- break;
101
- }
102
- const match = /^\s*([A-Za-z0-9_-]+):/.exec(line);
103
- if (match && lineIndent === parentIndent + 2) {
104
- keys.push(match[1]);
105
- }
106
- }
107
- return keys;
108
- }
109
-
110
- /**
111
- * Parse a `uses:` value referencing a reusable workflow into its basename and
112
- * ref, or `null` if it doesn't match the `.../.github/workflows/<file>@<ref>`
113
- * shape (e.g. a plain action reference like `actions/checkout@v6`).
114
- *
115
- * @param {string} usesValue - the raw value after `uses:`.
116
- * @returns {{ file: string, ref: string } | null} the parsed reference.
117
- */
118
- export function parseReusableReference(usesValue) {
119
- const match = /\.github\/workflows\/([\w.-]+\.ya?ml)@(\S+)$/.exec(
120
- usesValue.trim()
121
- );
122
- if (!match) {
123
- return null;
124
- }
125
- return { file: match[1], ref: match[2] };
126
- }
127
-
128
- /**
129
- * Scan a caller workflow file's contents for every job that invokes a
130
- * reusable workflow, returning the `with:` keys each job passes.
131
- *
132
- * @param {string} content - full caller workflow file contents.
133
- * @returns {{ reusableFile: string, ref: string, withKeys: string[] }[]}
134
- * one entry per job that calls a reusable workflow (in file order).
135
- */
136
- export function extractCallerJobs(content) {
137
- const lines = content.split(/\r?\n/);
138
- const jobs = [];
139
- for (let i = 0; i < lines.length; i++) {
140
- const usesMatch = /^(\s*)uses:\s*(\S+)\s*$/.exec(lines[i]);
141
- if (!usesMatch) {
142
- continue;
143
- }
144
- const [, indentStr, usesValue] = usesMatch;
145
- const ref = parseReusableReference(usesValue);
146
- if (!ref) {
147
- continue;
148
- }
149
- const indent = indentStr.length;
150
- let withKeys = [];
151
- for (let j = i + 1; j < lines.length; j++) {
152
- const line = lines[j];
153
- if (line.trim() === "") {
154
- continue;
155
- }
156
- const lineIndent = indentOf(line);
157
- if (lineIndent < indent) {
158
- break;
159
- }
160
- if (lineIndent === indent && /^\s*with:\s*$/.test(line)) {
161
- withKeys = collectImmediateChildKeys(lines, j, indent).sort();
162
- break;
163
- }
164
- }
165
- jobs.push({ reusableFile: ref.file, ref: ref.ref, withKeys });
166
- }
167
- return jobs;
168
- }
169
-
170
- /**
171
- * Extract the declared `on.workflow_call.inputs` key names from a reusable
172
- * workflow's contents.
173
- *
174
- * @param {string} content - full reusable workflow file contents.
175
- * @returns {string[] | null} declared input names (sorted), an empty array when
176
- * the reusable workflow declares no inputs, or `null` if the file has no
177
- * `workflow_call:` block.
178
- */
179
- export function extractDeclaredInputs(content) {
180
- const lines = content.split(/\r?\n/);
181
- for (let i = 0; i < lines.length; i++) {
182
- if (!/^\s*workflow_call:\s*$/.test(lines[i])) {
183
- continue;
184
- }
185
- const wcIndent = indentOf(lines[i]);
186
- for (let j = i + 1; j < lines.length; j++) {
187
- const line = lines[j];
188
- if (line.trim() === "") {
189
- continue;
190
- }
191
- const lineIndent = indentOf(line);
192
- if (lineIndent <= wcIndent) {
193
- break;
194
- }
195
- if (lineIndent === wcIndent + 2 && /^\s*inputs:\s*$/.test(line)) {
196
- return collectImmediateChildKeys(lines, j, lineIndent).sort();
197
- }
198
- }
199
- return [];
200
- }
201
- return null;
202
- }
203
-
204
- /**
205
- * Keys present in `used` but absent from `declared` — i.e. inputs a caller
206
- * passes that the reusable workflow's contract no longer accepts.
207
- *
208
- * @param {readonly string[]} used - `with:` keys a caller passes.
209
- * @param {readonly string[]} declared - `workflow_call.inputs` keys the
210
- * reusable workflow declares.
211
- * @returns {string[]} the stale keys, sorted.
212
- */
213
- export function diffStaleKeys(used, declared) {
214
- const declaredSet = new Set(declared);
215
- return used.filter(key => !declaredSet.has(key)).sort();
216
- }
217
-
218
86
  /**
219
87
  * Resolve a reusable workflow's declared inputs from the contracts root.
220
88
  *
@@ -245,7 +113,12 @@ function resolveContractInputs(contractsRoot, reusableFile) {
245
113
  function scanCallerFile(filePath, contractsRoot) {
246
114
  const jobs = extractCallerJobs(fs.readFileSync(filePath, "utf8"));
247
115
  return jobs.map(job => {
248
- const declared = resolveContractInputs(contractsRoot, job.reusableFile);
116
+ const isLisaContract =
117
+ job.owner?.toLowerCase() === LISA_OWNER &&
118
+ job.repo?.toLowerCase() === LISA_REPO;
119
+ const declared = isLisaContract
120
+ ? resolveContractInputs(contractsRoot, job.reusableFile)
121
+ : null;
249
122
  if (declared === null) {
250
123
  return { ...job, staleInputs: [], status: "unknown-contract" };
251
124
  }
@@ -0,0 +1,24 @@
1
+ export interface ReusableCallerJob {
2
+ readonly reusableFile: string;
3
+ readonly ref: string;
4
+ readonly owner: string | null;
5
+ readonly repo: string | null;
6
+ readonly withKeys: readonly string[];
7
+ }
8
+
9
+ export function parseReusableReference(usesValue: string): {
10
+ readonly file: string;
11
+ readonly ref: string;
12
+ readonly owner: string | null;
13
+ readonly repo: string | null;
14
+ } | null;
15
+ export function extractCallerJobs(
16
+ content: string
17
+ ): readonly ReusableCallerJob[];
18
+ export function extractDeclaredInputs(
19
+ content: string
20
+ ): readonly string[] | null;
21
+ export function diffStaleKeys(
22
+ used: readonly string[],
23
+ declared: readonly string[]
24
+ ): readonly string[];
@@ -0,0 +1,123 @@
1
+ /** Pure reusable-workflow caller/contract comparison shared by health and CLI. */
2
+
3
+ /** @param {string} line @returns {number} */
4
+ function indentOf(line) {
5
+ return line.length - line.trimStart().length;
6
+ }
7
+
8
+ /**
9
+ * @param {readonly string[]} lines
10
+ * @param {number} openerIndex
11
+ * @param {number} parentIndent
12
+ * @returns {string[]}
13
+ */
14
+ function collectImmediateChildKeys(lines, openerIndex, parentIndent) {
15
+ const keys = [];
16
+ for (let index = openerIndex + 1; index < lines.length; index++) {
17
+ const line = lines[index];
18
+ if (line.trim() === "") continue;
19
+ const indentation = indentOf(line);
20
+ if (indentation <= parentIndent) break;
21
+ const match = /^\s*([A-Za-z0-9_-]+):/.exec(line);
22
+ if (match && indentation === parentIndent + 2) keys.push(match[1]);
23
+ }
24
+ return keys;
25
+ }
26
+
27
+ /**
28
+ * @param {string} usesValue
29
+ * @returns {{ file: string, ref: string, owner: string | null, repo: string | null } | null}
30
+ */
31
+ export function parseReusableReference(usesValue) {
32
+ const value = usesValue.trim();
33
+ const local = /^\.\/\.github\/workflows\/([\w.-]+\.ya?ml)$/.exec(value);
34
+ if (local) {
35
+ return { file: local[1], ref: "local", owner: null, repo: null };
36
+ }
37
+ const remote =
38
+ /^([^/\s]+)\/([^/\s]+)\/\.github\/workflows\/([\w.-]+\.ya?ml)@(\S+)$/.exec(
39
+ value
40
+ );
41
+ return remote
42
+ ? {
43
+ file: remote[3],
44
+ ref: remote[4],
45
+ owner: remote[1],
46
+ repo: remote[2],
47
+ }
48
+ : null;
49
+ }
50
+
51
+ /**
52
+ * @param {string} content
53
+ * @returns {{ reusableFile: string, ref: string, owner: string | null, repo: string | null, withKeys: string[] }[]}
54
+ */
55
+ export function extractCallerJobs(content) {
56
+ const lines = content.split(/\r?\n/);
57
+ const jobs = [];
58
+ for (let index = 0; index < lines.length; index++) {
59
+ const usesMatch = /^(\s*)uses:\s*(\S+)\s*$/.exec(lines[index]);
60
+ if (!usesMatch) continue;
61
+ const reference = parseReusableReference(usesMatch[2]);
62
+ if (!reference) continue;
63
+ const indentation = usesMatch[1].length;
64
+ let withKeys = [];
65
+ for (let child = index + 1; child < lines.length; child++) {
66
+ const line = lines[child];
67
+ if (line.trim() === "") continue;
68
+ const childIndentation = indentOf(line);
69
+ if (childIndentation < indentation) break;
70
+ if (childIndentation === indentation && /^\s*with:\s*$/.test(line)) {
71
+ withKeys = collectImmediateChildKeys(lines, child, indentation).sort(
72
+ (left, right) => left.localeCompare(right)
73
+ );
74
+ break;
75
+ }
76
+ }
77
+ jobs.push({
78
+ reusableFile: reference.file,
79
+ ref: reference.ref,
80
+ owner: reference.owner,
81
+ repo: reference.repo,
82
+ withKeys,
83
+ });
84
+ }
85
+ return jobs;
86
+ }
87
+
88
+ /** @param {string} content @returns {string[] | null} */
89
+ export function extractDeclaredInputs(content) {
90
+ const lines = content.split(/\r?\n/);
91
+ for (let index = 0; index < lines.length; index++) {
92
+ if (!/^\s*workflow_call:\s*$/.test(lines[index])) continue;
93
+ const indentation = indentOf(lines[index]);
94
+ for (let child = index + 1; child < lines.length; child++) {
95
+ const line = lines[child];
96
+ if (line.trim() === "") continue;
97
+ const childIndentation = indentOf(line);
98
+ if (childIndentation <= indentation) break;
99
+ if (
100
+ childIndentation === indentation + 2 &&
101
+ /^\s*inputs:\s*$/.test(line)
102
+ ) {
103
+ return collectImmediateChildKeys(lines, child, childIndentation).sort(
104
+ (left, right) => left.localeCompare(right)
105
+ );
106
+ }
107
+ }
108
+ return [];
109
+ }
110
+ return null;
111
+ }
112
+
113
+ /**
114
+ * @param {readonly string[]} used
115
+ * @param {readonly string[]} declared
116
+ * @returns {string[]}
117
+ */
118
+ export function diffStaleKeys(used, declared) {
119
+ const declaredSet = new Set(declared);
120
+ return used
121
+ .filter(key => !declaredSet.has(key))
122
+ .sort((left, right) => left.localeCompare(right));
123
+ }