@entelligentsia/forgecli 0.10.1 → 0.11.3

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 (180) hide show
  1. package/CHANGELOG.md +88 -0
  2. package/README.md +21 -3
  3. package/dist/CHANGELOG-forge-plugin.md +49 -0
  4. package/dist/bin/forge.js +0 -0
  5. package/dist/extensions/forgecli/add-pipeline.d.ts +19 -0
  6. package/dist/extensions/forgecli/add-pipeline.js +143 -0
  7. package/dist/extensions/forgecli/add-pipeline.js.map +1 -0
  8. package/dist/extensions/forgecli/add-task.d.ts +20 -0
  9. package/dist/extensions/forgecli/add-task.js +154 -0
  10. package/dist/extensions/forgecli/add-task.js.map +1 -0
  11. package/dist/extensions/forgecli/ask-user-tool.js +32 -20
  12. package/dist/extensions/forgecli/ask-user-tool.js.map +1 -1
  13. package/dist/extensions/forgecli/calibrate.d.ts +61 -0
  14. package/dist/extensions/forgecli/calibrate.js +488 -0
  15. package/dist/extensions/forgecli/calibrate.js.map +1 -0
  16. package/dist/extensions/forgecli/config-layer.js +4 -1
  17. package/dist/extensions/forgecli/config-layer.js.map +1 -1
  18. package/dist/extensions/forgecli/config-writer.js +4 -1
  19. package/dist/extensions/forgecli/config-writer.js.map +1 -1
  20. package/dist/extensions/forgecli/fix-bug.d.ts +9 -1
  21. package/dist/extensions/forgecli/fix-bug.js +101 -9
  22. package/dist/extensions/forgecli/fix-bug.js.map +1 -1
  23. package/dist/extensions/forgecli/forge-commands.js +15 -22
  24. package/dist/extensions/forgecli/forge-commands.js.map +1 -1
  25. package/dist/extensions/forgecli/forge-subagent.js +34 -7
  26. package/dist/extensions/forgecli/forge-subagent.js.map +1 -1
  27. package/dist/extensions/forgecli/forge-update-command.d.ts +9 -0
  28. package/dist/extensions/forgecli/forge-update-command.js +106 -7
  29. package/dist/extensions/forgecli/forge-update-command.js.map +1 -1
  30. package/dist/extensions/forgecli/health-check.d.ts +32 -1
  31. package/dist/extensions/forgecli/health-check.js +337 -12
  32. package/dist/extensions/forgecli/health-check.js.map +1 -1
  33. package/dist/extensions/forgecli/hook-dispatcher.d.ts +25 -1
  34. package/dist/extensions/forgecli/hook-dispatcher.js +108 -11
  35. package/dist/extensions/forgecli/hook-dispatcher.js.map +1 -1
  36. package/dist/extensions/forgecli/hooks/check-update.d.ts +81 -0
  37. package/dist/extensions/forgecli/hooks/check-update.js +308 -0
  38. package/dist/extensions/forgecli/hooks/check-update.js.map +1 -0
  39. package/dist/extensions/forgecli/hooks/forge-permissions.d.ts +32 -0
  40. package/dist/extensions/forgecli/hooks/forge-permissions.js +119 -0
  41. package/dist/extensions/forgecli/hooks/forge-permissions.js.map +1 -0
  42. package/dist/extensions/forgecli/hooks/triage-error.d.ts +23 -0
  43. package/dist/extensions/forgecli/hooks/triage-error.js +62 -0
  44. package/dist/extensions/forgecli/hooks/triage-error.js.map +1 -0
  45. package/dist/extensions/forgecli/hooks/write-guard.d.ts +28 -0
  46. package/dist/extensions/forgecli/hooks/write-guard.js +229 -0
  47. package/dist/extensions/forgecli/hooks/write-guard.js.map +1 -0
  48. package/dist/extensions/forgecli/index.js +60 -0
  49. package/dist/extensions/forgecli/index.js.map +1 -1
  50. package/dist/extensions/forgecli/init-context.d.ts +1 -1
  51. package/dist/extensions/forgecli/init-context.js +21 -6
  52. package/dist/extensions/forgecli/init-context.js.map +1 -1
  53. package/dist/extensions/forgecli/lib/store-error-remediation.d.ts +65 -0
  54. package/dist/extensions/forgecli/lib/store-error-remediation.js +298 -0
  55. package/dist/extensions/forgecli/lib/store-error-remediation.js.map +1 -0
  56. package/dist/extensions/forgecli/materialize.d.ts +16 -0
  57. package/dist/extensions/forgecli/materialize.js +195 -0
  58. package/dist/extensions/forgecli/materialize.js.map +1 -0
  59. package/dist/extensions/forgecli/migrate.d.ts +19 -0
  60. package/dist/extensions/forgecli/migrate.js +258 -0
  61. package/dist/extensions/forgecli/migrate.js.map +1 -0
  62. package/dist/extensions/forgecli/migration-engine.d.ts +111 -0
  63. package/dist/extensions/forgecli/migration-engine.js +533 -0
  64. package/dist/extensions/forgecli/migration-engine.js.map +1 -0
  65. package/dist/extensions/forgecli/quiz-agent.d.ts +17 -0
  66. package/dist/extensions/forgecli/quiz-agent.js +98 -0
  67. package/dist/extensions/forgecli/quiz-agent.js.map +1 -0
  68. package/dist/extensions/forgecli/remove-command.d.ts +17 -0
  69. package/dist/extensions/forgecli/remove-command.js +124 -0
  70. package/dist/extensions/forgecli/remove-command.js.map +1 -0
  71. package/dist/extensions/forgecli/report-bug.d.ts +25 -0
  72. package/dist/extensions/forgecli/report-bug.js +159 -0
  73. package/dist/extensions/forgecli/report-bug.js.map +1 -0
  74. package/dist/extensions/forgecli/retrospective.d.ts +19 -0
  75. package/dist/extensions/forgecli/retrospective.js +156 -0
  76. package/dist/extensions/forgecli/retrospective.js.map +1 -0
  77. package/dist/extensions/forgecli/run-sprint.js +50 -1
  78. package/dist/extensions/forgecli/run-sprint.js.map +1 -1
  79. package/dist/extensions/forgecli/run-task.d.ts +9 -1
  80. package/dist/extensions/forgecli/run-task.js +94 -18
  81. package/dist/extensions/forgecli/run-task.js.map +1 -1
  82. package/dist/extensions/forgecli/session-registry.d.ts +27 -2
  83. package/dist/extensions/forgecli/session-registry.js +52 -1
  84. package/dist/extensions/forgecli/session-registry.js.map +1 -1
  85. package/dist/extensions/forgecli/status-command.d.ts +19 -0
  86. package/dist/extensions/forgecli/status-command.js +140 -0
  87. package/dist/extensions/forgecli/status-command.js.map +1 -0
  88. package/dist/extensions/forgecli/store-query.d.ts +22 -0
  89. package/dist/extensions/forgecli/store-query.js +107 -0
  90. package/dist/extensions/forgecli/store-query.js.map +1 -0
  91. package/dist/extensions/forgecli/store-repair.d.ts +17 -0
  92. package/dist/extensions/forgecli/store-repair.js +123 -0
  93. package/dist/extensions/forgecli/store-repair.js.map +1 -0
  94. package/dist/extensions/forgecli/store-resolver.d.ts +18 -0
  95. package/dist/extensions/forgecli/store-resolver.js +44 -4
  96. package/dist/extensions/forgecli/store-resolver.js.map +1 -1
  97. package/dist/extensions/forgecli/store-validator.d.ts +3 -0
  98. package/dist/extensions/forgecli/store-validator.js +4 -2
  99. package/dist/extensions/forgecli/store-validator.js.map +1 -1
  100. package/dist/extensions/forgecli/thread-switcher.js +213 -28
  101. package/dist/extensions/forgecli/thread-switcher.js.map +1 -1
  102. package/dist/extensions/forgecli/update-tools.d.ts +23 -0
  103. package/dist/extensions/forgecli/update-tools.js +136 -0
  104. package/dist/extensions/forgecli/update-tools.js.map +1 -0
  105. package/dist/extensions/forgecli/viewport-theme.js +4 -0
  106. package/dist/extensions/forgecli/viewport-theme.js.map +1 -1
  107. package/dist/forge-payload/.base-pack/personas/supervisor.md +9 -0
  108. package/dist/forge-payload/.base-pack/workflows/enhance.md +17 -11
  109. package/dist/forge-payload/.claude-plugin/plugin.json +1 -1
  110. package/dist/forge-payload/.schemas/config.schema.json +83 -0
  111. package/dist/forge-payload/.schemas/migrations.json +2065 -0
  112. package/dist/forge-payload/commands/regenerate.md +17 -1
  113. package/dist/forge-payload/meta/personas/README.md +16 -0
  114. package/dist/forge-payload/meta/personas/meta-architect.md +70 -0
  115. package/dist/forge-payload/meta/personas/meta-bug-fixer.md +73 -0
  116. package/dist/forge-payload/meta/personas/meta-collator.md +72 -0
  117. package/dist/forge-payload/meta/personas/meta-engineer.md +70 -0
  118. package/dist/forge-payload/meta/personas/meta-orchestrator.md +71 -0
  119. package/dist/forge-payload/meta/personas/meta-product-manager.md +82 -0
  120. package/dist/forge-payload/meta/personas/meta-qa-engineer.md +91 -0
  121. package/dist/forge-payload/meta/personas/meta-supervisor.md +92 -0
  122. package/dist/forge-payload/meta/skill-recommendations.md +154 -0
  123. package/dist/forge-payload/meta/skills/meta-architect-skills.md +43 -0
  124. package/dist/forge-payload/meta/skills/meta-bug-fixer-skills.md +43 -0
  125. package/dist/forge-payload/meta/skills/meta-collator-skills.md +41 -0
  126. package/dist/forge-payload/meta/skills/meta-engineer-skills.md +43 -0
  127. package/dist/forge-payload/meta/skills/meta-generic-skills.md +58 -0
  128. package/dist/forge-payload/meta/skills/meta-qa-engineer-skills.md +46 -0
  129. package/dist/forge-payload/meta/skills/meta-supervisor-skills.md +43 -0
  130. package/dist/forge-payload/meta/store-schema/bug.schema.md +71 -0
  131. package/dist/forge-payload/meta/store-schema/event.schema.md +76 -0
  132. package/dist/forge-payload/meta/store-schema/feature.schema.md +65 -0
  133. package/dist/forge-payload/meta/store-schema/sprint.schema.md +64 -0
  134. package/dist/forge-payload/meta/store-schema/task.schema.md +78 -0
  135. package/dist/forge-payload/meta/templates/meta-code-review.md +26 -0
  136. package/dist/forge-payload/meta/templates/meta-plan-review.md +28 -0
  137. package/dist/forge-payload/meta/templates/meta-plan.md +28 -0
  138. package/dist/forge-payload/meta/templates/meta-progress.md +25 -0
  139. package/dist/forge-payload/meta/templates/meta-retrospective.md +28 -0
  140. package/dist/forge-payload/meta/templates/meta-sprint-manifest.md +26 -0
  141. package/dist/forge-payload/meta/templates/meta-sprint-requirements.md +91 -0
  142. package/dist/forge-payload/meta/templates/meta-task-prompt.md +26 -0
  143. package/dist/forge-payload/meta/tool-specs/collate.spec.md +88 -0
  144. package/dist/forge-payload/meta/tool-specs/generation-manifest.spec.md +139 -0
  145. package/dist/forge-payload/meta/tool-specs/manage-config.spec.md +143 -0
  146. package/dist/forge-payload/meta/tool-specs/seed-store.spec.md +91 -0
  147. package/dist/forge-payload/meta/tool-specs/store-cli.spec.md +328 -0
  148. package/dist/forge-payload/meta/tool-specs/validate-store.spec.md +191 -0
  149. package/dist/forge-payload/meta/workflows/_fragments/context-injection.md +75 -0
  150. package/dist/forge-payload/meta/workflows/_fragments/event-emission-schema.md +73 -0
  151. package/dist/forge-payload/meta/workflows/_fragments/finalize.md +13 -0
  152. package/dist/forge-payload/meta/workflows/_fragments/friction-emit.md +73 -0
  153. package/dist/forge-payload/meta/workflows/_fragments/progress-reporting.md +38 -0
  154. package/dist/forge-payload/meta/workflows/_fragments/store-cli-verbs.md +39 -0
  155. package/dist/forge-payload/meta/workflows/meta-approve.md +119 -0
  156. package/dist/forge-payload/meta/workflows/meta-collate.md +89 -0
  157. package/dist/forge-payload/meta/workflows/meta-commit.md +93 -0
  158. package/dist/forge-payload/meta/workflows/meta-enhance.md +292 -0
  159. package/dist/forge-payload/meta/workflows/meta-fix-bug.md +501 -0
  160. package/dist/forge-payload/meta/workflows/meta-implement.md +132 -0
  161. package/dist/forge-payload/meta/workflows/meta-migrate.md +455 -0
  162. package/dist/forge-payload/meta/workflows/meta-orchestrate.md +993 -0
  163. package/dist/forge-payload/meta/workflows/meta-plan-task.md +133 -0
  164. package/dist/forge-payload/meta/workflows/meta-quiz-agent.md +135 -0
  165. package/dist/forge-payload/meta/workflows/meta-retrospective.md +65 -0
  166. package/dist/forge-payload/meta/workflows/meta-review-implementation.md +119 -0
  167. package/dist/forge-payload/meta/workflows/meta-review-plan.md +108 -0
  168. package/dist/forge-payload/meta/workflows/meta-review-sprint-completion.md +65 -0
  169. package/dist/forge-payload/meta/workflows/meta-sprint-intake.md +76 -0
  170. package/dist/forge-payload/meta/workflows/meta-sprint-plan.md +147 -0
  171. package/dist/forge-payload/meta/workflows/meta-update-implementation.md +76 -0
  172. package/dist/forge-payload/meta/workflows/meta-update-plan.md +76 -0
  173. package/dist/forge-payload/meta/workflows/meta-validate.md +111 -0
  174. package/dist/forge-payload/tools/build-persona-pack.cjs +120 -11
  175. package/dist/forge-payload/tools/check-structure.cjs +344 -0
  176. package/dist/forge-payload/tools/list-skills.js +76 -0
  177. package/dist/forge-payload/tools/store-cli.cjs +27 -1
  178. package/dist/forge-payload/tools/substitute-placeholders.cjs +60 -8
  179. package/dist/forge-payload/tools/verify-integrity.cjs +86 -0
  180. package/package.json +2 -2
