@entelligentsia/forgecli 1.0.21 → 1.0.36

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 (218) hide show
  1. package/CHANGELOG.md +346 -0
  2. package/README.md +2 -0
  3. package/dist/CHANGELOG-forge-plugin.md +281 -0
  4. package/dist/bin/argv.d.ts +2 -2
  5. package/dist/bin/argv.js +25 -0
  6. package/dist/bin/argv.js.map +1 -1
  7. package/dist/bin/forge.js +12 -0
  8. package/dist/bin/forge.js.map +1 -1
  9. package/dist/bin/init.d.ts +23 -0
  10. package/dist/bin/init.js +123 -0
  11. package/dist/bin/init.js.map +1 -0
  12. package/dist/bin/uninstall.d.ts +20 -0
  13. package/dist/bin/uninstall.js +141 -0
  14. package/dist/bin/uninstall.js.map +1 -0
  15. package/dist/extensions/forgecli/claude-bootstrap/bootstrap.d.ts +40 -0
  16. package/dist/extensions/forgecli/claude-bootstrap/bootstrap.js +593 -0
  17. package/dist/extensions/forgecli/claude-bootstrap/bootstrap.js.map +1 -0
  18. package/dist/extensions/forgecli/claude-bootstrap/settings-merge.d.ts +46 -0
  19. package/dist/extensions/forgecli/claude-bootstrap/settings-merge.js +245 -0
  20. package/dist/extensions/forgecli/claude-bootstrap/settings-merge.js.map +1 -0
  21. package/dist/extensions/forgecli/claude-bootstrap/uninstall.d.ts +23 -0
  22. package/dist/extensions/forgecli/claude-bootstrap/uninstall.js +215 -0
  23. package/dist/extensions/forgecli/claude-bootstrap/uninstall.js.map +1 -0
  24. package/dist/extensions/forgecli/dashboard/component.js +10 -7
  25. package/dist/extensions/forgecli/dashboard/component.js.map +1 -1
  26. package/dist/extensions/forgecli/forge-tools.d.ts +1 -0
  27. package/dist/extensions/forgecli/forge-tools.js +73 -0
  28. package/dist/extensions/forgecli/forge-tools.js.map +1 -1
  29. package/dist/extensions/forgecli/lib/forge-root.d.ts +5 -0
  30. package/dist/extensions/forgecli/lib/forge-root.js +14 -1
  31. package/dist/extensions/forgecli/lib/forge-root.js.map +1 -1
  32. package/dist/extensions/forgecli/orchestrators/bug/bug-body.d.ts +1 -0
  33. package/dist/extensions/forgecli/orchestrators/bug/bug-body.js +65 -0
  34. package/dist/extensions/forgecli/orchestrators/bug/bug-body.js.map +1 -0
  35. package/dist/extensions/forgecli/orchestrators/bug/bug-id.d.ts +23 -0
  36. package/dist/extensions/forgecli/orchestrators/bug/bug-id.js +140 -0
  37. package/dist/extensions/forgecli/orchestrators/bug/bug-id.js.map +1 -0
  38. package/dist/extensions/forgecli/orchestrators/bug/bug-phase-dispatch.d.ts +54 -0
  39. package/dist/extensions/forgecli/orchestrators/bug/bug-phase-dispatch.js +349 -0
  40. package/dist/extensions/forgecli/orchestrators/bug/bug-phase-dispatch.js.map +1 -0
  41. package/dist/extensions/forgecli/orchestrators/bug/bug-phases.d.ts +8 -0
  42. package/dist/extensions/forgecli/orchestrators/bug/bug-phases.js +60 -0
  43. package/dist/extensions/forgecli/orchestrators/bug/bug-phases.js.map +1 -0
  44. package/dist/extensions/forgecli/orchestrators/bug/bug-state.d.ts +14 -0
  45. package/dist/extensions/forgecli/orchestrators/bug/bug-state.js +100 -0
  46. package/dist/extensions/forgecli/orchestrators/bug/bug-state.js.map +1 -0
  47. package/dist/extensions/forgecli/orchestrators/bug/bug-triage-routing.d.ts +72 -0
  48. package/dist/extensions/forgecli/orchestrators/bug/bug-triage-routing.js +204 -0
  49. package/dist/extensions/forgecli/orchestrators/bug/bug-triage-routing.js.map +1 -0
  50. package/dist/extensions/forgecli/orchestrators/bug/bug-verdict-loop.d.ts +38 -0
  51. package/dist/extensions/forgecli/orchestrators/bug/bug-verdict-loop.js +166 -0
  52. package/dist/extensions/forgecli/orchestrators/bug/bug-verdict-loop.js.map +1 -0
  53. package/dist/extensions/forgecli/orchestrators/bug/bug-verdict.d.ts +3 -0
  54. package/dist/extensions/forgecli/orchestrators/bug/bug-verdict.js +55 -0
  55. package/dist/extensions/forgecli/orchestrators/bug/bug-verdict.js.map +1 -0
  56. package/dist/extensions/forgecli/orchestrators/bug/run-bug-command.d.ts +7 -0
  57. package/dist/extensions/forgecli/orchestrators/bug/run-bug-command.js +293 -0
  58. package/dist/extensions/forgecli/orchestrators/bug/run-bug-command.js.map +1 -0
  59. package/dist/extensions/forgecli/orchestrators/bug/run-bug-pipeline.d.ts +2 -0
  60. package/dist/extensions/forgecli/orchestrators/bug/run-bug-pipeline.js +501 -0
  61. package/dist/extensions/forgecli/orchestrators/bug/run-bug-pipeline.js.map +1 -0
  62. package/dist/extensions/forgecli/orchestrators/bug/run-bug-types.d.ts +41 -0
  63. package/dist/extensions/forgecli/orchestrators/bug/run-bug-types.js +5 -0
  64. package/dist/extensions/forgecli/orchestrators/bug/run-bug-types.js.map +1 -0
  65. package/dist/extensions/forgecli/orchestrators/common/orchestrator-entry.d.ts +43 -0
  66. package/dist/extensions/forgecli/orchestrators/common/orchestrator-entry.js +85 -0
  67. package/dist/extensions/forgecli/orchestrators/common/orchestrator-entry.js.map +1 -0
  68. package/dist/extensions/forgecli/orchestrators/common/orchestrator-misc.d.ts +8 -0
  69. package/dist/extensions/forgecli/orchestrators/common/orchestrator-misc.js +37 -0
  70. package/dist/extensions/forgecli/orchestrators/common/orchestrator-misc.js.map +1 -0
  71. package/dist/extensions/forgecli/orchestrators/common/orchestrator-notify.d.ts +28 -0
  72. package/dist/extensions/forgecli/orchestrators/common/orchestrator-notify.js +45 -0
  73. package/dist/extensions/forgecli/orchestrators/common/orchestrator-notify.js.map +1 -0
  74. package/dist/extensions/forgecli/orchestrators/common/orchestrator-transcript-session.d.ts +26 -0
  75. package/dist/extensions/forgecli/orchestrators/common/orchestrator-transcript-session.js +75 -0
  76. package/dist/extensions/forgecli/orchestrators/common/orchestrator-transcript-session.js.map +1 -0
  77. package/dist/extensions/forgecli/orchestrators/common/summary-recovery.d.ts +24 -0
  78. package/dist/extensions/forgecli/orchestrators/common/summary-recovery.js +37 -0
  79. package/dist/extensions/forgecli/orchestrators/common/summary-recovery.js.map +1 -0
  80. package/dist/extensions/forgecli/orchestrators/fix-bug.d.ts +9 -92
  81. package/dist/extensions/forgecli/orchestrators/fix-bug.js +23 -1695
  82. package/dist/extensions/forgecli/orchestrators/fix-bug.js.map +1 -1
  83. package/dist/extensions/forgecli/orchestrators/run-sprint.d.ts +3 -12
  84. package/dist/extensions/forgecli/orchestrators/run-sprint.js +97 -270
  85. package/dist/extensions/forgecli/orchestrators/run-sprint.js.map +1 -1
  86. package/dist/extensions/forgecli/orchestrators/run-task.d.ts +10 -214
  87. package/dist/extensions/forgecli/orchestrators/run-task.js +31 -1481
  88. package/dist/extensions/forgecli/orchestrators/run-task.js.map +1 -1
  89. package/dist/extensions/forgecli/orchestrators/sprint/sprint-ceremony.d.ts +33 -0
  90. package/dist/extensions/forgecli/orchestrators/sprint/sprint-ceremony.js +135 -0
  91. package/dist/extensions/forgecli/orchestrators/sprint/sprint-ceremony.js.map +1 -0
  92. package/dist/extensions/forgecli/orchestrators/sprint/sprint-state.d.ts +18 -0
  93. package/dist/extensions/forgecli/orchestrators/sprint/sprint-state.js +55 -0
  94. package/dist/extensions/forgecli/orchestrators/sprint/sprint-state.js.map +1 -0
  95. package/dist/extensions/forgecli/orchestrators/task/run-task-command.d.ts +9 -0
  96. package/dist/extensions/forgecli/orchestrators/task/run-task-command.js +174 -0
  97. package/dist/extensions/forgecli/orchestrators/task/run-task-command.js.map +1 -0
  98. package/dist/extensions/forgecli/orchestrators/task/run-task-pipeline.d.ts +2 -0
  99. package/dist/extensions/forgecli/orchestrators/task/run-task-pipeline.js +494 -0
  100. package/dist/extensions/forgecli/orchestrators/task/run-task-pipeline.js.map +1 -0
  101. package/dist/extensions/forgecli/orchestrators/task/run-task-types.d.ts +62 -0
  102. package/dist/extensions/forgecli/orchestrators/task/run-task-types.js +5 -0
  103. package/dist/extensions/forgecli/orchestrators/task/run-task-types.js.map +1 -0
  104. package/dist/extensions/forgecli/orchestrators/task/task-body.d.ts +4 -0
  105. package/dist/extensions/forgecli/orchestrators/task/task-body.js +48 -0
  106. package/dist/extensions/forgecli/orchestrators/task/task-body.js.map +1 -0
  107. package/dist/extensions/forgecli/orchestrators/task/task-events.d.ts +63 -0
  108. package/dist/extensions/forgecli/orchestrators/task/task-events.js +185 -0
  109. package/dist/extensions/forgecli/orchestrators/task/task-events.js.map +1 -0
  110. package/dist/extensions/forgecli/orchestrators/task/task-gates.d.ts +34 -0
  111. package/dist/extensions/forgecli/orchestrators/task/task-gates.js +78 -0
  112. package/dist/extensions/forgecli/orchestrators/task/task-gates.js.map +1 -0
  113. package/dist/extensions/forgecli/orchestrators/task/task-phase-dispatch.d.ts +42 -0
  114. package/dist/extensions/forgecli/orchestrators/task/task-phase-dispatch.js +370 -0
  115. package/dist/extensions/forgecli/orchestrators/task/task-phase-dispatch.js.map +1 -0
  116. package/dist/extensions/forgecli/orchestrators/task/task-phases.d.ts +14 -0
  117. package/dist/extensions/forgecli/orchestrators/task/task-phases.js +26 -0
  118. package/dist/extensions/forgecli/orchestrators/task/task-phases.js.map +1 -0
  119. package/dist/extensions/forgecli/orchestrators/task/task-record.d.ts +9 -0
  120. package/dist/extensions/forgecli/orchestrators/task/task-record.js +58 -0
  121. package/dist/extensions/forgecli/orchestrators/task/task-record.js.map +1 -0
  122. package/dist/extensions/forgecli/orchestrators/task/task-state.d.ts +14 -0
  123. package/dist/extensions/forgecli/orchestrators/task/task-state.js +35 -0
  124. package/dist/extensions/forgecli/orchestrators/task/task-state.js.map +1 -0
  125. package/dist/extensions/forgecli/orchestrators/task/task-verdict-loop.d.ts +36 -0
  126. package/dist/extensions/forgecli/orchestrators/task/task-verdict-loop.js +152 -0
  127. package/dist/extensions/forgecli/orchestrators/task/task-verdict-loop.js.map +1 -0
  128. package/dist/extensions/forgecli/update/forge-update-command.js +10 -7
  129. package/dist/extensions/forgecli/update/forge-update-command.js.map +1 -1
  130. package/dist/forge-payload/.base-pack/commands/approve.md +2 -2
  131. package/dist/forge-payload/.base-pack/commands/check-agent.md +2 -2
  132. package/dist/forge-payload/.base-pack/commands/collate.md +2 -2
  133. package/dist/forge-payload/.base-pack/commands/commit.md +2 -2
  134. package/dist/forge-payload/.base-pack/commands/enhance.md +2 -2
  135. package/dist/forge-payload/.base-pack/commands/fix-bug.md +2 -2
  136. package/dist/forge-payload/.base-pack/commands/implement.md +2 -2
  137. package/dist/forge-payload/.base-pack/commands/init.md +278 -0
  138. package/dist/forge-payload/.base-pack/commands/new-sprint.md +2 -2
  139. package/dist/forge-payload/.base-pack/commands/plan-sprint.md +2 -2
  140. package/dist/forge-payload/.base-pack/commands/plan.md +2 -2
  141. package/dist/forge-payload/.base-pack/commands/retro.md +2 -2
  142. package/dist/forge-payload/.base-pack/commands/review-code.md +2 -2
  143. package/dist/forge-payload/.base-pack/commands/review-plan.md +2 -2
  144. package/dist/forge-payload/.base-pack/commands/run-sprint.md +2 -2
  145. package/dist/forge-payload/.base-pack/commands/run-task.md +2 -2
  146. package/dist/forge-payload/.base-pack/commands/validate.md +2 -2
  147. package/dist/forge-payload/.base-pack/workflows/_fragments/event-emission-schema.md +4 -0
  148. package/dist/forge-payload/.base-pack/workflows/_fragments/event-vocabulary.md +88 -0
  149. package/dist/forge-payload/.base-pack/workflows/collator_agent.md +5 -6
  150. package/dist/forge-payload/.base-pack/workflows/commit_task.md +41 -38
  151. package/dist/forge-payload/.base-pack/workflows/implement_plan.md +3 -3
  152. package/dist/forge-payload/.base-pack/workflows/migrate_structural.md +1 -1
  153. package/dist/forge-payload/.base-pack/workflows-js/wfl-fix-bug.js +42 -6
  154. package/dist/forge-payload/.base-pack/workflows-js/wfl-init.js +449 -0
  155. package/dist/forge-payload/.base-pack/workflows-js/wfl-run-task.js +32 -1
  156. package/dist/forge-payload/.claude-plugin/plugin.json +1 -1
  157. package/dist/forge-payload/.schemas/enum-catalog.json +2 -2
  158. package/dist/forge-payload/.schemas/event.schema.json +8 -3
  159. package/dist/forge-payload/.schemas/migrations.json +141 -0
  160. package/dist/forge-payload/commands/add-pipeline.md +1 -1
  161. package/dist/forge-payload/commands/add-task.md +3 -3
  162. package/dist/forge-payload/commands/ask.md +1 -1
  163. package/dist/forge-payload/commands/check-agent.md +1 -1
  164. package/dist/forge-payload/commands/config.md +1 -1
  165. package/dist/forge-payload/commands/health.md +1 -1
  166. package/dist/forge-payload/commands/init.md +62 -7
  167. package/dist/forge-payload/commands/rebuild.md +3 -3
  168. package/dist/forge-payload/commands/remove.md +1 -1
  169. package/dist/forge-payload/commands/repair.md +1 -1
  170. package/dist/forge-payload/commands/report-bug.md +1 -1
  171. package/dist/forge-payload/commands/status.md +1 -1
  172. package/dist/forge-payload/commands/update.md +3 -3
  173. package/dist/forge-payload/hooks/lib/common.cjs +228 -0
  174. package/dist/forge-payload/hooks/lib/plugin-detection.cjs +106 -0
  175. package/dist/forge-payload/hooks/lib/update-msg.cjs +23 -0
  176. package/dist/forge-payload/hooks/lib/update-url.cjs +46 -0
  177. package/dist/forge-payload/hooks/lib/write-registry.js +53 -0
  178. package/dist/forge-payload/init/discovery/discover-database.md +32 -0
  179. package/dist/forge-payload/init/discovery/discover-processes.md +31 -0
  180. package/dist/forge-payload/init/discovery/discover-routing.md +31 -0
  181. package/dist/forge-payload/init/discovery/discover-stack.md +33 -0
  182. package/dist/forge-payload/init/discovery/discover-testing.md +34 -0
  183. package/dist/forge-payload/init/generation/generate-commands.md +171 -0
  184. package/dist/forge-payload/init/generation/generate-kb-doc.md +60 -0
  185. package/dist/forge-payload/init/generation/generate-knowledge-base.md +56 -0
  186. package/dist/forge-payload/init/generation/generate-persona.md +73 -0
  187. package/dist/forge-payload/init/generation/generate-personas.md +54 -0
  188. package/dist/forge-payload/init/generation/generate-skill.md +66 -0
  189. package/dist/forge-payload/init/generation/generate-skills.md +36 -0
  190. package/dist/forge-payload/init/generation/generate-template.md +60 -0
  191. package/dist/forge-payload/init/generation/generate-templates.md +39 -0
  192. package/dist/forge-payload/init/generation/generate-tools.md +133 -0
  193. package/dist/forge-payload/init/generation/generate-workflows.md +78 -0
  194. package/dist/forge-payload/init/phases/phase-1-collect.md +10 -2
  195. package/dist/forge-payload/init/phases/phase-3-materialize.md +1 -1
  196. package/dist/forge-payload/init/phases/phase-4-register.md +8 -0
  197. package/dist/forge-payload/init/workflow-gen-plan.json +17 -0
  198. package/dist/forge-payload/integrity.json +16 -16
  199. package/dist/forge-payload/meta/store-schema/event.schema.md +7 -0
  200. package/dist/forge-payload/meta/workflows/_fragments/event-emission-schema.md +4 -0
  201. package/dist/forge-payload/meta/workflows/_fragments/event-vocabulary.md +88 -0
  202. package/dist/forge-payload/meta/workflows/meta-collate.md +5 -6
  203. package/dist/forge-payload/meta/workflows/meta-commit.md +46 -43
  204. package/dist/forge-payload/meta/workflows/meta-fix-bug.md +7 -2
  205. package/dist/forge-payload/meta/workflows/meta-implement.md +3 -3
  206. package/dist/forge-payload/meta/workflows/meta-migrate.md +1 -1
  207. package/dist/forge-payload/meta/workflows/meta-orchestrate.md +4 -1
  208. package/dist/forge-payload/schemas/enum-catalog.json +2 -2
  209. package/dist/forge-payload/schemas/event.schema.json +8 -3
  210. package/dist/forge-payload/schemas/structure-manifest.json +5 -12
  211. package/dist/forge-payload/tools/commit-task.cjs +218 -0
  212. package/dist/forge-payload/tools/forge-preflight.cjs +268 -0
  213. package/dist/forge-payload/tools/lib/paths.cjs +12 -11
  214. package/dist/forge-payload/tools/lib/pricing.cjs +31 -11
  215. package/dist/forge-payload/tools/query-logger.cjs +34 -0
  216. package/dist/forge-payload/tools/store-cli.cjs +6 -1
  217. package/dist/forge-payload/tools/substitute-placeholders.cjs +5 -6
  218. package/package.json +2 -2
