@codyswann/lisa 2.265.0 → 2.267.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 (168) 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 +38 -7
  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/.codex-plugin/skills/lisa-implement/SKILL.md +22 -1
  88. package/plugins/lisa/.codex-plugin/skills/lisa-spec-conformance/SKILL.md +43 -11
  89. package/plugins/lisa/agents/spec-conformance-specialist.md +5 -2
  90. package/plugins/lisa/agents/verification-specialist.md +1 -1
  91. package/plugins/lisa/hooks/enforce-verification-gate.sh +83 -9
  92. package/plugins/lisa/rules/reference/verification.md +2 -0
  93. package/plugins/lisa/skills/lisa-implement/SKILL.md +22 -1
  94. package/plugins/lisa/skills/lisa-spec-conformance/SKILL.md +43 -11
  95. package/plugins/lisa-agy/agents/spec-conformance-specialist.md +5 -2
  96. package/plugins/lisa-agy/agents/verification-specialist.md +1 -1
  97. package/plugins/lisa-agy/plugin.json +1 -1
  98. package/plugins/lisa-agy/skills/lisa-implement/SKILL.md +22 -1
  99. package/plugins/lisa-agy/skills/lisa-spec-conformance/SKILL.md +43 -11
  100. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  101. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  102. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  103. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  104. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  105. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  106. package/plugins/lisa-copilot/agents/spec-conformance-specialist.agent.md +5 -2
  107. package/plugins/lisa-copilot/agents/verification-specialist.agent.md +1 -1
  108. package/plugins/lisa-copilot/hooks/enforce-verification-gate.sh +83 -9
  109. package/plugins/lisa-copilot/rules/reference/verification.md +2 -0
  110. package/plugins/lisa-copilot/skills/lisa-implement/SKILL.md +22 -1
  111. package/plugins/lisa-copilot/skills/lisa-spec-conformance/SKILL.md +43 -11
  112. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  113. package/plugins/lisa-cursor/agents/spec-conformance-specialist.md +5 -2
  114. package/plugins/lisa-cursor/agents/verification-specialist.md +1 -1
  115. package/plugins/lisa-cursor/hooks/enforce-verification-gate.sh +83 -9
  116. package/plugins/lisa-cursor/rules/verification-reference.mdc +2 -0
  117. package/plugins/lisa-cursor/skills/lisa-implement/SKILL.md +22 -1
  118. package/plugins/lisa-cursor/skills/lisa-spec-conformance/SKILL.md +43 -11
  119. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  120. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  121. package/plugins/lisa-expo-agy/plugin.json +1 -1
  122. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  123. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  124. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  125. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  126. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  127. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  128. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  129. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  130. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  131. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  132. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  133. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  134. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  135. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  136. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  137. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  138. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  139. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  140. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  141. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  142. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  143. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  144. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  145. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  146. package/plugins/lisa-rails-agy/plugin.json +1 -1
  147. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  148. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  149. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  150. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  151. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  152. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  153. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  154. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  155. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  156. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  157. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  158. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  159. package/plugins/src/base/agents/spec-conformance-specialist.md +5 -2
  160. package/plugins/src/base/agents/verification-specialist.md +1 -1
  161. package/plugins/src/base/hooks/enforce-verification-gate.sh +83 -9
  162. package/plugins/src/base/rules/reference/verification.md +2 -0
  163. package/plugins/src/base/skills/lisa-implement/SKILL.md +22 -1
  164. package/plugins/src/base/skills/lisa-spec-conformance/SKILL.md +43 -11
  165. package/scripts/detect-stale-workflow-inputs.mjs +21 -148
  166. package/scripts/lib/reusable-workflow-contract.d.mts +24 -0
  167. package/scripts/lib/reusable-workflow-contract.mjs +123 -0
  168. package/scripts/verify-health-deterministic-built.mjs +499 -0
