@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
@@ -0,0 +1,469 @@
1
+ /** Safe read-only template ownership and conformance inspection. */
2
+ /* eslint-disable jsdoc/require-param-description, jsdoc/require-returns, max-lines -- ownership planning and exact comparators stay colocated */
3
+ import { lstat, readFile } from "node:fs/promises";
4
+ import * as fse from "fs-extra";
5
+ import path from "node:path";
6
+ import { PROJECT_TYPE_HIERARCHY, PROJECT_TYPE_ORDER, } from "../core/config.js";
7
+ import { decideTemplateOwnership, pendingDeletionPaths, } from "../core/template-ownership.js";
8
+ import { DEFAULT_PROJECT_LEARNINGS_FILE, resolveLegacyProjectLearningsFile, resolveProjectLearningsFile, } from "../core/project-config.js";
9
+ import { mergeCopyContents } from "../strategies/copy-contents.js";
10
+ import { mergeTemplateJson } from "../strategies/merge.js";
11
+ import { PackageLisaStrategy } from "../strategies/package-lisa.js";
12
+ import { TaggedMergeStrategy } from "../strategies/tagged-merge.js";
13
+ import { listFilesRecursive } from "../utils/file-operations.js";
14
+ import { matchesAnyPattern, parseIgnorePatterns, } from "../utils/ignore-patterns.js";
15
+ import { diffStaleKeys, extractCallerJobs, extractDeclaredInputs, } from "../../scripts/lib/reusable-workflow-contract.mjs";
16
+ import { projectPathKind, readProjectFile, readProjectJsonObject, readProjectText, } from "./read-only-fs.js";
17
+ const COPY_OVERWRITE = "copy-overwrite";
18
+ const COPY_CONTENTS = "copy-contents";
19
+ const CREATE_ONLY = "create-only";
20
+ const MANAGED = "managed";
21
+ const HOOKS = "hooks";
22
+ const WORKFLOWS = "workflows";
23
+ const HARPER_APP = "harper-app";
24
+ const LISA_WORKFLOW_OWNER = "codyswanngt";
25
+ const LISA_WORKFLOW_REPO = "lisa";
26
+ const STRATEGIES = [
27
+ COPY_OVERWRITE,
28
+ COPY_CONTENTS,
29
+ CREATE_ONLY,
30
+ "merge",
31
+ "tagged-merge",
32
+ ];
33
+ const MAX_TEMPLATE_BYTES = 1024 * 1024;
34
+ const PROJECT_LEARNINGS_SOURCE = path.join(".lisa", "PROJECT_LEARNINGS.md");
35
+ /**
36
+ * Resolve whether a caller contract is shipped by Lisa, local, or external.
37
+ * @param owner
38
+ * @param repo
39
+ * @param config
40
+ */
41
+ function workflowContractOwner(owner, repo, config) {
42
+ if (owner === null && repo === null)
43
+ return "project";
44
+ const normalizedOwner = owner?.toLowerCase();
45
+ const normalizedRepo = repo?.toLowerCase();
46
+ if (normalizedOwner === LISA_WORKFLOW_OWNER &&
47
+ normalizedRepo === LISA_WORKFLOW_REPO) {
48
+ return "lisa";
49
+ }
50
+ const github = config.github;
51
+ if (github === null || typeof github !== "object" || Array.isArray(github)) {
52
+ return undefined;
53
+ }
54
+ const configuredOwner = Reflect.get(github, "org");
55
+ const configuredRepo = Reflect.get(github, "repo");
56
+ return typeof configuredOwner === "string" &&
57
+ typeof configuredRepo === "string" &&
58
+ normalizedOwner === configuredOwner.toLowerCase() &&
59
+ normalizedRepo === configuredRepo.toLowerCase()
60
+ ? "project"
61
+ : undefined;
62
+ }
63
+ /**
64
+ * Read a safe file signal, rejecting unsafe path types.
65
+ * @param root
66
+ * @param relativePath
67
+ */
68
+ async function fileSignal(root, relativePath) {
69
+ const kind = await projectPathKind(root, relativePath);
70
+ if (kind === "directory")
71
+ throw new Error("Project signal is not a file");
72
+ return kind === "file";
73
+ }
74
+ /**
75
+ * Return dependency maps from a safe package document.
76
+ * @param packageJson
77
+ */
78
+ function dependencyNames(packageJson) {
79
+ if (packageJson === undefined)
80
+ return [];
81
+ return ["dependencies", "devDependencies"].flatMap(field => {
82
+ const value = packageJson[field];
83
+ return value !== null && typeof value === "object" && !Array.isArray(value)
84
+ ? Object.keys(value)
85
+ : [];
86
+ });
87
+ }
88
+ /**
89
+ * Expand detected types using Lisa's canonical hierarchy and ordering.
90
+ * @param types
91
+ */
92
+ function expandTypes(types) {
93
+ const expanded = new Set([
94
+ ...types,
95
+ ...[...types].flatMap(type => {
96
+ const parent = PROJECT_TYPE_HIERARCHY[type];
97
+ return parent === undefined ? [] : [parent];
98
+ }),
99
+ ]);
100
+ return PROJECT_TYPE_ORDER.filter(type => expanded.has(type));
101
+ }
102
+ /**
103
+ * Detect project types using only confined, bounded project reads.
104
+ * @param projectRoot - Canonical host project root
105
+ * @returns Safe project shape
106
+ */
107
+ // eslint-disable-next-line sonarjs/cognitive-complexity -- explicit signals keep stack detection auditable
108
+ export async function detectHealthProjectShape(projectRoot) {
109
+ const packageJson = await readProjectJsonObject(projectRoot, "package.json");
110
+ const dependencies = dependencyNames(packageJson);
111
+ const hasDependency = (name) => dependencies.includes(name);
112
+ const hasPrefix = (prefix) => dependencies.some(name => name.startsWith(prefix));
113
+ const [tsconfig, app, eas, nest, cdk, railsBin, railsApp, harperConfig, harperSchema,] = await Promise.all([
114
+ fileSignal(projectRoot, "tsconfig.json"),
115
+ fileSignal(projectRoot, "app.json"),
116
+ fileSignal(projectRoot, "eas.json"),
117
+ fileSignal(projectRoot, "nest-cli.json"),
118
+ fileSignal(projectRoot, "cdk.json"),
119
+ fileSignal(projectRoot, path.join("bin", "rails")),
120
+ fileSignal(projectRoot, path.join("config", "application.rb")),
121
+ fileSignal(projectRoot, path.join(HARPER_APP, "config.yaml")),
122
+ fileSignal(projectRoot, path.join(HARPER_APP, "schema.graphql")),
123
+ ]);
124
+ const harperText = harperConfig
125
+ ? await readProjectText(projectRoot, path.join(HARPER_APP, "config.yaml"))
126
+ : undefined;
127
+ const harperSignals = harperText?.includes("graphqlSchema:") === true &&
128
+ harperText.includes("jsResource:") &&
129
+ harperText.includes("static:");
130
+ const harper = harperConfig &&
131
+ harperSchema &&
132
+ (harperSignals || hasDependency("harperdb"));
133
+ const publishable = packageJson !== undefined &&
134
+ packageJson.private !== true &&
135
+ ["main", "bin", "exports", "files"].some(field => Object.hasOwn(packageJson, field));
136
+ const detected = new Set([
137
+ ...(tsconfig || hasDependency("typescript") ? ["typescript"] : []),
138
+ ...(app || eas || hasDependency("expo") ? ["expo"] : []),
139
+ ...(nest || hasPrefix("@nestjs") ? ["nestjs"] : []),
140
+ ...(cdk || hasPrefix("aws-cdk") ? ["cdk"] : []),
141
+ ...(harper ? ["harper-fabric"] : []),
142
+ ...(hasDependency("phaser") ? ["phaser"] : []),
143
+ ...(publishable ? ["npm-package"] : []),
144
+ ...(railsBin || railsApp ? ["rails"] : []),
145
+ ]);
146
+ return { types: expandTypes(detected), packageJson };
147
+ }
148
+ /**
149
+ * Backward-compatible type-only detector surface.
150
+ * @param projectRoot
151
+ */
152
+ export async function detectHealthProjectTypes(projectRoot) {
153
+ return (await detectHealthProjectShape(projectRoot)).types;
154
+ }
155
+ /**
156
+ * Normalize a template destination exactly as apply does.
157
+ * @param relativePath
158
+ * @param strategy
159
+ * @param type
160
+ * @param learningsFile
161
+ */
162
+ function destinationFor(relativePath, strategy, type, learningsFile) {
163
+ if (type === "all" &&
164
+ strategy === CREATE_ONLY &&
165
+ relativePath === PROJECT_LEARNINGS_SOURCE) {
166
+ return learningsFile;
167
+ }
168
+ if (strategy === COPY_CONTENTS &&
169
+ path.basename(relativePath) === "gitignore") {
170
+ return path.join(path.dirname(relativePath), ".gitignore");
171
+ }
172
+ return relativePath;
173
+ }
174
+ /**
175
+ * Collect all trusted template candidates.
176
+ * @param lisaRoot
177
+ * @param types
178
+ * @param learningsFile
179
+ */
180
+ async function collectCandidates(lisaRoot, types, learningsFile) {
181
+ const groups = await Promise.all(["all", ...types].flatMap(type => STRATEGIES.map(async (strategy) => {
182
+ const directory = path.join(lisaRoot, type, strategy);
183
+ if (!(await fse.pathExists(directory)))
184
+ return [];
185
+ return (await listFilesRecursive(directory)).map(source => {
186
+ const relativePath = path.relative(directory, source);
187
+ return {
188
+ type,
189
+ strategy,
190
+ source,
191
+ destination: destinationFor(relativePath, strategy, type, learningsFile),
192
+ };
193
+ });
194
+ })));
195
+ return groups.flat();
196
+ }
197
+ /**
198
+ * Resolve most-specific path ownership for one strategy.
199
+ * @param candidates
200
+ * @param strategy
201
+ */
202
+ function owners(candidates, strategy) {
203
+ return new Map(candidates
204
+ .filter(candidate => candidate.strategy === strategy)
205
+ .map(candidate => [candidate.destination, candidate.type]));
206
+ }
207
+ /**
208
+ * Read pending deletion paths from trusted Lisa manifests.
209
+ * @param lisaRoot
210
+ * @param types
211
+ */
212
+ async function pendingDeletions(lisaRoot, types) {
213
+ const all = await Promise.all(["all", ...types].map(async (type) => {
214
+ try {
215
+ const parsed = JSON.parse(await readFile(path.join(lisaRoot, type, "deletions.json"), "utf8"));
216
+ return pendingDeletionPaths(parsed).map(item => path.normalize(item));
217
+ }
218
+ catch {
219
+ return [];
220
+ }
221
+ }));
222
+ return new Set(all.flat());
223
+ }
224
+ /**
225
+ * Build the exact applicable template plan without touching the host.
226
+ * @param lisaRoot
227
+ * @param projectRoot
228
+ * @param types
229
+ * @param config
230
+ */
231
+ async function applicableCandidates(lisaRoot, projectRoot, types, config) {
232
+ const projectConfig = config;
233
+ const learningsFile = path.normalize(resolveProjectLearningsFile(projectConfig));
234
+ const candidates = await collectCandidates(lisaRoot, types, learningsFile);
235
+ const createOwners = owners(candidates, CREATE_ONLY);
236
+ const overwriteOwners = owners(candidates, COPY_OVERWRITE);
237
+ const deletions = await pendingDeletions(lisaRoot, types);
238
+ const ignoreText = await readProjectText(projectRoot, ".lisaignore");
239
+ const ignorePatterns = parseIgnorePatterns(ignoreText ?? "");
240
+ const canonicalLearning = candidates.some(candidate => candidate.type === "all" &&
241
+ candidate.strategy === CREATE_ONLY &&
242
+ candidate.destination === learningsFile);
243
+ const legacyFile = path.normalize(resolveLegacyProjectLearningsFile(projectConfig));
244
+ const suppressLearning = canonicalLearning &&
245
+ legacyFile !== learningsFile &&
246
+ (await projectPathKind(projectRoot, legacyFile)) === "file" &&
247
+ (await projectPathKind(projectRoot, learningsFile)) === "missing";
248
+ const orderedTypes = ["all", ...types];
249
+ return candidates.filter(candidate => decideTemplateOwnership({
250
+ relativePath: candidate.destination,
251
+ strategy: candidate.strategy,
252
+ currentType: candidate.type,
253
+ orderedTypes,
254
+ ignored: matchesAnyPattern(candidate.destination, ignorePatterns),
255
+ pendingDeletion: deletions.has(path.normalize(candidate.destination)),
256
+ projectLearningsPath: canonicalLearning ? learningsFile : undefined,
257
+ suppressLearningsSeed: suppressLearning,
258
+ createOnlyOwner: createOwners.get(candidate.destination),
259
+ copyOverwriteOwner: overwriteOwners.get(candidate.destination),
260
+ }).process);
261
+ }
262
+ /**
263
+ * Classify a template destination into its health finding.
264
+ * @param relativePath
265
+ */
266
+ function categoryFor(relativePath) {
267
+ const normalized = relativePath.split(path.sep).join("/");
268
+ if (normalized === "lefthook.yml" || normalized.startsWith(".husky/")) {
269
+ return HOOKS;
270
+ }
271
+ if (normalized.startsWith(".github/workflows/"))
272
+ return WORKFLOWS;
273
+ return MANAGED;
274
+ }
275
+ /**
276
+ * Decode trusted or safely captured bytes as strict UTF-8.
277
+ * @param bytes
278
+ */
279
+ function decode(bytes) {
280
+ return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
281
+ }
282
+ /**
283
+ * Canonicalize JSON object key order while preserving array order.
284
+ * @param value
285
+ */
286
+ function canonicalJson(value) {
287
+ if (Array.isArray(value))
288
+ return value.map(canonicalJson);
289
+ if (value !== null && typeof value === "object") {
290
+ return Object.fromEntries(Object.entries(value)
291
+ .sort(([left], [right]) => left.localeCompare(right))
292
+ .map(([key, item]) => [key, canonicalJson(item)]));
293
+ }
294
+ return value;
295
+ }
296
+ /**
297
+ * Apply one candidate to an in-memory destination in factory order.
298
+ * @param current
299
+ * @param candidate
300
+ */
301
+ async function composeCandidate(current, candidate) {
302
+ const sourceStat = await lstat(candidate.source);
303
+ if (!sourceStat.isFile() || sourceStat.size > MAX_TEMPLATE_BYTES) {
304
+ throw new Error("Unsafe health template source");
305
+ }
306
+ const source = await readFile(candidate.source);
307
+ const executable = current.requiresExecutable || (sourceStat.mode & 0o100) !== 0;
308
+ if (candidate.strategy === COPY_OVERWRITE) {
309
+ return {
310
+ bytes: source,
311
+ comparison: "bytes",
312
+ requiresExecutable: executable,
313
+ };
314
+ }
315
+ if (candidate.strategy === COPY_CONTENTS) {
316
+ return {
317
+ bytes: Buffer.from(mergeCopyContents(decode(source), decode(current.bytes))),
318
+ comparison: "bytes",
319
+ requiresExecutable: executable,
320
+ };
321
+ }
322
+ const sourceJson = JSON.parse(decode(source));
323
+ const targetJson = JSON.parse(decode(current.bytes));
324
+ const merged = candidate.strategy === "merge"
325
+ ? mergeTemplateJson(sourceJson, targetJson)
326
+ : new TaggedMergeStrategy().mergeJson(sourceJson, targetJson);
327
+ return {
328
+ bytes: Buffer.from(JSON.stringify(merged, null, 2)),
329
+ comparison: "json",
330
+ requiresExecutable: executable,
331
+ };
332
+ }
333
+ /**
334
+ * Compare one final composed destination against its captured host state.
335
+ * @param projectRoot
336
+ * @param candidates
337
+ */
338
+ async function destinationConforms(projectRoot, candidates) {
339
+ const first = candidates[0];
340
+ if (first === undefined)
341
+ return true;
342
+ const target = await readProjectFile(projectRoot, first.destination);
343
+ if (target === undefined)
344
+ return false;
345
+ const initial = {
346
+ bytes: target.bytes,
347
+ comparison: "bytes",
348
+ requiresExecutable: false,
349
+ };
350
+ const planned = await candidates.reduce(async (state, candidate) => composeCandidate(await state, candidate), Promise.resolve(initial));
351
+ const normalizedDestination = first.destination.split(path.sep).join("/");
352
+ if (normalizedDestination.startsWith(".husky/") &&
353
+ planned.requiresExecutable &&
354
+ (target.mode & 0o100) === 0) {
355
+ return false;
356
+ }
357
+ if (planned.comparison === "bytes")
358
+ return planned.bytes.equals(target.bytes);
359
+ return (JSON.stringify(canonicalJson(JSON.parse(decode(planned.bytes)))) ===
360
+ JSON.stringify(canonicalJson(JSON.parse(decode(target.bytes)))));
361
+ }
362
+ /**
363
+ * Inspect one category using Lisa's shared ownership and pure comparators.
364
+ * @param lisaRoot - Lisa package root
365
+ * @param projectRoot - Canonical host root
366
+ * @param types - Canonically ordered types
367
+ * @param category - Health category
368
+ * @param config - Safe project config
369
+ * @returns Sorted project-relative drift paths
370
+ */
371
+ export async function inspectManagedTemplates(lisaRoot, projectRoot, types, category, config = {}) {
372
+ const candidates = (await applicableCandidates(lisaRoot, projectRoot, types, config)).filter(candidate => candidate.strategy !== CREATE_ONLY &&
373
+ categoryFor(candidate.destination) === category);
374
+ const destinations = [...new Set(candidates.map(item => item.destination))];
375
+ const checks = await Promise.all(destinations.map(async (destination) => ({
376
+ destination,
377
+ conforms: await destinationConforms(projectRoot, candidates.filter(candidate => candidate.destination === destination)).catch(() => false),
378
+ })));
379
+ return [
380
+ ...new Set(checks
381
+ .filter(check => !check.conforms)
382
+ .map(check => check.destination.split(path.sep).join("/"))),
383
+ ].sort((left, right) => left.localeCompare(right));
384
+ }
385
+ /**
386
+ * Require applicable create-only workflow presence while preserving bytes.
387
+ * @param lisaRoot - Lisa package root
388
+ * @param projectRoot - Canonical host root
389
+ * @param types - Canonically ordered types
390
+ * @param config - Safe project config
391
+ * @returns Missing workflow paths
392
+ */
393
+ export async function inspectCreateOnlyWorkflows(lisaRoot, projectRoot, types, config = {}) {
394
+ const candidates = (await applicableCandidates(lisaRoot, projectRoot, types, config)).filter(candidate => candidate.strategy === CREATE_ONLY &&
395
+ categoryFor(candidate.destination) === WORKFLOWS);
396
+ const missing = await Promise.all(candidates.map(async (candidate) => (await projectPathKind(projectRoot, candidate.destination)) === "file"
397
+ ? undefined
398
+ : candidate.destination.split(path.sep).join("/")));
399
+ return missing
400
+ .filter((item) => item !== undefined)
401
+ .sort((left, right) => left.localeCompare(right));
402
+ }
403
+ /**
404
+ * Inspect applicable create-only workflow caller inputs against shipped contracts.
405
+ * @param lisaRoot - Lisa package root
406
+ * @param projectRoot - Canonical host root
407
+ * @param types - Canonically ordered types
408
+ * @param config - Safe project config
409
+ * @returns Stale and unknown caller descriptions
410
+ */
411
+ // eslint-disable-next-line sonarjs/cognitive-complexity -- parser outcomes remain explicit and bounded
412
+ export async function inspectWorkflowInputs(lisaRoot, projectRoot, types, config = {}) {
413
+ const candidates = (await applicableCandidates(lisaRoot, projectRoot, types, config)).filter(candidate => categoryFor(candidate.destination) === WORKFLOWS);
414
+ const stale = [];
415
+ const unknown = [];
416
+ for (const candidate of candidates) {
417
+ const caller = await readProjectText(projectRoot, candidate.destination);
418
+ if (caller === undefined)
419
+ continue;
420
+ for (const job of extractCallerJobs(caller)) {
421
+ const label = `${candidate.destination.split(path.sep).join("/")}#${job.reusableFile}`;
422
+ const contractOwner = workflowContractOwner(job.owner, job.repo, config);
423
+ if (contractOwner === undefined) {
424
+ // eslint-disable-next-line functional/immutable-data -- bounded local result collector
425
+ unknown.push(label);
426
+ continue;
427
+ }
428
+ const declared = contractOwner === "lisa"
429
+ ? await readFile(path.join(lisaRoot, ".github", WORKFLOWS, path.basename(job.reusableFile)), "utf8")
430
+ .then(content => extractDeclaredInputs(content))
431
+ .catch(() => null)
432
+ : await readProjectText(projectRoot, path.join(".github", WORKFLOWS, job.reusableFile))
433
+ .then(content => content === undefined ? null : extractDeclaredInputs(content))
434
+ .catch(() => null);
435
+ if (declared === null) {
436
+ // eslint-disable-next-line functional/immutable-data -- bounded local result collector
437
+ unknown.push(label);
438
+ continue;
439
+ }
440
+ for (const key of diffStaleKeys(job.withKeys, declared)) {
441
+ // eslint-disable-next-line functional/immutable-data -- bounded local result collector
442
+ stale.push(`${label}:${key}`);
443
+ }
444
+ }
445
+ }
446
+ return {
447
+ stale: [...new Set(stale)].sort((left, right) => left.localeCompare(right)),
448
+ unknown: [...new Set(unknown)].sort((left, right) => left.localeCompare(right)),
449
+ };
450
+ }
451
+ /**
452
+ * Inspect package.json through the exact pure package-lisa planner.
453
+ * @param lisaRoot - Lisa package root
454
+ * @param projectRoot - Canonical host root
455
+ * @param types - Safely detected project types
456
+ * @param packageJson - Safely captured package document
457
+ * @returns Whether package.json conforms
458
+ */
459
+ export async function packageJsonConforms(lisaRoot, projectRoot, types, packageJson) {
460
+ const safePackage = packageJson ?? (await readProjectJsonObject(projectRoot, "package.json"));
461
+ if (safePackage === undefined)
462
+ return false;
463
+ const safeTypes = types ?? (await detectHealthProjectTypes(projectRoot));
464
+ const planned = await new PackageLisaStrategy().planPackageJson({ ...safePackage }, safeTypes, lisaRoot);
465
+ return (JSON.stringify(planned, null, 2) === JSON.stringify(safePackage, null, 2));
466
+ }
467
+ export { DEFAULT_PROJECT_LEARNINGS_FILE };
468
+ /* eslint-enable jsdoc/require-param-description, jsdoc/require-returns, max-lines -- restore repository defaults */
469
+ //# sourceMappingURL=template-inspection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"template-inspection.js","sourceRoot":"","sources":["../../src/health/template-inspection.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,12 @@
1
1
  import type { FileOperationResult } from "../core/config.js";