@@ -1,4 +1,71 @@
1
1
  {
2
+ "1.2.21": {
3
+ "version": "1.3.0",
4
+ "date": "2026-06-07",
5
+ "notes": "CLI-first bootstrap: /forge:init now dispatches workflow('wfl:init') — the dynamic JS driver added in S31-T04. init.md command wrapper hoists interactive prompts (KB folder, CLAUDE.md offer, marketplace-skills offer). Phase-1 and Phase-4 rulebooks annotated with orchestrator-owned step markers. sdlc-init.md reduced to a spec pointer. New base-pack/commands/init.md project-local wrapper authored. No change to installed projects (init.md is a plugin command, not a materialized base-pack command).",
6
+ "target": "commands:init,init:wfl-init,init:phases:phase-1-collect,init:phases:phase-4-register,init:sdlc-init,init:base-pack:commands:init",
7
+ "regenerate": [],
8
+ "breaking": false,
9
+ "manual": [
10
+ "No action required for existing initialized projects. New inits use the dynamic wfl:init driver. The Workflow tool is required; upgrade Claude Code if not available."
11
+ ]
12
+ },
13
+ "1.2.20": {
14
+ "version": "1.2.21",
15
+ "date": "2026-06-06",
16
+ "notes": "commit-task.cjs hardening from its first live firing (HELLO-BUG-002 transcript analysis, forge-engineering#40). (1) Gitignored staging paths are pre-filtered with git check-ignore and warn-skipped — previously one ignored path aborted the all-or-nothing git add and the agent retried the identical command. (2) A clean staging set is now a legitimate no-op SUCCESS: the tool seals the record's terminal status and returns {ok:true, committed:false, reason:nothing-to-commit} — a bug whose fix is already at HEAD ends fixed without improvised manual transitions. Result JSON gains an explicit committed flag. meta-commit.md: forge_commit named tool is the primary surface on forgecli (typed args — no shell-quoting of the commit message); bash form stays as the Claude Code fallback; no-op outcome documented as success.",
17
+ "target": "tools:commit-task,workflows:commit_task",
18
+ "regenerate": [
19
+ "tools",
20
+ "workflows"
21
+ ],
22
+ "breaking": false,
23
+ "manual": [
24
+ "Run /forge:update, then /forge:rebuild tools workflows to refresh .forge/tools/commit-task.cjs and .forge/workflows/commit_task.md."
25
+ ]
26
+ },
27
+ "1.2.19": {
28
+ "version": "1.2.20",
29
+ "date": "2026-06-06",
30
+ "notes": "Deterministic commit choreography (forge-engineering#40). The commit phase was the most expensive phase of the pipeline (15-31% of run input tokens) because an LLM re-derived deterministic choreography turn-by-turn. New tools/commit-task.cjs owns the whole sequence — preflight gate, status precondition, staging-set derivation (record artifact dir + implementation file provenance), commit-boundary guard (aborts on a pre-populated index), git commit, terminal status transition (task→committed / bug→fixed) — with --dry-run, argv-array spawns, and operator-gated --force. The implement phase now records files_changed provenance in IMPLEMENTATION-SUMMARY.json (new optional PHASE_SUMMARY_SCHEMA key, max 100 paths) so commit never re-derives the change surface from git. meta-commit.md rewritten: agent inspects once (git diff --stat), crafts the message, calls the tool once; explicit no-summary contract (commit is not in VALID_SUMMARY_PHASES — the previous prompt commanded writing a SUMMARY that the schema rejects, sending agents on an unwinnable format hunt plus 3 write-verification retries); batched-inspection rule; commit-early/reset/redo loop forbidden.",
31
+ "target": "tools:commit-task,tools:store-cli,workflows:commit_task,workflows:implement_plan",
32
+ "regenerate": [
33
+ "tools",
34
+ "workflows"
35
+ ],
36
+ "breaking": false,
37
+ "manual": [
38
+ "Run /forge:update, then /forge:rebuild tools workflows to vendor .forge/tools/commit-task.cjs and refresh .forge/workflows/commit_task.md + implement_plan.md. Until the rebuild, the regenerated commit workflow will reference a tool that is not yet vendored and will halt loudly (by design)."
39
+ ]
40
+ },
41
+ "1.2.18": {
42
+ "version": "1.2.19",
43
+ "date": "2026-06-06",
44
+ "notes": "Event-type discipline in the JS workflow drivers (forge-engineering#39 Phase 3). wfl-run-task.js and wfl-fix-bug.js now carry TASK_TYPE_TOKENS / BUG_TYPE_TOKENS maps (verbatim ports of _fragments/event-vocabulary.md) and inject explicit per-phase type guidance into the subagent emit instructions: start events MUST NOT carry a type field, complete events set the exact pass/fail token for the phase, and copying the action value (start/complete) into type is explicitly forbidden — that guess-leak was the source of the schema-rejected type:start/type:complete store residue. Also fixes wfl-run-task emitSkip(): the skip event emitted iteration 0, violating the schema's minimum of 1, so every pre-task skip event was silently rejected; now 1.",
45
+ "target": "workflows-js:wfl-run-task,workflows-js:wfl-fix-bug",
46
+ "regenerate": [
47
+ "workflows-js"
48
+ ],
49
+ "breaking": false,
50
+ "manual": [
51
+ "Run /forge:update, then /forge:rebuild to regenerate .claude/workflows/wfl-run-task.js and wfl-fix-bug.js from the base-pack. Prompt/instruction text + one literal fix — no schema or tool change."
52
+ ]
53
+ },
54
+ "1.2.17": {
55
+ "version": "1.2.18",
56
+ "date": "2026-06-06",
57
+ "notes": "Event-type vocabulary gets a spec owner (forge-engineering#39). New canonical fragment _fragments/event-vocabulary.md defines the phase→type token tables for task/bug/sprint events; schemas/event.schema.json now mirrors it: adds the bug-pipeline fail tokens fix-revision-requested (approve) and bug-commit-failed (commit) that forge-cli already emits (previously rejected by the enum — events and their token telemetry were silently dropped), adds bug-skipped (pre-loop skip; requires bugId, no phase/iteration), and retires bug-fixed (defined, emitted by nothing, zero store occurrences). The wfl-fix-bug.js base-pack driver's skip emit was triple-broken against the schema (underscore bug_skipped token, missing required eventId/model/provider, undeclared reason key) — now emits a valid bug-skipped event with the skip reason in notes and model/provider \"n/a\". meta-fix-bug.md and meta-orchestrate.md reference the vocabulary fragment; emitted tokens MUST come from its tables.",
58
+ "target": "schemas:event,fragments:event-vocabulary,fragments:event-emission-schema,workflows:fix_bug,workflows:orchestrate_task,workflows-js:wfl-fix-bug",
59
+ "regenerate": [
60
+ "schemas",
61
+ "workflows",
62
+ "workflows-js"
63
+ ],
64
+ "breaking": false,
65
+ "manual": [
66
+ "Run /forge:update, then /forge:rebuild schemas workflows to refresh .forge/schemas/event.schema.json, .forge/workflows/ (fix_bug.md, orchestrate_task.md, _fragments/), and the regenerated .claude/workflows/wfl-fix-bug.js. Projects with the legacy invalid store events (type start/complete/feature_implemented) can clear them via /forge:repair — strip the invalid type field (untyped events are valid)."
67
+ ]
68
+ },
2
69
  "1.2.16": {
3
70
  "version": "1.2.17",
4
71
  "date": "2026-06-04",
@@ -2857,5 +2924,79 @@
2857
2924
  ],
2858
2925
  "breaking": false,
2859
2926
  "manual": []
2927
+ },
2928
+ "1.3.0": {
2929
+ "version": "1.4.0",
2930
+ "date": "2026-06-07",
2931
+ "notes": "Fixed /forge:* command namespace (CLI-first redesign). Project-prefix command namespaces (/acme:*, /hello:*) are retired: getCommandsSubdir() now returns 'forge' unconditionally, base-pack command templates carry literal /forge: headings ({{PREFIX}} removed), and substitute-placeholders materializes commands to .claude/commands/forge/. All command .md files (plugin commands/ + base-pack commands/) resolve FORGE_ROOT with a vendored fallback: ${CLAUDE_PLUGIN_ROOT:-$(pwd)/.forge} — full parity for plugin-less projects bootstrapped by '4ge init claude' where tools/schemas/hooks are vendored under .forge/. Rulebooks (generate-commands, phase-3-materialize, phase-1-collect, meta-migrate, migrate_structural) updated to the fixed namespace.",
2932
+ "target": "tools:lib:paths,tools:substitute-placeholders,commands:*,init:base-pack:commands:*,init:generation:generate-commands,init:phases:phase-1-collect,init:phases:phase-3-materialize,meta:workflows:meta-migrate,init:base-pack:workflows:migrate_structural",
2933
+ "regenerate": [
2934
+ "commands"
2935
+ ],
2936
+ "breaking": true,
2937
+ "manual": [
2938
+ "Existing projects with prefix-namespaced commands (.claude/commands/<prefix>/): re-run materialization (or /forge:rebuild) to produce .claude/commands/forge/, then delete the old .claude/commands/<prefix>/ directory. The old commands keep working until removed but will not receive updates."
2939
+ ]
2940
+ },
2941
+ "1.4.0": {
2942
+ "version": "1.4.1",
2943
+ "date": "2026-06-07",
2944
+ "notes": "wfl-init.js Workflow-harness parse fix: the driver shipped wrapped in 'export default async function wflInit(args)' — the Workflow tool permits exactly one export (the meta literal) and evaluates the rest as an async function body, so every /forge:init dispatch failed at launch with \"SyntaxError: Unexpected keyword 'export'\". Body now runs at top level reading the global args. New regression test (wfl-drivers-parse.test.cjs) parses all four base-pack drivers exactly the way the harness does.",
2945
+ "target": "init:base-pack:workflows-js:wfl-init",
2946
+ "regenerate": [
2947
+ "workflows"
2948
+ ],
2949
+ "breaking": false,
2950
+ "manual": [
2951
+ "Projects bootstrapped with the broken driver: re-run '4ge init claude .' (idempotent repair overwrites .claude/workflows/wfl-init.js)."
2952
+ ]
2953
+ },
2954
+ "1.4.1": {
2955
+ "version": "1.4.2",
2956
+ "date": "2026-06-07",
2957
+ "notes": "wfl-init.js Workflow-API rewrite (forge#112): the driver mis-used the harness API four ways — phase(title, callback) callbacks were silently discarded so init ran 0 agents while reporting fabricated ok:true; parallel() received promises instead of thunks; agent() had model as first arg instead of opts.model; structured results lacked opts.schema. Phase bodies now run inline at top level, fan-outs use thunks, all agents use agent(prompt, {model, label, phase, schema}), and the report reflects real phase results. Rulebook reads use vendored .forge/init/... paths with direct-analysis fallback. Contract enforced by wfl-drivers-parse.test.cjs (phase-callback, thunk, arg-order, referenced-rulebook-exists gates).",
2958
+ "target": "init:base-pack:workflows-js:wfl-init",
2959
+ "regenerate": [
2960
+ "workflows"
2961
+ ],
2962
+ "breaking": false,
2963
+ "manual": [
2964
+ "Projects bootstrapped with the broken driver: re-run '4ge init claude .' (idempotent repair overwrites .claude/workflows/wfl-init.js)."
2965
+ ]
2966
+ },
2967
+ "1.4.2": {
2968
+ "version": "1.4.3",
2969
+ "date": "2026-06-07",
2970
+ "notes": "wfl-init.js verify-phase CLI alignment (forge#112 follow-up): the Phase 2 verify prompts invoked 'verify-phase.cjs --phase 2' bare, but the tool requires '--phase 2 --kb-path <path>' (exit 2 without it). Both Phase 2 invocations now pass --kb-path \"${kbFolder}\". New contract gates in wfl-drivers-parse.test.cjs assert every driver verify-phase invocation uses a supported phase (1-3) and that --phase 2 always carries --kb-path.",
2971
+ "target": "init:base-pack:workflows-js:wfl-init",
2972
+ "regenerate": [
2973
+ "workflows"
2974
+ ],
2975
+ "breaking": false,
2976
+ "manual": []
2977
+ },
2978
+ "1.4.3": {
2979
+ "version": "1.4.4",
2980
+ "date": "2026-06-07",
2981
+ "notes": "First full CLI-first field-test gap fixes (config drift + manifest scope): (1) phase-1-collect.md no longer instructs the config-writer to derive paths.commands from the project prefix — it is ALWAYS '.claude/commands/forge' (the prefix-derived instruction survived the 1.4.0 namespace redesign and produced paths.commands='.claude/commands/git' -> check-structure 0/14). (2) generate-tools.md gains an explicit step writing paths.forgeRoot='.forge' — without it the register agent improvised an absolute npm-global payload path, which breaks on version upgrades and nvm switches. (3) build-manifest.cjs excludes 10 plugin-development tools from the tools namespace — the structure manifest describes instances, and expecting dev tools produced false 49/59 'missing tools' gaps in /forge:health.",
2982
+ "target": "init:phases:phase-1-collect,init:generation:generate-tools,tools:build-manifest,schemas:structure-manifest",
2983
+ "regenerate": [],
2984
+ "breaking": false,
2985
+ "manual": [
2986
+ "Existing CLI-first projects with drifted config: run node .forge/tools/manage-config.cjs set paths.commands '\".claude/commands/forge\"' and set paths.forgeRoot '\".forge\"', then re-run '4ge init claude .' to refresh .forge/schemas/structure-manifest.json."
2987
+ ]
2988
+ },
2989
+ "1.4.4": {
2990
+ "version": "1.4.5",
2991
+ "date": "2026-06-09",
2992
+ "notes": "Collate-workflow KB-link refresh is orchestrator-owned under forge-cli. collator_agent.md (base-pack) + meta-collate.md (meta source) no longer instruct the collator subagent to invoke the forge:refresh-kb-links Skill tool: forge-cli subagents run via the Pi runtime, which has no Skill tool, so the instruction triggered a multi-call bash-probe loop before falling back. The Claude Code TUI standalone path still uses the Skill tool; under forge-cli the run-task orchestrator owns the KB refresh (runRefreshKbLinks) after the writeback phase. Prose-only change to the generated collate workflow.",
2993
+ "target": "workflows:collator_agent,meta:meta-collate",
2994
+ "regenerate": [
2995
+ "workflows:collator_agent"
2996
+ ],
2997
+ "breaking": false,
2998
+ "manual": [
2999
+ "Run /forge:update (or /forge:rebuild) to regenerate the collate workflow (collator_agent.md) with the orchestrator-owned KB-refresh guidance."
3000
+ ]
2860
3001
  }