@@ -0,0 +1,123 @@
1
+ /** Installed and executable Git-hook inspection without running hooks. */
2
+ /* eslint-disable jsdoc/require-param-description, jsdoc/require-returns -- typed hook readers are self-describing */
3
+ import { execFile } from "node:child_process";
4
+ import path from "node:path";
5
+ import { projectPathKind, readProjectFile, readProjectText, } from "./read-only-fs.js";
6
+ const GIT_TIMEOUT_MS = 10_000;
7
+ const MAX_GIT_OUTPUT_BYTES = 16 * 1024;
8
+ const HUSKY_HOOKS = [
9
+ "commit-msg",
10
+ "post-checkout",
11
+ "pre-commit",
12
+ "pre-push",
13
+ "prepare-commit-msg",
14
+ ];
15
+ const LEFTHOOK_HOOKS = [
16
+ "commit-msg",
17
+ "pre-commit",
18
+ "pre-push",
19
+ "prepare-commit-msg",
20
+ ];
21
+ /**
22
+ * Default fixed-argv local git-config reader.
23
+ * @param projectRoot
24
+ * @param timeoutMs
25
+ * @param signal
26
+ */
27
+ export const readCoreHooksPath = async (projectRoot, timeoutMs, signal) => new Promise((resolve, reject) => {
28
+ execFile(
29
+ // eslint-disable-next-line sonarjs/no-os-command-from-path -- fixed user-installed git executable
30
+ "git", ["config", "--local", "--get", "core.hooksPath"], {
31
+ cwd: projectRoot,
32
+ encoding: "utf8",
33
+ maxBuffer: MAX_GIT_OUTPUT_BYTES,
34
+ signal,
35
+ killSignal: "SIGKILL",
36
+ timeout: Math.max(1, Math.min(timeoutMs, GIT_TIMEOUT_MS)),
37
+ }, (error, stdout) => {
38
+ if (error === null) {
39
+ resolve(stdout.trim() || undefined);
40
+ return;
41
+ }
42
+ if (error.code === 1) {
43
+ resolve(undefined);
44
+ return;
45
+ }
46
+ reject(error);
47
+ });
48
+ });
49
+ /**
50
+ * Check executable regular files under one project-relative prefix.
51
+ * @param projectRoot
52
+ * @param prefix
53
+ * @param names
54
+ */
55
+ async function executableDrift(projectRoot, prefix, names) {
56
+ const checks = await Promise.all(names.map(async (name) => {
57
+ const relative = path.join(prefix, name);
58
+ const file = await readProjectFile(projectRoot, relative);
59
+ return file !== undefined && (file.mode & 0o100) !== 0
60
+ ? undefined
61
+ : relative.split(path.sep).join("/");
62
+ }));
63
+ return checks.filter((item) => item !== undefined);
64
+ }
65
+ /**
66
+ * Inspect Lefthook wrappers when the git directory is locally readable.
67
+ * @param projectRoot
68
+ */
69
+ async function inspectLefthook(projectRoot) {
70
+ const gitKind = await projectPathKind(projectRoot, ".git");
71
+ if (gitKind !== "directory") {
72
+ return { status: "warn", drift: ["lefthook installation unavailable"] };
73
+ }
74
+ const modeDrift = await executableDrift(projectRoot, path.join(".git", "hooks"), LEFTHOOK_HOOKS);
75
+ const contentDrift = (await Promise.all(LEFTHOOK_HOOKS.map(async (name) => {
76
+ const relative = path.join(".git", "hooks", name);
77
+ const content = await readProjectText(projectRoot, relative);
78
+ return content !== undefined &&
79
+ !content.toLowerCase().includes("lefthook")
80
+ ? relative.split(path.sep).join("/")
81
+ : undefined;
82
+ }))).filter((item) => item !== undefined);
83
+ const drift = [...new Set([...modeDrift, ...contentDrift])].sort((left, right) => left.localeCompare(right));
84
+ return drift.length === 0
85
+ ? { status: "pass", drift: [] }
86
+ : { status: "fail", drift };
87
+ }
88
+ /**
89
+ * Inspect actual installed hook state for applicable stacks.
90
+ * @param projectRoot - Canonical host root
91
+ * @param types - Safely detected project types
92
+ * @param reader - core.hooksPath reader
93
+ * @param timeoutMs - Remaining shared deadline
94
+ * @param signal - Shared cancellation signal
95
+ * @returns Hook installation status
96
+ */
97
+ export async function inspectHookInstallation(projectRoot, types, reader, timeoutMs, signal) {
98
+ if (types.includes("rails"))
99
+ return inspectLefthook(projectRoot);
100
+ if (!types.includes("typescript")) {
101
+ return { status: "warn", drift: ["unsupported hook stack"] };
102
+ }
103
+ const modeDrift = [
104
+ ...(await executableDrift(projectRoot, ".husky", HUSKY_HOOKS)),
105
+ ];
106
+ const hooksPath = await reader(projectRoot, timeoutMs, signal).catch(() => null);
107
+ if (hooksPath === null) {
108
+ return modeDrift.length === 0
109
+ ? { status: "warn", drift: ["core.hooksPath unavailable"] }
110
+ : { status: "fail", drift: modeDrift };
111
+ }
112
+ const drift = [
113
+ ...modeDrift,
114
+ ...(hooksPath === ".husky" || hooksPath === ".husky/_"
115
+ ? []
116
+ : ["core.hooksPath"]),
117
+ ];
118
+ return drift.length === 0
119
+ ? { status: "pass", drift: [] }
120
+ : { status: "fail", drift };
121
+ }
122
+ /* eslint-enable jsdoc/require-param-description, jsdoc/require-returns -- restore repository documentation defaults */
123
+ //# sourceMappingURL=hook-inspection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hook-inspection.js","sourceRoot":"","sources":["../../src/health/hook-inspection.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,4 @@
1
1
  export * from "./contract.js";