@@ -6,13 +6,24 @@
6
6
  // function directly. sendUserMessage is NOT used here (skill §198 pitfall).
7
7
  //
8
8
  // Implements a programmatic subset of the checks listed in
9
- // forge/forge/commands/health.md (config-completeness + KB freshness +
10
- // store integrity). Full check output is returned as structured data;
11
- // the caller formats it for display.
9
+ // forge/forge/commands/health.md. As of FORGE-S23-T07, covers 6/15 checks:
10
+ // 1. Config completeness
11
+ // 2. KB freshness
12
+ // 3. Stale docs (NEW — architecture/*.md files older than STALE_DOCS_THRESHOLD_MS)
13
+ // 8. Store integrity
14
+ // 9. Modified generated files (NEW — generation-manifest.cjs list --modified)
15
+ // 15. Plugin integrity (NEW — native TS hash check against forgeRoot/integrity.json)
16
+ //
17
+ // Full check output is returned as structured data; the caller formats it for
18
+ // display. See forge-cli/doc/health-parity-audit.md for the full parity table.
12
19
  import { execFileSync } from "node:child_process";
13
20
  import * as crypto from "node:crypto";
14
21
  import * as fs from "node:fs";
15
22
  import * as path from "node:path";
23
+ import { remediateValidationOutput } from "./lib/store-error-remediation.js";
24
+ // ── Constants ──────────────────────────────────────────────────────────────
25
+ /** Architecture docs older than this are flagged as stale (90 days). */
26
+ const STALE_DOCS_THRESHOLD_MS = 90 * 24 * 60 * 60 * 1000;
16
27
  // ── Required config fields ─────────────────────────────────────────────────