2861
3002
  }
@@ -10,7 +10,7 @@ description: Conversational pipeline manager — add, customize, view, or remove
10
10
  ## Setup
11
11
 
12
12
  ```
13
- FORGE_ROOT: !`echo "${CLAUDE_PLUGIN_ROOT}"`
13
+ FORGE_ROOT: !`echo "${CLAUDE_PLUGIN_ROOT:-$(pwd)/.forge}"`
14
14
  ```
15
15
 
16
16
  Read project config values:
@@ -10,7 +10,7 @@ Add a task to an existing sprint without re-running the full sprint planner.
10
10
  ## Setup
11
11
 
12
12
  ```
13
- FORGE_ROOT: !`echo "${CLAUDE_PLUGIN_ROOT}"`
13
+ FORGE_ROOT: !`echo "${CLAUDE_PLUGIN_ROOT:-$(pwd)/.forge}"`
14
14
  ```
15
15
 
16
16
  Read project config values:
@@ -256,8 +256,8 @@ Display a summary (substitute `{PREFIX}` with `PROJECT_PREFIX.toLowerCase()`):
256
256
  Pipeline: {PIPELINE}
257
257
 
258
258
  ── Next steps:
259
- 1. Run /{PREFIX}:run-task {TASK_ID} to execute the full pipeline.
260
- 2. Or run /{PREFIX}:plan {TASK_ID} to plan it first.
259
+ 1. Run /forge:run-task {TASK_ID} to execute the full pipeline.
260
+ 2. Or run /forge:plan {TASK_ID} to plan it first.
261
261
  ```
262
262
 
263
263
  ---
@@ -10,7 +10,7 @@ Single conversational entry point for all Forge intent.
10
10
  ## Locate plugin root
11
11
 
12
12
  ```