2
+ export * from "./deterministic.js";
2
3
  export * from "./storage.js";
3
4
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/health/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/health/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,cAAc,oBAAoB,CAAC;AACnC,cAAc,cAAc,CAAC"}
@@ -1,3 +1,4 @@
1
1
  export * from "./contract.js";
2
+ export * from "./deterministic.js";
2
3
  export * from "./storage.js";
3
4
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/health/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/health/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,cAAc,oBAAoB,CAAC;AACnC,cAAc,cAAc,CAAC"}
@@ -0,0 +1,37 @@
1
+ import { type ProjectType } from "../core/config.js";
2
+ /**
3
+ * Injectable fixed-argv reader for actual project plugin installation.
4
+ * Implementations must honor `signal` and release any owned handles before
5
+ * settling.
6
+ */
7
+ export type InstalledPluginReader = (projectRoot: string, timeoutMs: number, signal: AbortSignal) => Promise<readonly string[]>;
8
+ /** Structured plugin inspection outcome. */
9
+ export type PluginInspection = {
10
+ readonly status: "pass";
11
+ readonly drift: readonly string[];
12
+ } | {
13
+ readonly status: "warn";
14
+ readonly drift: readonly string[];
15
+ } | {
16
+ readonly status: "fail";
17
+ readonly drift: readonly string[];
18
+ };
19
+ /**
20
+ * Default fixed-argv Claude installed-plugin reader.
21
+ * @param projectRoot
22
+ * @param timeoutMs
23
+ * @param signal
24
+ */
25
+ export declare const readInstalledClaudePlugins: InstalledPluginReader;
26
+ /**
27
+ * Inspect settings, marker, and actual installed plugin state.
28
+ * @param projectRoot - Canonical host root
29
+ * @param config - Safe project config
30
+ * @param types - Safely detected project types
31
+ * @param reader - Actual installed-plugin reader
32
+ * @param timeoutMs - Remaining shared deadline
33
+ * @param signal - Shared cancellation signal
34
+ * @returns Pass, fail, or optional-harness warning
35
+ */
36
+ export declare function inspectPlugins(projectRoot: string, config: Readonly<Record<string, unknown>>, types: readonly ProjectType[], reader: InstalledPluginReader, timeoutMs: number, signal: AbortSignal): Promise<PluginInspection>;
37
+ //# sourceMappingURL=plugin-inspection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin-inspection.d.ts","sourceRoot":"","sources":["../../src/health/plugin-inspection.ts"],"names":[],"mappings":"AAKA,OAAO,EAIL,KAAK,WAAW,EACjB,MAAM,mBAAmB,CAAC;AAa3B;;;;GAIG;AACH,MAAM,MAAM,qBAAqB,GAAG,CAClC,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,WAAW,KAChB,OAAO,CAAC,SAAS,MAAM,EAAE,CAAC,CAAC;AAEhC,4CAA4C;AAC5C,MAAM,MAAM,gBAAgB,GACxB;IAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,GAC9D;IAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,GAC9D;IAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,CAAC;AAEnE;;;;;GAKG;AACH,eAAO,MAAM,0BAA0B,EAAE,qBA4CrC,CAAC;AAwBL;;;;;;;;;GASG;AACH,wBAAsB,cAAc,CAClC,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EACzC,KAAK,EAAE,SAAS,WAAW,EAAE,EAC7B,MAAM,EAAE,qBAAqB,EAC7B,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,WAAW,GAClB,OAAO,CAAC,gBAAgB,CAAC,CA0D3B"}
@@ -0,0 +1,128 @@
1
+ /** Bounded actual plugin-installation inspection for deterministic health. */
2
+ /* eslint-disable jsdoc/require-param-description, jsdoc/require-returns -- typed plugin readers are self-describing */
3
+ import { execFile } from "node:child_process";
4
+ import path from "node:path";
5
+ import { DEFAULT_HARNESS, harnessIncludesAgent, } from "../core/config.js";
6
+ import { selectProjectLisaPluginsFromState } from "../core/lisa-plugin-selection.js";
7
+ import { getPackageVersion } from "../cli/version.js";
8
+ import { projectPathKind, readProjectJsonObject, readProjectText, } from "./read-only-fs.js";
9
+ const MAX_PLUGIN_OUTPUT_BYTES = 256 * 1024;
10
+ const MAX_INSTALLED_PLUGINS = 512;
11
+ const PLUGIN_TIMEOUT_MS = 15_000;
12
+ /**
13
+ * Default fixed-argv Claude installed-plugin reader.
14
+ * @param projectRoot
15
+ * @param timeoutMs
16
+ * @param signal
17
+ */
18
+ export const readInstalledClaudePlugins = async (projectRoot, timeoutMs, signal) => new Promise((resolve, reject) => {
19
+ execFile(
20
+ // eslint-disable-next-line sonarjs/no-os-command-from-path -- fixed user-installed Claude executable
21
+ "claude", ["plugin", "list", "--json"], {
22
+ cwd: projectRoot,
23
+ encoding: "utf8",
24
+ maxBuffer: MAX_PLUGIN_OUTPUT_BYTES,
25
+ signal,
26
+ killSignal: "SIGKILL",
27
+ timeout: Math.max(1, Math.min(timeoutMs, PLUGIN_TIMEOUT_MS)),
28
+ }, (error, stdout) => {
29
+ if (error !== null) {
30
+ reject(error);
31
+ return;
32
+ }
33
+ try {
34
+ const parsed = JSON.parse(stdout);
35
+ if (!Array.isArray(parsed) || parsed.length > MAX_INSTALLED_PLUGINS) {
36
+ throw new Error("Installed plugin response exceeded its contract");
37
+ }
38
+ const ids = parsed.flatMap(entry => {
39
+ if (entry === null || typeof entry !== "object")
40
+ return [];
41
+ const id = Reflect.get(entry, "id");
42
+ const projectPath = Reflect.get(entry, "projectPath");
43
+ return typeof id === "string" && projectPath === projectRoot
44
+ ? [id]
45
+ : [];
46
+ });
47
+ resolve([...new Set(ids)].sort((left, right) => left.localeCompare(right)));
48
+ }
49
+ catch (parseError) {
50
+ reject(parseError);
51
+ }
52
+ });
53
+ });
54
+ /**
55
+ * Resolve configured harness without accepting invalid strings.
56
+ * @param config
57
+ */
58
+ function configuredHarness(config) {
59
+ const harness = config.harness ?? DEFAULT_HARNESS;
60
+ return typeof harness === "string" &&
61
+ [
62
+ "claude",
63
+ "codex",
64
+ "cursor",
65
+ "agy",
66
+ "copilot",
67
+ "opencode",
68
+ "fleet",
69
+ ].includes(harness)
70
+ ? harness
71
+ : undefined;
72
+ }
73
+ /**
74
+ * Inspect settings, marker, and actual installed plugin state.
75
+ * @param projectRoot - Canonical host root
76
+ * @param config - Safe project config
77
+ * @param types - Safely detected project types
78
+ * @param reader - Actual installed-plugin reader
79
+ * @param timeoutMs - Remaining shared deadline
80
+ * @param signal - Shared cancellation signal
81
+ * @returns Pass, fail, or optional-harness warning
82
+ */
83
+ export async function inspectPlugins(projectRoot, config, types, reader, timeoutMs, signal) {
84
+ const harness = configuredHarness(config);
85
+ if (harness === undefined || !harnessIncludesAgent(harness, "claude")) {
86
+ return { status: "warn", drift: ["unsupported harness"] };
87
+ }
88
+ const settings = await readProjectJsonObject(projectRoot, path.join(".claude", "settings.json"));
89
+ if (settings === undefined) {
90
+ return { status: "fail", drift: [".claude/settings.json"] };
91
+ }
92
+ const enabled = settings.enabledPlugins;
93
+ if (enabled === null ||
94
+ typeof enabled !== "object" ||
95
+ Array.isArray(enabled)) {
96
+ return { status: "fail", drift: ["plugin settings"] };
97
+ }
98
+ const hasWiki = (await projectPathKind(projectRoot, path.join("wiki", "lisa-wiki.config.json"))) === "file";
99
+ const expected = [
100
+ ...selectProjectLisaPluginsFromState(config, types, hasWiki),
101
+ ]
102
+ .map(plugin => `${plugin}@lisa`)
103
+ .sort((left, right) => left.localeCompare(right));
104
+ const settingsDrift = expected.filter(plugin => Reflect.get(enabled, plugin) !== true);
105
+ const marker = await readProjectText(projectRoot, path.join(".claude", ".lisa-plugins-synced"));
106
+ const drift = [
107
+ ...settingsDrift,
108
+ ...(marker?.trim() === getPackageVersion()
109
+ ? []
110
+ : ["plugin version marker"]),
111
+ ];
112
+ if (drift.length > 0)
113
+ return { status: "fail", drift };
114
+ const installed = await reader(projectRoot, timeoutMs, signal).catch(() => undefined);
115
+ if (installed === undefined) {
116
+ return {
117
+ status: "warn",
118
+ drift: ["actual plugin installation unavailable"],
119
+ };
120
+ }
121
+ const installedSet = new Set(installed);
122
+ const missing = expected.filter(plugin => !installedSet.has(plugin));
123
+ return missing.length === 0
124
+ ? { status: "pass", drift: [] }
125
+ : { status: "fail", drift: missing };
126
+ }
127
+ /* eslint-enable jsdoc/require-param-description, jsdoc/require-returns -- restore repository documentation defaults */
128
+ //# sourceMappingURL=plugin-inspection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin-inspection.js","sourceRoot":"","sources":["../../src/health/plugin-inspection.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ import type { SyncReport } from "../sync/config-sync.js";
2
+ import type { HealthFinding } from "./contract.js";
3
+ import type { HealthProjectShape } from "./template-inspection.js";
4
+ /** Result of safely loading committed project configuration. */
5
+ export interface HealthConfigState {
6
+ readonly config: Readonly<Record<string, unknown>>;
7
+ readonly present: boolean;
8
+ readonly readable: boolean;
9
+ }
10
+ /**
11
+ * Project and legacy overlay check derived from doctor without mutation.
12
+ * @param projectRoot
13
+ * @param configState
14
+ * @param shape
15
+ */
16
+ export declare function projectStateFinding(projectRoot: string, configState: HealthConfigState, shape: HealthProjectShape | undefined): Promise<HealthFinding>;
17
+ /**
18
+ * Local wiki contract check derived from doctor without network or writes.
19
+ * @param projectRoot
20
+ */
21
+ export declare function wikiFinding(projectRoot: string): Promise<HealthFinding>;
22
+ /** Offline representation of doctor's network-only starter check. */
23
+ export declare function starterFinding(): HealthFinding;
24
+ /**
25
+ * Read-only canonical instruction check.
26
+ * @param projectRoot
27
+ */
28
+ export declare function instructionFinding(projectRoot: string): Promise<HealthFinding>;
29
+ /**
30
+ * Convert one dry-run sync report into required-key and sync findings.
31
+ * @param report
32
+ */
33
+ export declare function configFindings(report: SyncReport): readonly [HealthFinding, HealthFinding];
34
+ //# sourceMappingURL=project-probes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project-probes.d.ts","sourceRoot":"","sources":["../../src/health/project-probes.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAGnD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAKnE,gEAAgE;AAChE,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACnD,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;CAC5B;AAED;;;;;GAKG;AACH,wBAAsB,mBAAmB,CACvC,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,iBAAiB,EAC9B,KAAK,EAAE,kBAAkB,GAAG,SAAS,GACpC,OAAO,CAAC,aAAa,CAAC,CAqCxB;AAED;;;GAGG;AACH,wBAAsB,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAyC7E;AAED,qEAAqE;AACrE,wBAAgB,cAAc,IAAI,aAAa,CAM9C;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,aAAa,CAAC,CA0BxB;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,UAAU,GACjB,SAAS,CAAC,aAAa,EAAE,aAAa,CAAC,CA2BzC"}
@@ -0,0 +1,104 @@
1
+ /** Safe local project/doctor-derived deterministic health probes. */
2
+ /* eslint-disable jsdoc/require-param-description, jsdoc/require-returns -- typed doctor projections are self-describing */
3
+ import path from "node:path";
4
+ import { CLAUDE_MD_AGENTS_IMPORT } from "../claude/claude-md-installer.js";
5
+ import { LISA_HOOKS_SUBDIR, LISA_RULES_SUBDIR, } from "../codex/hooks-installer.js";
6
+ import { LISA_SKILLS_SUBDIR } from "../codex/skills-installer.js";
7
+ import { LISA_RULES_END_MARKER, LISA_RULES_START_MARKER, } from "../core/instruction-files-migration.js";
8
+ import { deterministicFinding, namedReason } from "./finding-utils.js";
9
+ import { projectPathKind, readProjectText } from "./read-only-fs.js";
10
+ const PROJECT_STATE_CHECK = "project.state";
11
+ const PROJECT_WIKI_CHECK = "project.wiki";
12
+ /**
13
+ * Project and legacy overlay check derived from doctor without mutation.
14
+ * @param projectRoot
15
+ * @param configState
16
+ * @param shape
17
+ */
18
+ export async function projectStateFinding(projectRoot, configState, shape) {
19
+ if (!configState.present || !configState.readable || shape === undefined) {
20
+ return deterministicFinding(PROJECT_STATE_CHECK, "fail", "Project config or project shape is missing, malformed, or unsafe.");
21
+ }
22
+ if (shape.types.length === 0) {
23
+ return deterministicFinding(PROJECT_STATE_CHECK, "warn", "No supported Lisa project type was safely detected.");
24
+ }
25
+ const legacy = await Promise.all([LISA_HOOKS_SUBDIR, LISA_RULES_SUBDIR, LISA_SKILLS_SUBDIR].map(async (subdir) => {
26
+ const relative = path.join(".codex", subdir);
27
+ return (await projectPathKind(projectRoot, relative)) === "directory"
28
+ ? relative.split(path.sep).join("/")
29
+ : undefined;
30
+ }));
31
+ const present = legacy.filter((item) => item !== undefined);
32
+ return present.length === 0
33
+ ? deterministicFinding(PROJECT_STATE_CHECK, "pass", "Project config, type, and local doctor state are readable.")
34
+ : deterministicFinding(PROJECT_STATE_CHECK, "warn", namedReason("Legacy Codex overlay paths", present));
35
+ }
36
+ /**
37
+ * Local wiki contract check derived from doctor without network or writes.
38
+ * @param projectRoot
39
+ */
40
+ export async function wikiFinding(projectRoot) {
41
+ const wiki = await projectPathKind(projectRoot, "wiki");
42
+ if (wiki === "missing") {
43
+ return deterministicFinding(PROJECT_WIKI_CHECK, "pass", "No local wiki contract is present.");
44
+ }
45
+ if (wiki !== "directory") {
46
+ return deterministicFinding(PROJECT_WIKI_CHECK, "fail", "The local wiki path is unsafe or not a directory.");
47
+ }
48
+ const required = [
49
+ "wiki/lisa-wiki.config.json",
50
+ "wiki/schema/llm-wiki-contract.md",
51
+ "wiki/index.md",
52
+ ];
53
+ const missing = (await Promise.all(required.map(async (relative) => (await projectPathKind(projectRoot, relative)) === "file"
54
+ ? undefined
55
+ : relative))).filter((item) => item !== undefined);
56
+ return missing.length === 0
57
+ ? deterministicFinding(PROJECT_WIKI_CHECK, "pass", "Local wiki contract files are present.")
58
+ : deterministicFinding(PROJECT_WIKI_CHECK, "fail", namedReason("Missing wiki contract files", missing));
59
+ }
60
+ /** Offline representation of doctor's network-only starter check. */
61
+ export function starterFinding() {
62
+ return deterministicFinding("starters.remote", "warn", "Starter repository reachability is unavailable in the offline deterministic path.");
63
+ }
64
+ /**
65
+ * Read-only canonical instruction check.
66
+ * @param projectRoot
67
+ */
68
+ export async function instructionFinding(projectRoot) {
69
+ const [agents, claude] = await Promise.all([
70
+ readProjectText(projectRoot, "AGENTS.md"),
71
+ readProjectText(projectRoot, "CLAUDE.md"),
72
+ ]);
73
+ const drift = [
74
+ ...(agents === undefined ? ["AGENTS.md"] : []),
75
+ ...(agents?.includes(LISA_RULES_START_MARKER) === true ||
76
+ agents?.includes(LISA_RULES_END_MARKER) === true
77
+ ? ["AGENTS.md legacy managed block"]
78
+ : []),
79
+ ...(claude === undefined || !claude.includes(CLAUDE_MD_AGENTS_IMPORT)
80
+ ? ["CLAUDE.md"]
81
+ : []),
82
+ ];
83
+ return drift.length === 0
84
+ ? deterministicFinding("instructions.canonical", "pass", "Instruction files are canonical.")
85
+ : deterministicFinding("instructions.canonical", "fail", namedReason("Instruction file drift", drift));
86
+ }
87
+ /**
88
+ * Convert one dry-run sync report into required-key and sync findings.
89
+ * @param report
90
+ */
91
+ export function configFindings(report) {
92
+ const keys = report.missingRequired.map(item => item.key);
93
+ const actions = report.actions.map(action => action.key);
94
+ return [
95
+ keys.length === 0
96
+ ? deterministicFinding("config.required", "pass", "Required config keys are populated.")
97
+ : deterministicFinding("config.required", "fail", namedReason("Missing required config keys", keys)),
98
+ actions.length === 0
99
+ ? deterministicFinding("config.sync", "pass", "Config and mirrored artifacts are synchronized.")
100
+ : deterministicFinding("config.sync", "fail", namedReason("Config sync drift", actions)),
101
+ ];
102
+ }
103
+ /* eslint-enable jsdoc/require-param-description, jsdoc/require-returns -- restore repository documentation defaults */
104
+ //# sourceMappingURL=project-probes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project-probes.js","sourceRoot":"","sources":["../../src/health/project-probes.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,54 @@
1
+ /** Stable metadata and bytes captured from one confined regular file. */
2
+ export interface ProjectFileSnapshot {
3
+ readonly bytes: Buffer;
4
+ readonly mode: number;
5
+ }
6
+ /** Safe path kinds understood by deterministic health. */
7
+ export type ProjectPathKind = "missing" | "file" | "directory";
8
+ /**
9
+ * Resolve a project-relative path without permitting traversal.
10
+ * @param root - Canonical project root
11
+ * @param relativePath - Project-relative path
12
+ * @returns Confined absolute path
13
+ */
14
+ export declare function resolveProjectPath(root: string, relativePath: string): string;
15
+ /**
16
+ * Read one stable regular file without following a target outside the project.
17
+ * @param root - Canonical project root
18
+ * @param relativePath - Project-relative path
19
+ * @param maximumBytes - Maximum permitted file size
20
+ * @returns File bytes, or undefined when absent
21
+ */
22
+ export declare function readProjectFile(root: string, relativePath: string, maximumBytes?: number): Promise<ProjectFileSnapshot | undefined>;
23
+ /**
24
+ * Read just the bytes from one confined stable regular file.
25
+ * @param root - Canonical project root
26
+ * @param relativePath - Project-relative path
27
+ * @param maximumBytes - Maximum permitted file size
28
+ * @returns File bytes, or undefined when absent
29
+ */
30
+ export declare function readProjectBytes(root: string, relativePath: string, maximumBytes?: number): Promise<Buffer | undefined>;
31
+ /**
32
+ * Classify a confined path without following symbolic links or accepting
33
+ * special files such as FIFOs and devices.
34
+ * @param root - Canonical project root
35
+ * @param relativePath - Project-relative path
36
+ * @returns Safe path kind
37
+ */
38
+ export declare function projectPathKind(root: string, relativePath: string): Promise<ProjectPathKind>;
39
+ /**
40
+ * Read strict UTF-8 text from a confined project file.
41
+ * @param root - Canonical project root
42
+ * @param relativePath - Project-relative path
43
+ * @param maximumBytes - Maximum permitted file size
44
+ * @returns Text, or undefined when absent
45
+ */
46
+ export declare function readProjectText(root: string, relativePath: string, maximumBytes?: number): Promise<string | undefined>;
47
+ /**
48
+ * Read a JSON object without exposing its values in failures.
49
+ * @param root - Canonical project root
50
+ * @param relativePath - Project-relative JSON path
51
+ * @returns Parsed object, or undefined when absent
52
+ */
53
+ export declare function readProjectJsonObject(root: string, relativePath: string): Promise<Readonly<Record<string, unknown>> | undefined>;
54
+ //# sourceMappingURL=read-only-fs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"read-only-fs.d.ts","sourceRoot":"","sources":["../../src/health/read-only-fs.ts"],"names":[],"mappings":"AAQA,yEAAyE;AACzE,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,0DAA0D;AAC1D,MAAM,MAAM,eAAe,GAAG,SAAS,GAAG,MAAM,GAAG,WAAW,CAAC;AAE/D;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAS7E;AAsCD;;;;;;GAMG;AACH,wBAAsB,eAAe,CACnC,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,MAAM,EACpB,YAAY,SAAoB,GAC/B,OAAO,CAAC,mBAAmB,GAAG,SAAS,CAAC,CAmC1C;AAED;;;;;;GAMG;AACH,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,MAAM,EACpB,YAAY,SAAoB,GAC/B,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAE7B;AAED;;;;;;GAMG;AACH,wBAAsB,eAAe,CACnC,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,eAAe,CAAC,CAiB1B;AAED;;;;;;GAMG;AACH,wBAAsB,eAAe,CACnC,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,MAAM,EACpB,YAAY,SAAoB,GAC/B,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAK7B;AAED;;;;;GAKG;AACH,wBAAsB,qBAAqB,CACzC,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAQxD"}