2
2
  import type { ICopyStrategy, StrategyContext } from "./strategy.interface.js";
3
+ /**
4
+ * Produce the exact copy-contents result without reading or writing files.
5
+ * @param sourceContent - Lisa-managed source block
6
+ * @param destinationContent - Existing host content
7
+ * @returns Content that apply would persist
8
+ */
9
+ export declare function mergeCopyContents(sourceContent: string, destinationContent: string): string;
3
10
  /**
4
11
  * Copy-contents strategy: Block-based merge for .gitignore-style files
5
12
  * - Create new files silently
@@ -9,31 +16,6 @@ import type { ICopyStrategy, StrategyContext } from "./strategy.interface.js";
9
16
  */
10
17
  export declare class CopyContentsStrategy implements ICopyStrategy {
11
18
  readonly name: "copy-contents";
12
- private readonly BEGIN_MARKER_PREFIX;
13
- private readonly END_MARKER_PREFIX;
14
- /**
15
- * Find the guardrails block in content
16
- * @param content File content to search
17
- * @param beginMarker Opening marker for this managed block
18
- * @param endMarker Closing marker for this managed block
19
- * @returns Object with start/end indices or null if not found
20
- */
21
- private findGuardrailsBlock;
22
- /**
23
- * Read the guardrail marker pair from the source block. This supports
24
- * stack-specific blocks such as `# BEGIN: AI GUARDRAILS HARPER-FABRIC`
25
- * without replacing the universal guardrails block.
26
- * @param sourceContent Source file content
27
- * @returns Marker pair to use while merging
28
- */
29
- private getSourceMarkers;
30
- /**
31
- * Merge source content with destination, replacing or appending guardrails block
32
- * @param sourceContent Source file content
33
- * @param destContent Destination file content
34
- * @returns Merged content
35
- */
36
- private mergeContent;
37
19
  /**
38
20
  * Handle the case where destination doesn't exist
39
21
  * @param sourcePath Source file path
@@ -1 +1 @@
1
- {"version":3,"file":"copy-contents.d.ts","sourceRoot":"","sources":["../../src/strategies/copy-contents.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAC7D,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAG9E;;;;;;GAMG;AACH,qBAAa,oBAAqB,YAAW,aAAa;IACxD,QAAQ,CAAC,IAAI,EAAG,eAAe,CAAU;IAEzC,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAA4B;IAChE,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAA0B;IAE5D;;;;;;OAMG;IACH,OAAO,CAAC,mBAAmB;IAqB3B;;;;;;OAMG;IACH,OAAO,CAAC,gBAAgB;IAiBxB;;;;;OAKG;IACH,OAAO,CAAC,YAAY;IA0BpB;;;;;;;OAOG;YACW,aAAa;IAc3B;;;;;;;OAOG;YACW,WAAW;IAkBzB;;;;;;;;;;;;OAYG;YACW,eAAe;IAkB7B;;;;;;;OAOG;IACG,KAAK,CACT,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,eAAe,GACvB,OAAO,CAAC,mBAAmB,CAAC;CA0EhC"}
1
+ {"version":3,"file":"copy-contents.d.ts","sourceRoot":"","sources":["../../src/strategies/copy-contents.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAC7D,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAM9E;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,aAAa,EAAE,MAAM,EACrB,kBAAkB,EAAE,MAAM,GACzB,MAAM,CAuBR;AAED;;;;;;GAMG;AACH,qBAAa,oBAAqB,YAAW,aAAa;IACxD,QAAQ,CAAC,IAAI,EAAG,eAAe,CAAU;IAEzC;;;;;;;OAOG;YACW,aAAa;IAc3B;;;;;;;OAOG;YACW,WAAW;IAkBzB;;;;;;;;;;;;OAYG;YACW,eAAe;IAkB7B;;;;;;;OAOG;IACG,KAAK,CACT,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,eAAe,GACvB,OAAO,CAAC,mBAAmB,CAAC;CA0EhC"}
@@ -2,6 +2,36 @@ import * as fse from "fs-extra";
2
2
  import { readFile, copyFile, writeFile } from "node:fs/promises";
3
3
  import * as path from "node:path";
4
4
  import { filesIdentical, ensureParentDir } from "../utils/file-operations.js";
5
+ const BEGIN_MARKER_PREFIX = "# BEGIN: AI GUARDRAILS";
6
+ const END_MARKER_PREFIX = "# END: AI GUARDRAILS";
7
+ /**
8
+ * Produce the exact copy-contents result without reading or writing files.
9
+ * @param sourceContent - Lisa-managed source block
10
+ * @param destinationContent - Existing host content
11
+ * @returns Content that apply would persist
12
+ */
13
+ export function mergeCopyContents(sourceContent, destinationContent) {
14
+ if (!sourceContent.includes(BEGIN_MARKER_PREFIX))
15
+ return sourceContent;
16
+ const lines = sourceContent.split(/\r?\n/);
17
+ const begin = lines.find(line => line.startsWith(BEGIN_MARKER_PREFIX)) ??
18
+ BEGIN_MARKER_PREFIX;
19
+ const suffix = begin.slice(BEGIN_MARKER_PREFIX.length);
20
+ const matchingEnd = `${END_MARKER_PREFIX}${suffix}`;
21
+ const end = lines.includes(matchingEnd) ? matchingEnd : END_MARKER_PREFIX;
22
+ const startIndex = destinationContent.indexOf(begin);
23
+ const endIndex = destinationContent.indexOf(end, startIndex + begin.length);
24
+ if (startIndex !== -1 && endIndex !== -1) {
25
+ const trimmedSource = sourceContent.endsWith("\n")
26
+ ? sourceContent.slice(0, -1)
27
+ : sourceContent;
28
+ return (destinationContent.slice(0, startIndex) +
29
+ trimmedSource +
30
+ destinationContent.slice(endIndex + end.length));
31
+ }
32
+ const prefix = destinationContent.endsWith("\n") ? "\n" : "\n\n";
33
+ return destinationContent + prefix + sourceContent;
34
+ }
5
35
  /**
6
36
  * Copy-contents strategy: Block-based merge for .gitignore-style files
7
37
  * - Create new files silently
@@ -11,67 +41,6 @@ import { filesIdentical, ensureParentDir } from "../utils/file-operations.js";
11
41
  */
12
42
  export class CopyContentsStrategy {
13
43
  name = "copy-contents";
14
- BEGIN_MARKER_PREFIX = "# BEGIN: AI GUARDRAILS";
15
- END_MARKER_PREFIX = "# END: AI GUARDRAILS";
16
- /**
17
- * Find the guardrails block in content
18
- * @param content File content to search
19
- * @param beginMarker Opening marker for this managed block
20
- * @param endMarker Closing marker for this managed block
21
- * @returns Object with start/end indices or null if not found
22
- */
23
- findGuardrailsBlock(content, beginMarker, endMarker) {
24
- const startIndex = content.indexOf(beginMarker);
25
- if (startIndex === -1) {
26
- return null;
27
- }
28
- const endIndex = content.indexOf(endMarker, startIndex + beginMarker.length);
29
- if (endIndex === -1) {
30
- return null;
31
- }
32
- return { start: startIndex, end: endIndex + endMarker.length };
33
- }
34
- /**
35
- * Read the guardrail marker pair from the source block. This supports
36
- * stack-specific blocks such as `# BEGIN: AI GUARDRAILS HARPER-FABRIC`
37
- * without replacing the universal guardrails block.
38
- * @param sourceContent Source file content
39
- * @returns Marker pair to use while merging
40
- */
41
- getSourceMarkers(sourceContent) {
42
- const lines = sourceContent.split(/\r?\n/);
43
- const begin = lines.find(line => line.startsWith(this.BEGIN_MARKER_PREFIX)) ??
44
- this.BEGIN_MARKER_PREFIX;
45
- const suffix = begin.slice(this.BEGIN_MARKER_PREFIX.length);
46
- const matchingEnd = `${this.END_MARKER_PREFIX}${suffix}`;
47
- const end = lines.includes(matchingEnd)
48
- ? matchingEnd
49
- : this.END_MARKER_PREFIX;
50
- return { begin, end };
51
- }
52
- /**
53
- * Merge source content with destination, replacing or appending guardrails block
54
- * @param sourceContent Source file content
55
- * @param destContent Destination file content
56
- * @returns Merged content
57
- */
58
- mergeContent(sourceContent, destContent) {
59
- const markers = this.getSourceMarkers(sourceContent);
60
- const block = this.findGuardrailsBlock(destContent, markers.begin, markers.end);
61
- if (block) {
62
- // Replace existing block, trimming source trailing newline
63
- // to prevent doubling with destination's post-marker newline
64
- const trimmedSource = sourceContent.endsWith("\n")
65
- ? sourceContent.slice(0, -1)
66
- : sourceContent;
67
- return (destContent.slice(0, block.start) +
68
- trimmedSource +
69
- destContent.slice(block.end));
70
- }
71
- // Append if no block exists
72
- const prefix = destContent.endsWith("\n") ? "\n" : "\n\n";
73
- return destContent + prefix + sourceContent;
74
- }
75
44
  /**
76
45
  * Handle the case where destination doesn't exist
77
46
  * @param sourcePath Source file path
@@ -178,10 +147,10 @@ export class CopyContentsStrategy {
178
147
  // bug that shipped lisa-mutation.mjs twice). Such a file is fully
179
148
  // Lisa-managed and belongs in copy-overwrite — treat it as an overwrite
180
149
  // here so a miscategorized file can never silently self-duplicate.
181
- if (!sourceContent.includes(this.BEGIN_MARKER_PREFIX)) {
150
+ if (!sourceContent.includes(BEGIN_MARKER_PREFIX)) {
182
151
  return this.handleOverwrite(realDestPath, realRelativePath, sourceContent, context);
183
152
  }
184
- const mergedContent = this.mergeContent(sourceContent, destContent);
153
+ const mergedContent = mergeCopyContents(sourceContent, destContent);
185
154
  if (mergedContent === destContent) {
186
155
  return {
187
156
  relativePath: realRelativePath,