13
- FORGE_ROOT: !`echo "${CLAUDE_PLUGIN_ROOT}"`
13
+ FORGE_ROOT: !`echo "${CLAUDE_PLUGIN_ROOT:-$(pwd)/.forge}"`
14
14
  ```
15
15
 
16
16
  ## Open with oracle banner
@@ -12,7 +12,7 @@ release engineering, significant refactors).
12
12
  ## Locate plugin root
13
13
 
14
14
  ```
15
- FORGE_ROOT: !`echo "${CLAUDE_PLUGIN_ROOT}"`
15
+ FORGE_ROOT: !`echo "${CLAUDE_PLUGIN_ROOT:-$(pwd)/.forge}"`
16
16
  ```
17
17
 
18
18
  ## Arguments
@@ -11,7 +11,7 @@ commands (e.g. `paths.forgeRoot` is refreshed by `/forge:update`).
11
11
  ## Locate the Forge plugin
12
12
 
13
13
  ```
14
- FORGE_ROOT: !`echo "${CLAUDE_PLUGIN_ROOT}"`
14
+ FORGE_ROOT: !`echo "${CLAUDE_PLUGIN_ROOT:-$(pwd)/.forge}"`
15
15
  ```
16
16
 
17
17
  Read `.forge/config.json`. If it does not exist, stop and emit:
@@ -32,7 +32,7 @@ Assess the health and currency of the project's SDLC knowledge base.
32
32
 
33
33
  First, resolve the plugin root and project root:
34
34
  ```