17
28
  const REQUIRED_TOP_LEVEL = ["version", "project", "stack", "commands", "paths"];
18
29
  const REQUIRED_PROJECT = ["name", "prefix"];
@@ -24,8 +35,9 @@ const REQUIRED_PATHS = ["engineering", "store", "workflows", "commands", "templa
24
35
  *
25
36
  * @param cwd - project working directory (where .forge/ lives)
26
37
  * @param bundleRoot - path to dist/forge-payload/ (contains tools/)
38
+ * @param forgeRoot - optional path to the installed Forge plugin root (for plugin integrity check)
27
39
  */
28
- export async function runHealthCheck(cwd, bundleRoot) {
40
+ export async function runHealthCheck(cwd, bundleRoot, forgeRoot) {
29
41
  const gaps = [];
30
42
  // 1. Config-completeness check
31
43
  const configPath = path.join(cwd, ".forge", "config.json");
@@ -96,7 +108,9 @@ export async function runHealthCheck(cwd, bundleRoot) {
96
108
  gaps.push({
97
109
  check: "kb-freshness",
98
110
  severity: "warning",
99
- message: "No calibration baseline found — run /forge:calibrate to establish one.",
111
+ message: "No calibration baseline found — run /forge:calibrate to establish one." +
112
+ "\n 💡 Calibration aligns the knowledge base with your current project structure." +
113
+ "\n → Set FORGE_YES=1 or FORGE_NON_INTERACTIVE=1 for hands-free calibration.",
100
114
  });
101
115
  }
102
116
  else if (calibrationBaseline.masterIndexHash) {
@@ -106,10 +120,31 @@ export async function runHealthCheck(cwd, bundleRoot) {
106
120
  const content = fs.readFileSync(masterIndexPath, "utf8");
107
121
  const currentHash = crypto.createHash("sha256").update(content, "utf8").digest("hex");
108
122
  if (currentHash !== calibrationBaseline.masterIndexHash) {
123
+ // Determine what drifted by scanning for section keywords (forge-cli#23)
124
+ const driftedSections = [];
125
+ const lowerContent = content.toLowerCase();
126
+ const sectionKeywords = [
127
+ { section: "tasks", keywords: ["task", "implement", "plan"] },
128
+ { section: "bugs", keywords: ["bug", "triag", "in-progress"] },
129
+ { section: "sprints", keywords: ["sprint", "retrospective"] },
130
+ { section: "features", keywords: ["feature", "deliver"] },
131
+ { section: "architecture", keywords: ["architecture", "stack", "database"] },
132
+ ];
133
+ for (const { section, keywords } of sectionKeywords) {
134
+ if (keywords.some((kw) => lowerContent.includes(kw))) {
135
+ driftedSections.push(section);
136
+ }
137
+ }
138
+ const driftDetail = driftedSections.length > 0
139
+ ? ` Drifted sections: ${driftedSections.join(", ")}.`
140
+ : "";
141
+ const message = `KB freshness: MASTER_INDEX.md hash has changed since last calibration — run /forge:calibrate to re-align.${driftDetail}` +
142
+ "\n 💡 Re-calibrating keeps the knowledge base in sync with sprint/task changes." +
143
+ "\n → Set FORGE_YES=1 or FORGE_NON_INTERACTIVE=1 for hands-free calibration.";
109
144
  gaps.push({
110
145
  check: "kb-freshness",
111
146
  severity: "warning",
112
- message: "KB freshness: MASTER_INDEX.md hash has changed since last calibration — consider /forge:calibrate.",
147
+ message,
113
148
  });
114
149
  }
115
150
  }
@@ -134,14 +169,73 @@ export async function runHealthCheck(cwd, bundleRoot) {
134
169
  }
135
170
  catch (err) {
136
171
  const e = err;
137
- const detail = e.stderr || e.stdout || "unknown error";
138
- gaps.push({
139
- check: "store-integrity",
140
- severity: "warning",
141
- message: `Store integrity check failed: ${detail.trim().slice(0, 200)}`,
142
- });
172
+ const output = (e.stderr || e.stdout || "").trim();
173
+ if (!output) {
174
+ gaps.push({
175
+ check: "store-integrity",
176
+ severity: "warning",
177
+ message: "Store integrity check failed with no output.",
178
+ });
179
+ }
180
+ else {
181
+ // Parse validation output into individual errors with remediation (forge-cli#24).
182
+ // Uses the shared store-error-remediation module for consistent
183
+ // user-facing hints across health-check, write-guard, and hook-dispatcher.
184
+ const lines = output.split("\n").filter((l) => l.trim());
185
+ const errors = lines.filter((l) => l.startsWith("ERROR"));
186
+ const warnings = lines.filter((l) => l.startsWith("WARN"));
187
+ const errorCount = errors.length;
188
+ const warnCount = warnings.length;
189
+ const summary = errorCount > 0
190
+ ? `${errorCount} error(s)${warnCount > 0 ? `, ${warnCount} warning(s)` : ""}`
191
+ : (warnCount > 0 ? `${warnCount} warning(s)` : "");
192
+ // Use shared remediation module to generate per-error hints and commands.
193
+ const remediations = remediateValidationOutput(output);
194
+ // Build user-friendly message with hints + commands.
195
+ const remediationLines = [];
196
+ for (const { errorLine, remediation } of remediations) {
197
+ if (remediation.hint) {
198
+ remediationLines.push(` 💡 ${remediation.hint}`);
199
+ }
200
+ if (remediation.command) {
201
+ remediationLines.push(` → ${remediation.command}`);
202
+ }
203
+ }
204
+ // Cap remediations to avoid overwhelming output.
205
+ const maxRemediations = 5;
206
+ const shown = remediationLines.slice(0, maxRemediations * 2); // hint + command per error
207
+ const moreCount = remediationLines.length - shown.length;
208
+ let message = `Store integrity: ${summary}`;
209
+ if (shown.length > 0) {
210
+ message += "\n" + shown.join("\n");
211
+ if (moreCount > 0) {
212
+ message += `\n ... and ${Math.ceil(moreCount / 2)} more.`;
213
+ }
214
+ }
215
+ gaps.push({
216
+ check: "store-integrity",
217
+ severity: errorCount > 0 ? "error" : "warning", message,
218
+ });
219
+ }
143
220
  }
144
221
  }
222
+ // 4. Stale docs check — engineering/architecture/*.md files older than 90 days
223
+ // This check runs regardless of .forge/ presence (only needs cwd + engPath).
224
+ gaps.push(...checkStaleDocs(cwd, configRecord));
225
+ // 5. Modified generated files check — generation-manifest.cjs list --modified
226
+ // Only runs when config is present (manifest lives in .forge/).
227
+ gaps.push(...checkModifiedGeneratedFiles(cwd, bundleRoot));
228
+ // 6. Plugin integrity check — native TS hash check against forgeRoot/integrity.json
229
+ // Only runs when forgeRoot is provided.
230
+ if (forgeRoot) {
231
+ gaps.push(...checkPluginIntegrity(forgeRoot));
232
+ }
233
+ // 7. Generated file structure check — check-structure.cjs
234
+ // Runs the bundled check-structure tool to verify expected files exist.
235
+ gaps.push(...checkStructure(cwd, bundleRoot));
236
+ // 8. Integrity verification — verify-integrity.cjs
237
+ // Runs the bundled verify-integrity tool to detect tampering.
238
+ gaps.push(...checkVerifyIntegrity(forgeRoot ?? bundleRoot));
145
239
  const clean = gaps.length === 0;
146
240
  const summary = clean ? "〇 /forge:health: clean." : `△ /forge:health: ${gaps.length} gap(s) detected.`;
147
241
  return {
@@ -151,4 +245,235 @@ export async function runHealthCheck(cwd, bundleRoot) {
151
245
  summary,
152
246
  };
153
247
  }
248
+ // ── Check: Stale docs ──────────────────────────────────────────────────────
249
+ /**
250
+ * Check for architecture docs not updated in over 90 days.
251
+ * Returns one HealthGap per stale file.
252
+ * Silently returns [] if the architecture directory does not exist.
253
+ */
254
+ export function checkStaleDocs(cwd, config) {
255
+ const gaps = [];
256
+ const engPath = config.paths?.engineering ?? "engineering";
257
+ const archDir = path.join(cwd, engPath, "architecture");
258
+ if (!fs.existsSync(archDir)) {
259
+ return gaps;
260
+ }
261
+ let files;
262
+ try {
263
+ files = fs.readdirSync(archDir).filter((f) => f.endsWith(".md"));
264
+ }
265
+ catch {
266
+ return gaps;
267
+ }
268
+ const now = Date.now();
269
+ for (const file of files) {
270
+ const filePath = path.join(archDir, file);
271
+ try {
272
+ const stat = fs.statSync(filePath);
273
+ const ageMs = now - stat.mtimeMs;
274
+ if (ageMs > STALE_DOCS_THRESHOLD_MS) {
275
+ const ageDays = Math.floor(ageMs / (24 * 60 * 60 * 1000));
276
+ gaps.push({
277
+ check: "stale-docs",
278
+ severity: "warning",
279
+ message: `${engPath}/architecture/${file} not updated in ${ageDays} days — may be stale. Run /forge:collate to refresh.`,
280
+ });
281
+ }
282
+ }
283
+ catch {
284
+ // Skip files we can't stat
285
+ }
286
+ }
287
+ return gaps;
288
+ }
289
+ // ── Check: Modified generated files ───────────────────────────────────────
290
+ /**
291
+ * Check for generated files that have been manually edited.
292
+ * Spawns generation-manifest.cjs list --modified from the bundled tools.
293
+ * Returns one HealthGap per modified/missing file.
294
+ * Silently returns [] if the tool is absent.
295
+ */
296
+ export function checkModifiedGeneratedFiles(cwd, bundleRoot) {
297
+ const gaps = [];
298
+ const genManifestTool = path.join(bundleRoot, "tools", "generation-manifest.cjs");
299
+ if (!fs.existsSync(genManifestTool)) {
300
+ return gaps;
301
+ }
302
+ let stdout = "";
303
+ try {
304
+ stdout = execFileSync("node", [genManifestTool, "list", "--modified"], {
305
+ cwd,
306
+ encoding: "utf8",
307
+ timeout: 10000,
308
+ stdio: ["ignore", "pipe", "pipe"],
309
+ });
310
+ }
311
+ catch (err) {
312
+ // generation-manifest exits non-zero when modifications are found.
313
+ // Parse stdout from the error object.
314
+ const e = err;
315
+ stdout = e.stdout ?? "";
316
+ }
317
+ const lines = stdout
318
+ .split("\n")
319
+ .map((l) => l.trim())
320
+ .filter((l) => l.length > 0 && !l.startsWith("#"));
321
+ for (const line of lines) {
322
+ gaps.push({
323
+ check: "modified-generated-files",
324
+ severity: "warning",
325
+ message: `Generated file modified: ${line} — regeneration will warn before overwriting. Run /forge:regenerate to review.`,
326
+ });
327
+ }
328
+ return gaps;
329
+ }
330
+ /**
331
+ * Check plugin files against the integrity.json manifest.
332
+ * Natively implemented in TypeScript — no subprocess required.
333
+ * Returns one HealthGap per modified or missing file.
334
+ * Silently returns [] if forgeRoot is not provided or integrity.json is missing.
335
+ */
336
+ export function checkPluginIntegrity(forgeRoot) {
337
+ const gaps = [];
338
+ const integrityPath = path.join(forgeRoot, "integrity.json");
339
+ if (!fs.existsSync(integrityPath)) {
340
+ gaps.push({
341
+ check: "plugin-integrity",
342
+ severity: "warning",
343
+ message: "integrity.json not found in forge plugin root — run /forge:update to restore.",
344
+ });
345
+ return gaps;
346
+ }
347
+ let manifest;
348
+ try {
349
+ manifest = JSON.parse(fs.readFileSync(integrityPath, "utf8"));
350
+ }
351
+ catch (err) {
352
+ const e = err;
353
+ gaps.push({
354
+ check: "plugin-integrity",
355
+ severity: "warning",
356
+ message: `integrity.json is not valid JSON: ${e.message}`,
357
+ });
358
+ return gaps;
359
+ }
360
+ if (!manifest.files || typeof manifest.files !== "object") {
361
+ gaps.push({
362
+ check: "plugin-integrity",
363
+ severity: "warning",
364
+ message: "integrity.json has no files section — run /forge:update to restore.",
365
+ });
366
+ return gaps;
367
+ }
368
+ for (const [relativePath, expectedHash] of Object.entries(manifest.files)) {
369
+ const absolutePath = path.join(forgeRoot, relativePath);
370
+ if (!fs.existsSync(absolutePath)) {
371
+ gaps.push({
372
+ check: "plugin-integrity",
373
+ severity: "warning",
374
+ message: `Plugin file missing: ${relativePath} — run /forge:update to restore.`,
375
+ });
376
+ continue;
377
+ }
378
+ try {
379
+ const content = fs.readFileSync(absolutePath);
380
+ const actualHash = crypto.createHash("sha256").update(content).digest("hex");
381
+ if (actualHash !== expectedHash) {
382
+ gaps.push({
383
+ check: "plugin-integrity",
384
+ severity: "warning",
385
+ message: `Plugin file modified: ${relativePath} — run /forge:update to restore.`,
386
+ });
387
+ }
388
+ }
389
+ catch {
390
+ gaps.push({
391
+ check: "plugin-integrity",
392
+ severity: "warning",
393
+ message: `Cannot read plugin file: ${relativePath} — permission or I/O error.`,
394
+ });
395
+ }
396
+ }
397
+ return gaps;
398
+ }
399
+ // ── Check: Generated file structure ──────────────────────────────────────────
400
+ /**
401
+ * Run check-structure.cjs to verify expected generated files exist.
402
+ * Returns one HealthGap per missing/extra file, or silently [] if absent.
403
+ */
404
+ export function checkStructure(cwd, bundleRoot) {
405
+ const gaps = [];
406
+ const checkStructureTool = path.join(bundleRoot, "tools", "check-structure.cjs");
407
+ if (!fs.existsSync(checkStructureTool)) {
408
+ return gaps; // Tool absent — skip silently rather than per-check noise.
409
+ }
410
+ try {
411
+ const result = execFileSync("node", [checkStructureTool, "--path", cwd], {
412
+ cwd,
413
+ encoding: "utf8",
414
+ timeout: 10000,
415
+ stdio: ["ignore", "pipe", "pipe"],
416
+ });
417
+ // check-structure exits 0 when all files are present.
418
+ // If it exits non-zero, the missing files are in stdout.
419
+ if (result.trim()) {
420
+ const lines = result.trim().split("\n").filter((l) => l.trim());
421
+ for (const line of lines) {
422
+ gaps.push({
423
+ check: "generated-structure",
424
+ severity: "warning",
425
+ message: `Generated file structure: ${line.trim()} — run /forge:update to regenerate.`,
426
+ });
427
+ }
428
+ }
429
+ }
430
+ catch (err) {
431
+ const e = err;
432
+ const output = (e.stdout || e.stderr || "").trim();
433
+ if (output) {
434
+ const lines = output.split("\n").filter((l) => l.trim());
435
+ for (const line of lines) {
436
+ gaps.push({
437
+ check: "generated-structure",
438
+ severity: "warning",
439
+ message: `Generated file structure: ${line.trim()} — run /forge:update to regenerate.`,
440
+ });
441
+ }
442
+ }
443
+ }
444
+ return gaps;
445
+ }
446
+ // ── Check: Integrity verification ─────────────────────────────────────────────
447
+ /**
448
+ * Run verify-integrity.cjs to detect plugin file tampering.
449
+ * Returns one HealthGap per modified file, or silently [] if absent.
450
+ */
451
+ export function checkVerifyIntegrity(forgeRoot) {
452
+ const gaps = [];
453
+ const verifyTool = path.join(forgeRoot, "tools", "verify-integrity.cjs");
454
+ if (!fs.existsSync(verifyTool)) {
455
+ return gaps; // Tool absent — skip silently.
456
+ }
457
+ try {
458
+ execFileSync("node", [verifyTool, "--forge-root", forgeRoot], {
459
+ cwd: forgeRoot,
460
+ encoding: "utf8",
461
+ timeout: 10000,
462
+ stdio: ["ignore", "pipe", "pipe"],
463
+ });
464
+ // verify-integrity exits 0 when all files pass.
465
+ }
466
+ catch (err) {
467
+ const e = err;
468
+ const output = (e.stdout || e.stderr || "").trim();
469
+ if (output) {
470
+ gaps.push({
471
+ check: "plugin-integrity-verify",
472
+ severity: "warning",
473
+ message: `Integrity verification: ${output.slice(0, 300)}`,
474
+ });
475
+ }
476
+ }
477
+ return gaps;
478
+ }
154
479
  //# sourceMappingURL=health-check.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"health-check.js","sourceRoot":"","sources":["../../../src/extensions/forgecli/health-check.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"health-check.js","sourceRoot":"","sources":["../../../src/extensions/forgecli/health-check.ts"],"names":[],"mappings":"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"}
@@ -26,8 +26,32 @@ export interface SprintCollateCompleteEvent {
26
26
  /** Absolute path to the project root (process.cwd() at emit time). */
27
27
  cwd: string;
28
28
  }
29
+ /**
30
+ * Payload for the migration-applied synthetic event (FORGE-S23-T01).
31
+ *
32
+ * Emitted by forge-update-command.ts after runMigrations() completes
33
+ * successfully. Enables future hook handlers to react to migration completion
34
+ * (e.g. trigger /forge:health checks, update-check resets).
35
+ *
36
+ * NOTE: No emitSyntheticEvent call is wired in this task — the event type is
37
+ * added to the union for forward compatibility. The interim emit path uses
38
+ * `store-cli emit SYS-migration` directly from forge-update-command.ts.
39
+ * event.schema.json is NOT modified in this task (system-migration type deferred
40
+ * to a follow-on task per PLAN.md §1D).
41
+ */
42
+ export interface MigrationAppliedEvent {
43
+ type: "migration-applied";
44
+ /** Version the user was running before migration */
45
+ fromVersion: string;
46
+ /** Version the user upgraded to */
47
+ toVersion: string;
48
+ /** Number of migration entries applied */
49
+ appliedCount: number;
50
+ /** Absolute path to the project root */
51
+ cwd: string;
52
+ }
29
53
  /** Union of all synthetic event payloads. Extend here as new events are added. */
30
- export type SyntheticEvent = InitCompleteEvent | SprintCollateCompleteEvent;
54
+ export type SyntheticEvent = InitCompleteEvent | SprintCollateCompleteEvent | MigrationAppliedEvent;
31
55
  /**
32
56
  * Handler signature for synthetic events.
33
57
  * ctx is the ExtensionCommandContext of the emitting phase — callers must
@@ -1,4 +1,4 @@
1
- // Pi-runtime hook adapter — FORGE-S18-T02 / FORGE-S18-T03 / FORGE-S21-T04
1
+ // Pi-runtime hook adapter — FORGE-S18-T02 / FORGE-S18-T03 / FORGE-S21-T04 / FORGE-S23-T02 / FORGE-S23-T03
2
2
  //
3
3
  // Wires Forge's hook semantics onto pi's tool_call / tool_result events.
4
4
  // T02: Provides audit-only observation scaffolding.
@@ -8,6 +8,11 @@
8
8
  // T04: Adds typed synthetic event taxonomy (InitCompleteEvent) with
9
9
  // onSyntheticEvent / emitSyntheticEvent for in-process hook dispatch
10
10
  // that bridges deterministic TS handler phases to registered hook handlers.
11
+ // T02 (S23): Adds full FS-level write-boundary schema guard via write-guard.ts —
12
+ // validates Write/Edit tool calls targeting .forge/store/**/*.json and
13
+ // .forge/config.json against Forge JSON schemas. Composed after two-layer-guard.
14
+ // T03 (S23): Adds triage-error hook — on Bash tool_result with isError=true and a
15
+ // Forge-related command, injects ctx.ui.notify suggesting /forge:report-bug.
11
16
  //
12
17
  // Audit logging: set FORGE_HOOK_AUDIT=1 to write to .forge/logs/hooks.log.
13
18
  // In enforcement mode (default): violations are blocked.
@@ -16,10 +21,17 @@
16
21
  // --force scope:
17
22
  // When --force is present in store-cli argv, transition-guard is bypassed.
18
23
  // store-validator still runs — a malformed payload is always invalid.
24
+ //
25
+ // Write-guard bypass:
26
+ // FORGE_SKIP_WRITE_VALIDATION=1 bypasses checkWriteGuard for one turn.
27
+ // The write-guard itself handles this env var; hook-dispatcher defers to it.
19
28
  import { appendFileSync, mkdirSync } from "node:fs";
20
29
  import * as path from "node:path";
21
- import { isToolCallEventType } from "@earendil-works/pi-coding-agent";
30
+ import { isBashToolResult, isToolCallEventType } from "@earendil-works/pi-coding-agent";
22
31
  import { checkTwoLayerBoundary } from "./hooks/two-layer-guard.js";
32
+ import { applyPiEdits, checkWriteGuard } from "./hooks/write-guard.js";
33
+ import { buildTriageMessage, isForgeRelated } from "./hooks/triage-error.js";
34
+ import { matchForgePermission } from "./hooks/forge-permissions.js";
23
35
  import { validateStoreCLIPayload } from "./store-validator.js";
24
36
  import { checkTransition } from "./transition-guard.js";
25
37
  // Module-level registry. Pi extension processes are single-session; the
@@ -222,20 +234,87 @@ export function registerHookDispatcher(pi, forgeRoot) {
222
234
  // ── tool_call: fires before any tool executes ─────────────────────────────
223
235
  pi.on("tool_call", (event) => {
224
236
  appendAudit(logsDir, `[tool_call] toolName=${event.toolName} toolCallId=${event.toolCallId}`);
237
+ // ── Forge-permission auto-allow (FORGE-S23-T04) ───────────────────────
238
+ // Port of forge-permissions.js pattern-match logic. Pi has no PermissionRequest
239
+ // event, so matched patterns silently return undefined (no block) from the
240
+ // tool_call handler.
241
+ //
242
+ // Bash match: full short-circuit — skip all downstream bash handling.
243
+ // Write/Edit: skip two-layer-guard (sets skipTwoLayerGuard=true), but
244
+ // FALL THROUGH to write-guard schema check (AC#4: an allowed
245
+ // write to .forge/store/ that fails schema is still blocked).
246
+ //
247
+ // Security: this guard can only ALLOW, never DENY. Patterns are ported
248
+ // verbatim from forge-permissions.js including the node -e exclusion.
249
+ //
250
+ // Important: bash commands targeting store-cli.cjs must NOT be short-circuited
251
+ // here — they need to fall through to the store-cli schema/transition validation
252
+ // below. The node-tool BASH_PATTERN matches store-cli.cjs invocations, but the
253
+ // security invariant is: store-cli payloads are always validated against schema,
254
+ // regardless of permission match. Skip forge-permissions for store-cli commands.
255
+ if (isToolCallEventType("bash", event)) {
256
+ const bashCmd = event.input.command ?? "";
257
+ if (!bashCmd.includes("store-cli.cjs")) {
258
+ const bashRule = matchForgePermission("bash", { command: bashCmd });
259
+ if (bashRule !== null) {
260
+ appendAudit(logsDir, `[forge-permissions] allowed bash: ${bashRule}`);
261
+ return undefined;
262
+ }
263
+ }
264
+ }
265
+ let skipTwoLayerGuard = false;
266
+ if (isToolCallEventType("write", event) || isToolCallEventType("edit", event)) {
267
+ const filePath = event.input.path ?? "";
268
+ const writeRule = matchForgePermission(event.toolName, { path: filePath });
269
+ if (writeRule !== null) {
270
+ appendAudit(logsDir, `[forge-permissions] allowed ${event.toolName}: ${writeRule}`);
271
+ skipTwoLayerGuard = true;
272
+ }
273
+ }
225
274
  // ── Two-layer boundary guard (FORGE-S20-T07) ───────────────────────────
226
275
  // Reject any write/edit whose target path resolves under
227
276
  // <cwd>/forge/forge/meta/. Two-layer rule: fixes to Forge itself go
228
277
  // through forge-engineer/forge-bugfixer against forge/, not via
229
278
  // forge-cli runtime edits. FORGE_HOOK_AUDIT=1 logs but never blocks.
279
+ // Skipped when forge-permissions already matched the path (skipTwoLayerGuard=true).
230
280
  if (isToolCallEventType("write", event) || isToolCallEventType("edit", event)) {
231
- const verdict = checkTwoLayerBoundary(event.input.path, process.cwd());
232
- if (!verdict.allowed) {
233
- appendAudit(logsDir, `[two-layer-guard] decision=would-block path=${verdict.resolvedPath} reason=${verdict.reason}`);
281
+ if (!skipTwoLayerGuard) {
282
+ const verdict = checkTwoLayerBoundary(event.input.path, process.cwd());
283
+ if (!verdict.allowed) {
284
+ appendAudit(logsDir, `[two-layer-guard] decision=would-block path=${verdict.resolvedPath} reason=${verdict.reason}`);
285
+ if (auditEnabled()) {
286
+ return undefined;
287
+ }
288
+ return { block: true, reason: verdict.reason };
289
+ }
290
+ }
291
+ // ── Write-boundary schema guard (FORGE-S23-T02) ─────────────────────
292
+ // After two-layer passes (or is skipped), validate the post-write contents
293
+ // against the Forge schema for the target path. Always runs regardless of
294
+ // skipTwoLayerGuard — AC#4: an allowed write to .forge/store/ that fails
295
+ // schema is still blocked. Only applies to .forge/store/** and
296
+ // .forge/config.json paths (registry-matched). Fail-open on internal errors.
297
+ // FORGE_SKIP_WRITE_VALIDATION=1 bypasses.
298
+ const resolvedPath = path.resolve(process.cwd(), event.input.path);
299
+ let postContents;
300
+ if (isToolCallEventType("write", event)) {
301
+ postContents = typeof event.input.content === "string" ? event.input.content : "";
302
+ }
303
+ else {
304
+ // Edit event: apply pi edits array to current file contents
305
+ const editsInput = event.input.edits;
306
+ const edits = Array.isArray(editsInput) ? editsInput : [];
307
+ postContents = applyPiEdits(resolvedPath, edits);
308
+ }
309
+ const guardResult = checkWriteGuard(resolvedPath, postContents, forgeRoot);
310
+ if (guardResult.block) {
311
+ appendAudit(logsDir, `[write-guard] decision=would-block path=${resolvedPath} reason=${guardResult.reason ?? "schema violation"}`);
234
312
  if (auditEnabled()) {
235
313
  return undefined;
236
314
  }
237
- return { block: true, reason: verdict.reason };
315
+ return { block: true, reason: guardResult.reason };
238
316
  }
317
+ appendAudit(logsDir, `[write-guard] decision=would-allow path=${resolvedPath}`);
239
318
  }
240
319
  // Bash interception: identify store-cli write/update-status calls.
241
320
  if (isToolCallEventType("bash", event)) {
@@ -256,7 +335,7 @@ export function registerHookDispatcher(pi, forgeRoot) {
256
335
  // Audit mode: log and allow.
257
336
  return undefined;
258
337
  }
259
- return { block: true, reason: validation.reason };
338
+ return { block: true, reason: validation.remediation || validation.reason };
260
339
  }
261
340
  appendAudit(logsDir, `[store-cli-intercept] decision=would-allow`);
262
341
  }
@@ -280,7 +359,9 @@ export function registerHookDispatcher(pi, forgeRoot) {
280
359
  if (auditEnabled()) {
281
360
  return undefined;
282
361
  }
283
- return { block: true, reason: guard.reason };
362
+ // Add remediation command to transition error (forge-cli#24)
363
+ const transitionHint = `\n → node "$FORGE_ROOT/tools/store-cli.cjs" update-status ${intercept.entity} ${payloadRecord.entityId} status <legal-value>\n Or add --force to bypass transition guard.`;
364
+ return { block: true, reason: guard.reason + transitionHint };
284
365
  }
285
366
  appendAudit(logsDir, `[store-cli-intercept] decision=would-allow`);
286
367
  }
@@ -292,10 +373,26 @@ export function registerHookDispatcher(pi, forgeRoot) {
292
373
  }
293
374
  return undefined;
294
375
  });
295
- // ── tool_result: fires after any tool completes (observe-only) ──────────────
296
- pi.on("tool_result", (event) => {
376
+ // ── tool_result: fires after any tool completes ───────────────────────────
377
+ pi.on("tool_result", (event, ctx) => {
297
378
  appendAudit(logsDir, `[tool_result] toolName=${event.toolName} toolCallId=${event.toolCallId}`);
298
- // Audit-only in T02 return void (no result replacement).
379
+ // ── Triage-error: post-Bash-failure context injection (FORGE-S23-T03) ──
380
+ // When a Bash command exits non-zero and matches Forge-related patterns,
381
+ // notify the user to file a bug via /forge:report-bug.
382
+ if (isBashToolResult(event) && event.isError) {
383
+ const command = typeof event.input.command === "string" ? event.input.command : "";
384
+ if (isForgeRelated(command)) {
385
+ const snippet = event.content
386
+ .filter((c) => c.type === "text")
387
+ .map((c) => c.text)
388
+ .join("")
389
+ .split("\n")
390
+ .slice(0, 3)
391
+ .join(" ")
392
+ .trim();
393
+ ctx.ui.notify(buildTriageMessage(command, snippet), "warning");
394
+ }
395
+ }
299
396
  });
300
397
  }
301
398
  //# sourceMappingURL=hook-dispatcher.js.map