35
- FORGE_ROOT: !`echo "${CLAUDE_PLUGIN_ROOT}"`
35
+ FORGE_ROOT: !`echo "${CLAUDE_PLUGIN_ROOT:-$(pwd)/.forge}"`
36
36
  ```
37
37
 
38
38
  Open the run with the oracle hero + subtitle:
@@ -13,7 +13,7 @@ AI software development lifecycle for the codebase in the current working direct
13
13
  Set `$FORGE_ROOT` to the plugin root provided by Claude Code:
14
14
 
15
15
  ```
16
- FORGE_ROOT: !`echo "${CLAUDE_PLUGIN_ROOT}"`
16
+ FORGE_ROOT: !`echo "${CLAUDE_PLUGIN_ROOT:-$(pwd)/.forge}"`
17
17
  ```
18
18
 
19
19
  `$FORGE_ROOT` is the directory containing `meta/`, `init/`, `hooks/`, and `commands/`.
@@ -136,13 +136,68 @@ Any other input (including 0, 5+, or non-numeric text) re-prompts with the same
136
136
  If a `$ARGUMENTS` phase number is provided (e.g. `3`), skip the pre-flight
137
137
  table and go straight to the specified phase.
138
138
 
139
- Read `$FORGE_ROOT/init/sdlc-init.md` that document is your complete orchestration.
140
- Follow it exactly. It defines 4 phases:
139
+ Before dispatching, gather the following values interactively:
141
140
 
142
- 1. **Collect** — 5 parallel discovery prompts, KB folder prompt, `config.json`
143
- 2. **Discover** — KB doc fan-out + inline `project-context.json` construction
144
- 3. **Materialize** — `substitute-placeholders.cjs` + `build-overlay.cjs` smoke test
145
- 4. **Register** tools, versioning, manifest, cache, store seed, Tomoshibi, `.gitignore` update (unconditional), CLAUDE.md KB-link offer
141
+ **KB Folder Prompt:**
142
+
143
+ ```
144
+ What should your engineering knowledge base folder be named?
145
+ Default: engineering
146
+
147
+ KB folder name [engineering]: ___
148
+ ```
149
+
150
+ If the user provides a custom name, write it:
151
+ ```sh
152
+ node "$FORGE_ROOT/tools/manage-config.cjs" set paths.engineering "{name}"
153
+ ```
154
+ Set `kbFolder` to the chosen name (default: `"engineering"`).
155
+
156
+ **CLAUDE.md Offer:**
157
+
158
+ ```sh
159
+ ls CLAUDE.md AGENTS.md CLAUDE.local.md .cursorrules 2>/dev/null
160
+ ```
161
+
162
+ If NONE of those files exist, ask:
163
+ ```
164
+ No CLAUDE.md / AGENTS.md found. Create a minimal CLAUDE.md with Forge KB links? [Y/n]: ___
165
+ ```
166
+ Set `createClaudeMd = true` (default Y) or `false`.
167
+ If any file exists, set `createClaudeMd = false` (skip silently).
168
+
169
+ **Timestamp:** `isoTimestamp = new Date().toISOString()`
170
+
171
+ Execute the workflow:
172
+
173
+ ```
174
+ workflow('wfl:init', {
175
+ forgeRoot: FORGE_ROOT,
176
+ kbFolder,
177
+ startPhase,
178
+ createClaudeMd,
179
+ isoTimestamp,
180
+ rawArguments: $ARGUMENTS
181
+ })
182
+ ```
183
+
184
+ If the Workflow tool is unavailable, halt immediately with the following message:
185
+
186
+ > The Workflow tool is required to run `/forge:init`. This Claude Code build does not
187
+ > support the Workflow tool. Upgrade Claude Code and try again.
188
+ >
189
+ > (Alternatively, run `4ge init claude .` again to re-scaffold, then upgrade Claude Code.)
190
+
191
+ Do NOT fall back to reading `sdlc-init.md` or any other document — Iron Law 5.
192
+
193
+ **Post-workflow:** on `result.ok === true`, render closing banners (use `banners.cjs forge`
194
+ and `--subtitle "灯 SDLC ready…"`), emit the welcome block, present the marketplace-skills
195
+ offer from `result.skillMatches` (install accepted skills via `manage-config.cjs set
196
+ installedSkills.{id} true`), invoke `forge:refresh-kb-links` via the Skill tool, and
197
+ print the final report (KB doc count, workflow count, command count, accepted/skipped skills).
198
+
199
+ On `result.ok === false`: surface `result.failure` as formatted JSON and offer
200
+ `/forge:report-bug`.
146
201
 
147
202
  The current working directory is the target project. All generated artifacts go into
148
203
  `.forge/`, the configured KB folder (default: `engineering/`), and `.claude/commands/`
@@ -10,7 +10,7 @@ Re-run generation phases using the current state of the project. Use `--enrich`
10
10
  ## Locate the Forge plugin
11
11
 
12
12
  ```
13
- FORGE_ROOT: !`echo "${CLAUDE_PLUGIN_ROOT}"`
13
+ FORGE_ROOT: !`echo "${CLAUDE_PLUGIN_ROOT:-$(pwd)/.forge}"`
14
14
  ```
15
15
 
16
16
  Read `.forge/config.json`. If it does not exist, stop and tell the user to run
@@ -18,7 +18,7 @@ Read `.forge/config.json`. If it does not exist, stop and tell the user to run
18
18
 
19
19
  Resolve tools from the plugin:
20
20
  ```
21
- FORGE_ROOT: !`echo "${CLAUDE_PLUGIN_ROOT}"`
21
+ FORGE_ROOT: !`echo "${CLAUDE_PLUGIN_ROOT:-$(pwd)/.forge}"`
22
22
  ```
23
23
 
24
24
  All tool invocations in this command use `node "$FORGE_ROOT/tools/<tool>.cjs"`.
@@ -709,7 +709,7 @@ When `$ARGUMENTS` contains `--enrich`, run the enhancement workflow instead of r
709
709
  This is the v1.0 replacement for the removed `/forge:enhance` command.
710
710
 
711
711
  ```
712
- FORGE_ROOT: !`echo "${CLAUDE_PLUGIN_ROOT}"`
712
+ FORGE_ROOT: !`echo "${CLAUDE_PLUGIN_ROOT:-$(pwd)/.forge}"`
713
713
  ```
714
714
 
715
715
  1. Check that `$FORGE_ROOT/meta/workflows/meta-enhance.md` exists. If absent:
@@ -21,7 +21,7 @@ nothing is deleted until you confirm explicitly.
21
21
  Read the configured KB path and project prefix from config:
22
22
 
23
23
  ```sh
24
- FORGE_ROOT: !`echo "${CLAUDE_PLUGIN_ROOT}"`
24
+ FORGE_ROOT: !`echo "${CLAUDE_PLUGIN_ROOT:-$(pwd)/.forge}"`
25
25
  KB_PATH: !`node -e "try{console.log(require('./.forge/config.json').paths.engineering)}catch{console.log('engineering')}"`
26
26
  PREFIX: !`node -e "try{console.log(require('./.forge/config.json').project.prefix.toLowerCase())}catch{console.log('')}"`
27
27
  ```
@@ -19,7 +19,7 @@ $ARGUMENTS
19
19
 
20
20
  1. Resolve the plugin root:
21
21
  ```
22
- FORGE_ROOT: !`echo "${CLAUDE_PLUGIN_ROOT}"`
22
+ FORGE_ROOT: !`echo "${CLAUDE_PLUGIN_ROOT:-$(pwd)/.forge}"`
23
23
  ```
24
24
 
25
25
  2. Run the four-phase repair workflow below. If `--dry-run` is in `$ARGUMENTS`,
@@ -30,7 +30,7 @@ gh is not installed or not authenticated.
30
30
  Collect the following in parallel:
31
31
 
32
32
  ```
33
- forge_version: !`cat "${CLAUDE_PLUGIN_ROOT}/.claude-plugin/plugin.json" 2>/dev/null | grep '"version"' | head -1 | sed 's/.*: *"\(.*\)".*/\1/'`
33
+ forge_version: !`cat "${CLAUDE_PLUGIN_ROOT:-$(pwd)/.forge}/.claude-plugin/plugin.json" 2>/dev/null | grep '"version"' | head -1 | sed 's/.*: *"\(.*\)".*/\1/'`
34
34
  node_version: !`node --version 2>/dev/null || echo "N/A"`
35
35
  os_info: !`uname -srm 2>/dev/null || echo "N/A"`
36
36
  forge_config: !`cat ".forge/config.json" 2>/dev/null | head -30 || echo "No .forge/config.json found"`
@@ -12,7 +12,7 @@ Quick overview of the current sprint and task state.
12
12
  ## Locate plugin root
13
13
 
14
14
  ```
15
- FORGE_ROOT: !`echo "${CLAUDE_PLUGIN_ROOT}"`
15
+ FORGE_ROOT: !`echo "${CLAUDE_PLUGIN_ROOT:-$(pwd)/.forge}"`
16
16
  ```
17
17
 
18
18
  ## Locate project config
@@ -12,7 +12,7 @@ project's generated artifacts.
12
12
  ## Locate plugin root
13
13
 
14
14
  ```
15
- FORGE_ROOT: !`echo "${CLAUDE_PLUGIN_ROOT}"`
15
+ FORGE_ROOT: !`echo "${CLAUDE_PLUGIN_ROOT:-$(pwd)/.forge}"`
16
16
  ```
17
17
 
18
18
  Detect install mode:
@@ -442,7 +442,7 @@ install changes the cache path (e.g. `…/cache/forge/forge/0.9.6/` →
442
442
  this command is stale.
443
443
 
444
444
  ```sh
445
- FORGE_ROOT: !`echo "${CLAUDE_PLUGIN_ROOT}"`
445
+ FORGE_ROOT: !`echo "${CLAUDE_PLUGIN_ROOT:-$(pwd)/.forge}"`
446
446
  ```
447
447
 
448
448
  If the re-derived `FORGE_ROOT` differs from the original value, print:
@@ -916,7 +916,7 @@ consolidated prompt. **Nothing is written without the user saying yes.**
916
916
  ### 5a — Locate tools
917
917
 
918
918
  ```
919
- FORGE_ROOT: !`echo "${CLAUDE_PLUGIN_ROOT}"`
919
+ FORGE_ROOT: !`echo "${CLAUDE_PLUGIN_ROOT:-$(pwd)/.forge}"`
920
920
  ```
921
921
 
922
922
  All tools are invoked directly from the plugin:
@@ -0,0 +1,228 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * hooks/lib/common.cjs — Shared primitives for Forge hooks
5
+ *
6
+ * Provides four canonical exports used across hook files:
7
+ * - resolveForgePaths() (H-1a) — read .forge/config.json and derive key paths
8
+ * - readStdinJson(cb, stdin) (H-1b) — async stdin → JSON parsing (callback pattern)
9
+ * - formatHookOutput(name, p) (H-1c) — build hookSpecificOutput JSON envelope
10
+ * - FORGE_COMMAND_PATTERNS (H-1d) — canonical forge-command RegExp array
11
+ *
12
+ * IMPORTANT: This file is intentionally excluded from the forge-cli payload
13
+ * (build-payload.cjs copies only hooks/*.js, not hooks/lib/). Therefore:
14
+ * - Only .cjs hooks (post-init.cjs, post-sprint.cjs) may require this file.
15
+ * - .js hooks (triage-error.js, forge-permissions.js) retain inline copies
16
+ * of the patterns they need. Those inline copies carry comments pointing
17
+ * here as the canonical source. Keep both in sync when patterns change.
18
+ *
19
+ * Closes findings: H-1a, H-1b, H-1c, H-1d (FORGE-S25-T08)
20
+ */
21
+
22
+ const fs = require('fs');
23
+ const path = require('path');
24
+ const os = require('os');
25
+
26
+ // ---------------------------------------------------------------------------
27
+ // H-1a: resolveForgePaths
28
+ // ---------------------------------------------------------------------------
29
+
30
+ /**
31
+ * Read .forge/config.json from the current working directory and derive the
32
+ * key filesystem paths used by hooks.
33
+ *
34
+ * Merged from the two identical implementations in post-init.cjs and
35
+ * post-sprint.cjs. The post-init version included structureVersionsPath;
36
+ * this function always returns all fields — callers that don't need a field
37
+ * simply ignore it.
38
+ *
39
+ * @returns {{ forgeDir: string, eventsRoot: string, cacheDir: string,
40
+ * forgeRoot: string|null, structureVersionsPath: string } | null}
41
+ * Returns null if .forge/config.json is missing or unparseable.
42
+ */
43
+ function resolveForgePaths() {
44
+ const cfgPath = path.join(process.cwd(), '.forge', 'config.json');
45
+ if (!fs.existsSync(cfgPath)) return null;
46
+ let cfg;
47
+ try { cfg = JSON.parse(fs.readFileSync(cfgPath, 'utf8')); } catch (_) { return null; }
48
+ const forgeDir = path.dirname(cfgPath);
49
+ return {
50
+ forgeDir,
51
+ eventsRoot: path.join(forgeDir, 'store', 'events'),
52
+ structureVersionsPath: path.join(forgeDir, 'structure-versions.json'),
53
+ cacheDir: path.join(forgeDir, 'cache'),
54
+ forgeRoot: (cfg.paths && cfg.paths.forgeRoot) || null,
55
+ };
56
+ }
57
+
58
+ // ---------------------------------------------------------------------------
59
+ // H-1b: readStdinJson
60
+ // ---------------------------------------------------------------------------
61
+
62
+ /**
63
+ * Read all data from a readable stream (defaulting to process.stdin), parse
64
+ * as JSON, and invoke the callback with the result.
65
+ *
66
+ * Merges the async stdin reading pattern used by triage-error.js and
67
+ * forge-permissions.js. Note: .js hooks retain inline copies of this
68
+ * pattern to avoid a module-scope require on hooks/lib/ (forge-cli bundle
69
+ * gap — see file header). This function is provided for .cjs hook consumers
70
+ * and for future use when the .js extension is migrated in T14.
71
+ *
72
+ * @param {function(object|null): void} callback
73
+ * Called with the parsed object, or null if input is empty or malformed.
74
+ * @param {NodeJS.ReadableStream} [stdin]
75
+ * Readable stream to consume. Defaults to process.stdin.
76
+ */
77
+ function readStdinJson(callback, stdin) {
78
+ const stream = stdin || process.stdin;
79
+ if (stream.setEncoding) stream.setEncoding('utf8');
80
+ let raw = '';
81
+ stream.on('data', chunk => { raw += chunk; });
82
+ stream.on('end', () => {
83
+ if (!raw) { callback(null); return; }
84
+ try {
85
+ callback(JSON.parse(raw));
86
+ } catch (_) {
87
+ callback(null);
88
+ }
89
+ });
90
+ }
91
+
92
+ // ---------------------------------------------------------------------------
93
+ // H-1c: formatHookOutput
94
+ // ---------------------------------------------------------------------------
95
+
96
+ /**
97
+ * Build the Claude Code hook stdout envelope as a JSON string.
98
+ *
99
+ * Claude Code hook protocol: hooks write a JSON object to stdout where the
100
+ * top-level key is `hookSpecificOutput` containing `hookEventName` plus any
101
+ * additional fields from payload.
102
+ *
103
+ * @param {string} hookEventName - e.g. 'PostToolUse', 'SessionStart'
104
+ * @param {object} payload - additional fields to include in hookSpecificOutput
105
+ * @returns {string} JSON string ready for process.stdout.write
106
+ */
107
+ function formatHookOutput(hookEventName, payload) {
108
+ return JSON.stringify({
109
+ hookSpecificOutput: {
110
+ hookEventName,
111
+ ...payload,
112
+ },
113
+ });
114
+ }
115
+
116
+ // ---------------------------------------------------------------------------
117
+ // H-1d: FORGE_COMMAND_PATTERNS
118
+ // ---------------------------------------------------------------------------
119
+
120
+ /**
121
+ * Canonical array of RegExp patterns that identify forge-related commands.
122
+ *
123
+ * @catalogSync forge/schemas/enum-catalog.json#commandNames (FORGE-S25-T26)
124
+ *
125
+ * Build-time drift detection: forge/tools/__tests__/build-enum-catalog.test.cjs
126
+ * verifies that every `forge:*` entry in enum-catalog.json commandNames has at
127
+ * least one matching regex here. Run `node --test forge/tools/__tests__/*.test.cjs`
128
+ * to check. Drift will cause the drift-detection test to fail.
129
+ *
130
+ * Runtime: this array is the single source of truth for forge command recognition.
131
+ * hooks/triage-error.cjs and hooks/forge-permissions.cjs maintain inline copies
132
+ * of subsets; those files cannot require() this module due to the forge-cli bundle
133
+ * gap (build-payload.cjs bundles hooks/*.cjs but excludes hooks/lib/).
134
+ *
135
+ * When adding a new forge command: update this array, the inline copies in
136
+ * triage-error.cjs and forge-permissions.cjs, AND build-enum-catalog.cjs COMMAND_NAMES.
137
+ */
138
+ const FORGE_COMMAND_PATTERNS = [
139
+ /manage-config/,
140
+ /\.forge\//,
141
+ /CLAUDE_PLUGIN_ROOT/,
142
+ /FORGE_ROOT/,
143
+ /MANAGE_CONFIG/,
144
+ /engineering\/tools\//,
145
+ /forge:init/,
146
+ /forge:health/,
147
+ /forge:rebuild/,
148
+ /forge:update/,
149
+ /forge:add-pipeline/,
150
+ /forge:add-task/,
151
+ /forge:plan/,
152
+ /forge:implement/,
153
+ /forge:approve/,
154
+ /forge:commit/,
155
+ /forge:review/,
156
+ /forge:new-sprint/,
157
+ /forge:plan-sprint/,
158
+ /forge:run-task/,
159
+ /forge:run-sprint/,
160
+ /forge:fix-bug/,
161
+ /forge:retro/,
162
+ /forge:check-agent/,
163
+ /forge:report-bug/,
164
+ // forge:enhance removed in v1.0 (T03) — absorbed into forge:rebuild --enrich
165
+ // forge:collate removed from user-facing surface in v1.0 (T03) — internal tool only
166
+ /forge:validate/,
167
+ // forge:calibrate removed in v1.0 (T03) — absorbed into forge:health --fix (T04)
168
+ // forge:materialize removed in v1.0 (T03) — fast-mode eliminated in T01
169
+ /forge:remove/,
170
+ /forge:search/,
171
+ /forge:repair/,
172
+ /forge:store-custodian/,
173
+ /forge:config/,
174
+ /forge:ask/,
175
+ /forge:refresh-kb-links/,
176
+ /store-cli\.cjs/,
177
+ /validate-store\.cjs/,
178
+ ];
179
+
180
+ // ---------------------------------------------------------------------------
181
+ // H-5b: logSwallowedError
182
+ // ---------------------------------------------------------------------------
183
+
184
+ /**
185
+ * Append a diagnostic line to the swallowed-error log at
186
+ * `$dataDir/logs/forge-hooks.log`. If `dataDir` is falsy, falls back to
187
+ * `os.tmpdir()/forge-plugin-data/logs/forge-hooks.log`.
188
+ *
189
+ * Format per line:
190
+ * <ISO-timestamp> [<tag>] <err.message>
191
+ *
192
+ * Invariants:
193
+ * - Append-only. No log rotation. Users can `truncate -s 0` or `rm` the
194
+ * file; it will be recreated on the next swallowed error.
195
+ * - Hook code NEVER reads this log.
196
+ * - Fully fail-open: if the log write itself fails, we exit silently.
197
+ *
198
+ * Closes finding: H-5b (FORGE-S25-T15)
199
+ *
200
+ * @param {string} tag - Short hook identifier (e.g. 'post-init', 'post-sprint').
201
+ * @param {Error|*} err - The caught error. Uses err.message if available.
202
+ * @param {string|null|undefined} dataDir - CLAUDE_PLUGIN_DATA directory.
203
+ */
204
+ function logSwallowedError(tag, err, dataDir) {
205
+ try {
206
+ const baseDir = dataDir || path.join(os.tmpdir(), 'forge-plugin-data');
207
+ const logsDir = path.join(baseDir, 'logs');
208
+ fs.mkdirSync(logsDir, { recursive: true });
209
+ const logPath = path.join(logsDir, 'forge-hooks.log');
210
+ const msg = (err && err.message) ? err.message : String(err);
211
+ const line = `${new Date().toISOString()} [${tag}] ${msg}\n`;
212
+ fs.appendFileSync(logPath, line, 'utf8');
213
+ } catch (_) {
214
+ // Fully fail-open — never re-throw
215
+ }
216
+ }
217
+
218
+ // ---------------------------------------------------------------------------
219
+ // Exports
220
+ // ---------------------------------------------------------------------------
221
+
222
+ module.exports = {
223
+ resolveForgePaths,
224
+ readStdinJson,
225
+ formatHookOutput,
226
+ FORGE_COMMAND_PATTERNS,
227
+ logSwallowedError,
228
+ };