forge-workflow 0.0.9 → 0.1.0-beta.2

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 (479) hide show
  1. package/.claude/rules/{greptile-review-process.md → review-process.md} +56 -41
  2. package/.claude/scripts/{greptile-resolve.sh → review-resolve.sh} +13 -3
  3. package/.cursor/rules/permissions-guidance.mdc +2 -2
  4. package/.forge/hooks/check-tdd.js +3 -0
  5. package/.forge/hooks/forge-native-hook.js +245 -0
  6. package/.forge/protected-paths.yaml +157 -0
  7. package/AGENTS.md +151 -61
  8. package/CHANGELOG.md +681 -0
  9. package/CLAUDE.md +9 -106
  10. package/QUICKSTART.md +171 -0
  11. package/README.md +271 -363
  12. package/bin/forge-cmd.js +120 -9
  13. package/bin/forge-preflight.js +26 -5
  14. package/bin/forge.js +466 -489
  15. package/docs/INDEX.md +93 -0
  16. package/docs/PROJECT_DESIGN.md +685 -0
  17. package/docs/architecture/index.md +66 -0
  18. package/docs/architecture/notes/README.md +35 -0
  19. package/docs/architecture/subsystems/README.md +46 -0
  20. package/docs/{TOOLCHAIN.md → forge/TOOLCHAIN.md} +56 -47
  21. package/docs/forge/VALIDATION.md +82 -0
  22. package/docs/{AGENT_INSTALL_PROMPT.md → guides/AGENT_INSTALL_PROMPT.md} +3 -3
  23. package/docs/guides/BEADS_GITHUB_SYNC.md +32 -0
  24. package/docs/{ENHANCED_ONBOARDING.md → guides/ENHANCED_ONBOARDING.md} +16 -12
  25. package/docs/guides/GREPTILE_SETUP.md +46 -0
  26. package/docs/guides/MANUAL_REVIEW_GUIDE.md +58 -0
  27. package/docs/guides/MIGRATION.md +56 -0
  28. package/docs/guides/SETUP.md +118 -0
  29. package/docs/guides/SUPPORT.md +185 -0
  30. package/docs/guides/WORKFLOW_TEMPLATES.md +74 -0
  31. package/docs/guides/memory-backends.md +183 -0
  32. package/docs/reference/ADAPTERS.md +128 -0
  33. package/docs/reference/AGENT_SKILL_PARITY.md +175 -0
  34. package/docs/reference/COMMANDS.md +205 -0
  35. package/docs/reference/DECISION_DRIFT_GUARDS.md +97 -0
  36. package/docs/{EXAMPLES.md → reference/EXAMPLES.md} +7 -5
  37. package/docs/reference/FORGE_KERNEL_STORAGE_MODEL.md +135 -0
  38. package/docs/reference/HERMES_INTEGRATION.md +118 -0
  39. package/docs/reference/INSIGHTS_RECAP.md +63 -0
  40. package/docs/reference/INSTALL.md +164 -0
  41. package/docs/reference/KERNEL_TAXONOMY_VALIDATION.md +161 -0
  42. package/docs/reference/PROTECTED_PATH_MANIFEST.md +25 -0
  43. package/docs/reference/RELEASE.md +68 -0
  44. package/docs/reference/RESEARCH_TEMPLATE.md +292 -0
  45. package/docs/{ROADMAP.md → reference/ROADMAP.md} +12 -9
  46. package/docs/reference/SKILLS.md +35 -0
  47. package/docs/reference/STATUS_BOARD.md +80 -0
  48. package/docs/reference/TEMPLATES.md +106 -0
  49. package/docs/reference/TOOLCHAIN.md +658 -0
  50. package/docs/reference/VALIDATION.md +82 -0
  51. package/docs/reference/agent-permissions.md +169 -0
  52. package/docs/reference/beads-to-kernel-migration-ux.md +61 -0
  53. package/docs/reference/control-plane-guarantees.md +125 -0
  54. package/docs/reference/dependency-chain.md +331 -0
  55. package/docs/reference/forge-kernel-issue-command-contract.md +161 -0
  56. package/docs/reference/forge-kernel-schema.md +72 -0
  57. package/docs/reference/kernel-conflict-evaluators.md +27 -0
  58. package/docs/reference/patch-md-format.md +77 -0
  59. package/docs/reference/protected-state-surfaces.md +59 -0
  60. package/docs/reference/shepherd.md +115 -0
  61. package/docs/reference/superpowers-analysis.md +320 -0
  62. package/docs/reference/superpowers-integration-options.md +404 -0
  63. package/docs/reference/test-environment.md +519 -0
  64. package/docs/reference/upgrade-safety.md +59 -0
  65. package/lefthook.yml +18 -0
  66. package/lib/adapter-cli.js +307 -0
  67. package/lib/adapters/beads-issue-adapter.js +127 -0
  68. package/lib/adapters/beads-kernel-compat.js +1042 -0
  69. package/lib/adapters/greptile-review-adapter.js +141 -0
  70. package/lib/adapters/kernel-issue-adapter.js +101 -0
  71. package/lib/adapters/pr-state-adapter.js +484 -0
  72. package/lib/adoption-profiles.js +126 -0
  73. package/lib/agents/README.md +2 -6
  74. package/lib/agents/claude.plugin.json +3 -8
  75. package/lib/agents/codex.plugin.json +9 -1
  76. package/lib/agents/cursor.plugin.json +2 -6
  77. package/lib/agents/hermes.plugin.json +22 -0
  78. package/lib/agents-config.js +39 -1236
  79. package/lib/audit-evidence.js +282 -0
  80. package/lib/beads-setup.js +225 -28
  81. package/lib/beads-sync-scaffold.js +36 -107
  82. package/lib/codex-skills.js +51 -1
  83. package/lib/commands/_issue.js +744 -70
  84. package/lib/commands/_manifest.js +91 -0
  85. package/lib/commands/_registry.js +85 -34
  86. package/lib/commands/_resolve-command-opts.js +261 -0
  87. package/lib/commands/_serve-security.js +270 -0
  88. package/lib/commands/adapter.js +12 -0
  89. package/lib/commands/add.js +118 -0
  90. package/lib/commands/audit.js +70 -0
  91. package/lib/commands/blocked.js +5 -0
  92. package/lib/commands/board.js +64 -0
  93. package/lib/commands/claim.js +21 -2
  94. package/lib/commands/claims.js +7 -0
  95. package/lib/commands/clean.js +485 -75
  96. package/lib/commands/close.js +2 -2
  97. package/lib/commands/comment.js +5 -0
  98. package/lib/commands/control.js +148 -0
  99. package/lib/commands/create.js +2 -2
  100. package/lib/commands/dev.js +185 -7
  101. package/lib/commands/doc-gate.js +336 -0
  102. package/lib/commands/doctor.js +156 -0
  103. package/lib/commands/explain.js +15 -0
  104. package/lib/commands/export.js +237 -0
  105. package/lib/commands/gate.js +192 -0
  106. package/lib/commands/hooks.js +242 -0
  107. package/lib/commands/inbox.js +118 -0
  108. package/lib/commands/init.js +598 -0
  109. package/lib/commands/insights.js +79 -0
  110. package/lib/commands/issue.js +12 -1
  111. package/lib/commands/issues.js +66 -0
  112. package/lib/commands/lint.js +5 -0
  113. package/lib/commands/list.js +2 -2
  114. package/lib/commands/merge.js +312 -0
  115. package/lib/commands/migrate.js +523 -0
  116. package/lib/commands/new.js +12 -0
  117. package/lib/commands/options.js +241 -0
  118. package/lib/commands/orient.js +13 -0
  119. package/lib/commands/orphans.js +5 -0
  120. package/lib/commands/patch.js +67 -0
  121. package/lib/commands/plan.js +436 -24
  122. package/lib/commands/preflight.js +211 -0
  123. package/lib/commands/prime.js +13 -0
  124. package/lib/commands/push.js +69 -2
  125. package/lib/commands/ready.js +2 -2
  126. package/lib/commands/recall.js +116 -0
  127. package/lib/commands/recap.js +61 -0
  128. package/lib/commands/recommend.js +22 -2
  129. package/lib/commands/release.js +91 -0
  130. package/lib/commands/remember.js +74 -0
  131. package/lib/commands/role.js +99 -0
  132. package/lib/commands/serve.js +581 -0
  133. package/lib/commands/setup.js +851 -979
  134. package/lib/commands/shepherd.js +436 -0
  135. package/lib/commands/ship.js +23 -1
  136. package/lib/commands/show.js +2 -2
  137. package/lib/commands/stage.js +192 -0
  138. package/lib/commands/stale.js +5 -0
  139. package/lib/commands/status.js +329 -11
  140. package/lib/commands/sync.js +34 -46
  141. package/lib/commands/team.js +15 -2
  142. package/lib/commands/test.js +58 -7
  143. package/lib/commands/update.js +2 -2
  144. package/lib/commands/upgrade.js +47 -0
  145. package/lib/commands/validate.js +56 -25
  146. package/lib/commands/worktree.js +308 -128
  147. package/lib/config-writer.js +202 -0
  148. package/lib/control-plane.js +236 -0
  149. package/lib/core/runtime-graph.js +946 -0
  150. package/lib/dep-guard/keyword-ripple.js +184 -0
  151. package/lib/deprecated-sync-cleanup.js +362 -0
  152. package/lib/detect-agent.js +2 -28
  153. package/lib/detect-worktree.js +42 -17
  154. package/lib/doc-gate/declaration.js +177 -0
  155. package/lib/doc-gate/detect.js +289 -0
  156. package/lib/doc-gate/gate.js +375 -0
  157. package/lib/doc-gate/okf-config.js +128 -0
  158. package/lib/doc-gate/okf.js +429 -0
  159. package/lib/docs-command.js +1161 -6
  160. package/lib/forge-issues.js +697 -0
  161. package/lib/forge-lock.js +262 -0
  162. package/lib/gate-events.js +193 -0
  163. package/lib/global-flags.js +74 -0
  164. package/lib/greptile-match.js +7 -63
  165. package/lib/harness-capability-matrix.js +380 -0
  166. package/lib/hook-global-installer.js +347 -0
  167. package/lib/hook-renderer.js +451 -0
  168. package/lib/inbox.js +391 -0
  169. package/lib/insights.js +397 -0
  170. package/lib/issue-adapter.js +156 -0
  171. package/lib/issue-backend.js +145 -0
  172. package/lib/issue-render.js +220 -0
  173. package/lib/issue-sync/authority.js +100 -0
  174. package/lib/issue-sync/github-pull.js +184 -0
  175. package/lib/issue-sync/import-primitives.js +98 -0
  176. package/lib/issue-sync/legacy-link-bridge.js +436 -0
  177. package/lib/issue-sync/link-store.js +292 -0
  178. package/lib/issue-sync/project-github.js +123 -0
  179. package/lib/issue-sync/reconcile.js +195 -0
  180. package/lib/issue-sync/schema.js +126 -0
  181. package/lib/kernel/backing-issue.js +305 -0
  182. package/lib/kernel/broker.js +1218 -0
  183. package/lib/kernel/cli-broker-factory.js +130 -0
  184. package/lib/kernel/conflict-signal.js +82 -0
  185. package/lib/kernel/evaluators.js +195 -0
  186. package/lib/kernel/fs-class.js +495 -0
  187. package/lib/kernel/issue-command-contract.js +559 -0
  188. package/lib/kernel/issue-id-resolver.js +186 -0
  189. package/lib/kernel/lease-enforcer.js +158 -0
  190. package/lib/kernel/migrations.js +333 -0
  191. package/lib/kernel/planning-buckets-schema.js +109 -0
  192. package/lib/kernel/projection-jsonl-writer.js +450 -0
  193. package/lib/kernel/readiness-model.js +329 -0
  194. package/lib/kernel/schema.js +356 -0
  195. package/lib/kernel/sqlite-driver.js +2504 -0
  196. package/lib/kernel/taxonomy-validator.js +394 -0
  197. package/lib/lefthook-check.js +8 -4
  198. package/lib/lefthook-wiring.js +413 -0
  199. package/lib/mcp-config-renderer.js +288 -0
  200. package/lib/memory/graphiti-mcp.js +106 -0
  201. package/lib/memory/router.js +387 -0
  202. package/lib/memory/typed-api.js +102 -0
  203. package/lib/memory-digest.js +195 -0
  204. package/lib/merge-rules.js +395 -0
  205. package/lib/migrate-dry-run.js +466 -0
  206. package/lib/orientation.js +863 -0
  207. package/lib/package-manager-remediation.js +103 -0
  208. package/lib/package-root.js +381 -0
  209. package/lib/patch-intent.js +890 -0
  210. package/lib/plugin-catalog.js +3 -4
  211. package/lib/plugin-manager.js +0 -5
  212. package/lib/pr-bundle.js +186 -0
  213. package/lib/pr-monitor/differ.js +195 -0
  214. package/lib/pr-monitor/events.js +0 -0
  215. package/lib/pr-monitor/gather.js +124 -0
  216. package/lib/pr-monitor/journal.js +299 -0
  217. package/lib/pr-monitor/monitor.js +146 -0
  218. package/lib/pr-monitor/render-sticky.js +157 -0
  219. package/lib/pr-monitor/watch-lifecycle.js +95 -0
  220. package/lib/pr-monitor/watch.js +247 -0
  221. package/lib/pr-pull.js +1273 -0
  222. package/lib/pr-shepherd.js +494 -0
  223. package/lib/pr-state-validator.js +59 -0
  224. package/lib/preflight/gates.js +237 -0
  225. package/lib/preflight/runner.js +116 -0
  226. package/lib/project-discovery.js +0 -53
  227. package/lib/project-memory.js +166 -0
  228. package/lib/protected-path-manifest.js +281 -0
  229. package/lib/protected-state-surfaces.js +387 -0
  230. package/lib/release-readiness.js +2089 -0
  231. package/lib/reset.js +59 -45
  232. package/lib/review-adapter.js +68 -0
  233. package/lib/rules-sync.js +260 -0
  234. package/lib/runtime-health.js +332 -23
  235. package/lib/safety-config-renderer.js +268 -0
  236. package/lib/setup-action-log.js +1 -7
  237. package/lib/setup.js +27 -65
  238. package/lib/shell-utils.js +76 -6
  239. package/lib/skills-sync.js +330 -0
  240. package/lib/smart-status/conflicts.js +205 -0
  241. package/lib/smart-status/scoring.js +191 -0
  242. package/lib/status/beads-snapshot.js +145 -0
  243. package/lib/status/presenter.js +216 -0
  244. package/lib/status/snapshot.js +186 -0
  245. package/lib/sync-backend.js +202 -0
  246. package/lib/untrusted-content.js +52 -0
  247. package/lib/upgrade-safety.js +199 -0
  248. package/lib/workflow/enforce-stage.js +298 -47
  249. package/lib/workflow/stage-transition.js +115 -0
  250. package/lib/workflow/stages.js +30 -6
  251. package/lib/workflow/state-manager.js +159 -14
  252. package/lib/workflow/state.js +23 -1
  253. package/lib/workflow-profiles.js +17 -5
  254. package/package.json +46 -36
  255. package/rules/documentation.md +19 -0
  256. package/rules/kernel-tracking.md +26 -0
  257. package/rules/security.md +22 -0
  258. package/rules/tdd.md +20 -0
  259. package/rules/workflow.md +27 -0
  260. package/scripts/auto-backing-issue.js +47 -0
  261. package/scripts/beads-context.sh +165 -22
  262. package/scripts/beads-migrate-to-dolt.sh +7 -0
  263. package/scripts/beads-upgrade-smoke.sh +284 -0
  264. package/scripts/behavioral-judge.sh +115 -11
  265. package/scripts/benchmark.js +349 -63
  266. package/scripts/bootstrap-windows-tools.sh +78 -0
  267. package/scripts/branch-protection.js +2 -3
  268. package/scripts/check-agents.js +34 -137
  269. package/scripts/commitlint.js +3 -1
  270. package/scripts/conflict-detect.sh +3 -0
  271. package/scripts/dep-guard-analyze.js +52 -17
  272. package/scripts/dep-guard-keyword-ripple.js +29 -0
  273. package/scripts/dep-guard-render-review.js +86 -0
  274. package/scripts/dep-guard.sh +64 -232
  275. package/scripts/file-index.sh +3 -0
  276. package/scripts/forge-team/lib/claim.sh +34 -18
  277. package/scripts/forge-team/lib/dashboard.sh +61 -86
  278. package/scripts/forge-team/lib/epic.sh +99 -263
  279. package/scripts/forge-team/lib/hooks.sh +26 -28
  280. package/scripts/forge-team/lib/identity.sh +4 -4
  281. package/scripts/forge-team/lib/sync-github.sh +144 -47
  282. package/scripts/forge-team/lib/verify.sh +93 -83
  283. package/scripts/forge-team/lib/workload.sh +41 -65
  284. package/scripts/forge-team/tests/claim.test.sh +25 -19
  285. package/scripts/forge-team/tests/dashboard.test.sh +31 -46
  286. package/scripts/forge-team/tests/epic.test.sh +52 -71
  287. package/scripts/forge-team/tests/hooks.test.sh +38 -50
  288. package/scripts/forge-team/tests/identity.test.sh +3 -3
  289. package/scripts/forge-team/tests/integration.test.sh +44 -66
  290. package/scripts/forge-team/tests/sync-github.test.sh +183 -79
  291. package/scripts/forge-team/tests/verify.test.sh +37 -46
  292. package/scripts/forge-team/tests/workflow-integration.test.sh +4 -4
  293. package/scripts/forge-team/tests/workload.test.sh +32 -66
  294. package/scripts/gen-command-manifest.js +153 -0
  295. package/scripts/gen-embedded-assets.mjs +129 -0
  296. package/scripts/install.ps1 +139 -0
  297. package/scripts/install.sh +268 -0
  298. package/scripts/lib/beads-migrate-to-dolt.mjs +503 -0
  299. package/scripts/lib/release-asset.mjs +84 -0
  300. package/scripts/parity-check.mjs +145 -0
  301. package/scripts/parity-check.test.mjs +58 -0
  302. package/scripts/pin-agentic-workflow-images.js +112 -0
  303. package/scripts/pr-coordinator.sh +3 -0
  304. package/scripts/preflight-sonar.eslint.config.mjs +44 -0
  305. package/scripts/preflight.sh +108 -0
  306. package/scripts/protected-state-check.js +104 -0
  307. package/scripts/smart-status-score.js +31 -0
  308. package/scripts/smart-status-sessions.js +51 -0
  309. package/scripts/smart-status.sh +117 -369
  310. package/scripts/spikes/config-race-bench.js +111 -0
  311. package/scripts/spikes/harness-capability-matrix.js +13 -0
  312. package/scripts/spikes/patch-anchor-stability-bench.js +125 -0
  313. package/scripts/spikes/protected-path-manifest.js +20 -0
  314. package/scripts/spikes/skill-auto-invoke-parity.js +292 -0
  315. package/scripts/sync-agent-skills.js +62 -0
  316. package/scripts/sync-agentic-workflow.js +48 -0
  317. package/scripts/sync-utils.sh +3 -0
  318. package/scripts/test-ci-shard.js +251 -0
  319. package/scripts/test-dashboard.js +188 -52
  320. package/scripts/test-full-suite.js +186 -0
  321. package/scripts/test-profile.js +278 -0
  322. package/scripts/test.js +302 -28
  323. package/scripts/validate.js +143 -0
  324. package/scripts/validate.sh +18 -1
  325. package/skills/claim-safety/SKILL.md +102 -0
  326. package/skills/claim-safety/evals/evals.json +46 -0
  327. package/{.github/prompts/dev.prompt.md → skills/dev/SKILL.md} +46 -52
  328. package/skills/dev/evals/evals.json +50 -0
  329. package/skills/hermes-forge/SKILL.md +185 -0
  330. package/skills/hermes-forge/evals/evals.json +46 -0
  331. package/skills/issue-basics/SKILL.md +111 -0
  332. package/skills/issue-basics/evals/evals.json +46 -0
  333. package/skills/kernel/SKILL.md +166 -0
  334. package/skills/kernel/evals/evals.json +50 -0
  335. package/skills/memory/SKILL.md +102 -0
  336. package/skills/parallel-deep-research/SKILL.md +14 -11
  337. package/skills/parallel-deep-research/evals/evals.json +11 -27
  338. package/{.github/prompts/plan.prompt.md → skills/plan/SKILL.md} +134 -159
  339. package/skills/plan/evals/evals.json +42 -0
  340. package/skills/research/SKILL.md +195 -0
  341. package/skills/research/evals/evals.json +42 -0
  342. package/{.github/prompts/review.prompt.md → skills/review/SKILL.md} +98 -62
  343. package/skills/review/evals/evals.json +42 -0
  344. package/skills/rollback/SKILL.md +110 -0
  345. package/skills/rollback/evals/evals.json +46 -0
  346. package/skills/rollback/references/methods.md +204 -0
  347. package/{.cursor/commands/rollback.md → skills/rollback/references/workflow-integration.md} +10 -284
  348. package/skills/shepherd/SKILL.md +66 -0
  349. package/skills/shepherd/evals/evals.json +42 -0
  350. package/skills/ship/SKILL.md +251 -0
  351. package/skills/ship/evals/evals.json +42 -0
  352. package/skills/smith/SKILL.md +142 -0
  353. package/skills/smith/evals/evals.json +46 -0
  354. package/skills/smith/references/autonomy-and-gates.md +94 -0
  355. package/{.github/prompts/sonarcloud.prompt.md → skills/sonarcloud/SKILL.md} +14 -3
  356. package/skills/sonarcloud/evals/evals.json +46 -0
  357. package/skills/sonarcloud-analysis/SKILL.md +18 -13
  358. package/skills/sonarcloud-analysis/evals/evals.json +11 -15
  359. package/skills/status/SKILL.md +102 -0
  360. package/skills/status/evals/evals.json +50 -0
  361. package/skills/triage-ready/SKILL.md +121 -0
  362. package/skills/triage-ready/evals/evals.json +42 -0
  363. package/{.github/prompts/validate.prompt.md → skills/validate/SKILL.md} +52 -29
  364. package/skills/validate/evals/evals.json +42 -0
  365. package/skills/verify/SKILL.md +299 -0
  366. package/skills/verify/evals/evals.json +50 -0
  367. package/.claude/commands/dev.md +0 -345
  368. package/.claude/commands/plan.md +0 -566
  369. package/.claude/commands/premerge.md +0 -186
  370. package/.claude/commands/research.md +0 -42
  371. package/.claude/commands/review.md +0 -451
  372. package/.claude/commands/rollback.md +0 -721
  373. package/.claude/commands/ship.md +0 -213
  374. package/.claude/commands/sonarcloud.md +0 -152
  375. package/.claude/commands/status.md +0 -90
  376. package/.claude/commands/validate.md +0 -288
  377. package/.claude/commands/verify.md +0 -269
  378. package/.claude/rules/workflow.md +0 -121
  379. package/.cline/workflows/dev.md +0 -342
  380. package/.cline/workflows/plan.md +0 -563
  381. package/.cline/workflows/premerge.md +0 -183
  382. package/.cline/workflows/research.md +0 -39
  383. package/.cline/workflows/review.md +0 -448
  384. package/.cline/workflows/rollback.md +0 -718
  385. package/.cline/workflows/ship.md +0 -210
  386. package/.cline/workflows/sonarcloud.md +0 -146
  387. package/.cline/workflows/status.md +0 -87
  388. package/.cline/workflows/validate.md +0 -285
  389. package/.cline/workflows/verify.md +0 -266
  390. package/.codex/config.toml +0 -11
  391. package/.codex/skills/dev/SKILL.md +0 -345
  392. package/.codex/skills/plan/SKILL.md +0 -566
  393. package/.codex/skills/premerge/SKILL.md +0 -186
  394. package/.codex/skills/research/SKILL.md +0 -42
  395. package/.codex/skills/review/SKILL.md +0 -451
  396. package/.codex/skills/rollback/SKILL.md +0 -721
  397. package/.codex/skills/ship/SKILL.md +0 -213
  398. package/.codex/skills/sonarcloud/SKILL.md +0 -149
  399. package/.codex/skills/status/SKILL.md +0 -90
  400. package/.codex/skills/validate/SKILL.md +0 -288
  401. package/.codex/skills/verify/SKILL.md +0 -269
  402. package/.cursor/commands/dev.md +0 -342
  403. package/.cursor/commands/plan.md +0 -563
  404. package/.cursor/commands/premerge.md +0 -183
  405. package/.cursor/commands/research.md +0 -39
  406. package/.cursor/commands/review.md +0 -448
  407. package/.cursor/commands/ship.md +0 -210
  408. package/.cursor/commands/sonarcloud.md +0 -146
  409. package/.cursor/commands/status.md +0 -87
  410. package/.cursor/commands/validate.md +0 -285
  411. package/.cursor/commands/verify.md +0 -266
  412. package/.cursorrules +0 -149
  413. package/.github/prompts/premerge.prompt.md +0 -188
  414. package/.github/prompts/research.prompt.md +0 -44
  415. package/.github/prompts/rollback.prompt.md +0 -723
  416. package/.github/prompts/ship.prompt.md +0 -215
  417. package/.github/prompts/status.prompt.md +0 -92
  418. package/.github/prompts/verify.prompt.md +0 -271
  419. package/.github/workflows/beads-to-github.yml +0 -56
  420. package/.github/workflows/github-to-beads.yml +0 -97
  421. package/.kilocode/workflows/dev.md +0 -346
  422. package/.kilocode/workflows/plan.md +0 -567
  423. package/.kilocode/workflows/premerge.md +0 -187
  424. package/.kilocode/workflows/research.md +0 -43
  425. package/.kilocode/workflows/review.md +0 -452
  426. package/.kilocode/workflows/rollback.md +0 -722
  427. package/.kilocode/workflows/ship.md +0 -214
  428. package/.kilocode/workflows/sonarcloud.md +0 -150
  429. package/.kilocode/workflows/status.md +0 -91
  430. package/.kilocode/workflows/validate.md +0 -289
  431. package/.kilocode/workflows/verify.md +0 -270
  432. package/.opencode/commands/dev.md +0 -345
  433. package/.opencode/commands/plan.md +0 -566
  434. package/.opencode/commands/premerge.md +0 -186
  435. package/.opencode/commands/research.md +0 -42
  436. package/.opencode/commands/review.md +0 -451
  437. package/.opencode/commands/rollback.md +0 -721
  438. package/.opencode/commands/ship.md +0 -213
  439. package/.opencode/commands/sonarcloud.md +0 -149
  440. package/.opencode/commands/status.md +0 -90
  441. package/.opencode/commands/validate.md +0 -288
  442. package/.opencode/commands/verify.md +0 -269
  443. package/.roo/commands/dev.md +0 -346
  444. package/.roo/commands/plan.md +0 -567
  445. package/.roo/commands/premerge.md +0 -187
  446. package/.roo/commands/research.md +0 -43
  447. package/.roo/commands/review.md +0 -452
  448. package/.roo/commands/rollback.md +0 -722
  449. package/.roo/commands/ship.md +0 -214
  450. package/.roo/commands/sonarcloud.md +0 -150
  451. package/.roo/commands/status.md +0 -91
  452. package/.roo/commands/validate.md +0 -289
  453. package/.roo/commands/verify.md +0 -270
  454. package/docs/BEADS_GITHUB_SYNC.md +0 -255
  455. package/docs/GREPTILE_SETUP.md +0 -400
  456. package/docs/MANUAL_REVIEW_GUIDE.md +0 -106
  457. package/docs/SETUP.md +0 -663
  458. package/docs/VALIDATION.md +0 -363
  459. package/lib/agents/cline.plugin.json +0 -29
  460. package/lib/agents/copilot.plugin.json +0 -24
  461. package/lib/agents/kilocode.plugin.json +0 -22
  462. package/lib/agents/opencode.plugin.json +0 -23
  463. package/lib/agents/roo.plugin.json +0 -30
  464. package/lib/beads-health-check.js +0 -143
  465. package/lib/commands/commands-reset.js +0 -147
  466. package/opencode.json +0 -67
  467. package/scripts/beads-context.test.js +0 -567
  468. package/scripts/github-beads-sync/comment.mjs +0 -64
  469. package/scripts/github-beads-sync/config.mjs +0 -148
  470. package/scripts/github-beads-sync/github-api.mjs +0 -131
  471. package/scripts/github-beads-sync/index.mjs +0 -332
  472. package/scripts/github-beads-sync/label-mapper.mjs +0 -54
  473. package/scripts/github-beads-sync/mapping.mjs +0 -78
  474. package/scripts/github-beads-sync/reverse-sync-cli.mjs +0 -31
  475. package/scripts/github-beads-sync/reverse-sync.mjs +0 -138
  476. package/scripts/github-beads-sync/run-bd.mjs +0 -161
  477. package/scripts/github-beads-sync/sanitize.mjs +0 -121
  478. package/scripts/github-beads-sync.config.json +0 -26
  479. package/scripts/sync-commands.js +0 -600
@@ -0,0 +1,451 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Per-harness native HOOK config renderer.
5
+ *
6
+ * Projects Forge's lifecycle enforcement — the TDD gate (source changes require
7
+ * tests) and the protected-path guard — onto each harness's REAL native hook
8
+ * surface, using read → merge → write (idempotent, preserves user hooks). This is
9
+ * the hook analogue of lib/mcp-config-renderer.js and closes the honesty gap that
10
+ * #311 recorded: the capability matrix advertised native hooks that no renderer
11
+ * actually wrote.
12
+ *
13
+ * Verified native hook surfaces:
14
+ * - Claude : `.claude/settings.json` → a `hooks` block
15
+ * (PreToolUse matcher groups → { type:'command', command })
16
+ * - Cursor : `.cursor/hooks.json` → { version: 1, hooks: { <event>: [ { command } ] } }
17
+ * (Cursor 1.7+; only `before*` events can DENY — there is NO pre-edit
18
+ * deny event, so protected-path write-blocking + commit gating run on
19
+ * `beforeShellExecution`, and `afterFileEdit` is an observational audit.)
20
+ * - Codex : `.codex/config.toml` → `[hooks]` matcher groups. Codex reads the
21
+ * GLOBAL `~/.codex/config.toml` (per #311 / lib/agents-config.js), so
22
+ * project `forge setup` MUST NOT write it. Rendered + tested here for a
23
+ * global-config follow-up only.
24
+ * - Hermes : `~/.hermes/config.yaml` → a `hooks:` block of shell hooks (matcher +
25
+ * command; JSON-stdin/stdout wire protocol). A `pre_tool_call` hook CAN
26
+ * deny a tool call (it even accepts Claude's {decision:block} shape). Lives
27
+ * in GLOBAL (home) config, so project `forge setup` MUST NOT write it —
28
+ * rendered + tested here for a global-config follow-up only.
29
+ *
30
+ * The rendered `command` invokes Forge's installed native-hook adapter
31
+ * (`.forge/hooks/forge-native-hook.js`), which the setup flow installs alongside
32
+ * `.forge/hooks/check-tdd.js`. The adapter reads the harness's hook stdin, enforces
33
+ * the protected-path set, and delegates the TDD gate to the real `check-tdd.js`.
34
+ *
35
+ * Dependency-free (JSON only; no TOML lib) so it runs under `bun test` and the
36
+ * release gates. Reuses the MCP renderer's `backupFile` for the data-loss guard.
37
+ *
38
+ * @module hook-renderer
39
+ */
40
+
41
+ const fs = require('node:fs');
42
+ const path = require('node:path');
43
+ const { backupFile } = require('./mcp-config-renderer');
44
+
45
+ const HARNESS_HOOK_FILES = {
46
+ claude: '.claude/settings.json',
47
+ cursor: '.cursor/hooks.json',
48
+ codex: '.codex/config.toml',
49
+ hermes: '~/.hermes/config.yaml',
50
+ };
51
+
52
+ // The adapter Forge installs into every project (see lib/commands/setup.js). The
53
+ // stable `forge-native-hook.js` token also MARKS Forge-owned hook entries so a
54
+ // re-merge replaces them in place instead of duplicating (idempotency) and never
55
+ // clobbers a user's own hooks.
56
+ const FORGE_HOOK_ADAPTER_REL = '.forge/hooks/forge-native-hook.js';
57
+ const FORGE_HOOK_ADAPTER = `node ${FORGE_HOOK_ADAPTER_REL}`;
58
+ const FORGE_HOOK_MARKER = 'forge-native-hook.js';
59
+
60
+ // CONTEXT intents route to the `forge` CLI (which loads lib/), NOT the self-contained
61
+ // adapter — memory injection needs kernel/FTS access and must FAIL OPEN. The marker
62
+ // token below stamps Forge-owned context-hook entries so a re-merge replaces them in
63
+ // place (idempotency), the same role FORGE_HOOK_MARKER plays for enforcement entries.
64
+ //
65
+ // The command is a RESOLVED `node <abs bin/forge.js>` invocation — NOT a bare `forge`.
66
+ // A bare `forge` on a hook's minimal PATH either does not resolve (feature silently
67
+ // never fires) or resolves to the WRONG binary (e.g. a global `forge` that misroutes),
68
+ // whose stdout would then be injected as session context. Resolving the exact CLI that
69
+ // rendered the hook (via __dirname, works in the repo and under node_modules) removes
70
+ // both failure modes. FORGE_CONTEXT_MARKER is the stable idempotency token.
71
+ const FORGE_CLI_BIN = path.join(__dirname, '..', 'bin', 'forge.js');
72
+ const FORGE_CLI = `node "${FORGE_CLI_BIN}"`;
73
+ const FORGE_CONTEXT_MARKER = 'hooks session-start';
74
+ // The inbox-pickup context hook (UserPromptSubmit tier). A SECOND context marker so a
75
+ // re-merge recognizes + replaces the Forge-owned UserPromptSubmit entry in place, exactly
76
+ // as FORGE_CONTEXT_MARKER does for the SessionStart entry.
77
+ const FORGE_INBOX_CONTEXT_MARKER = 'hooks inbox-pickup';
78
+
79
+ // Per-harness SessionStart context-injection capability. Honest capability matrix —
80
+ // only Claude exposes a native session-start surface that can inject additionalContext.
81
+ // Cursor's 1.7 hooks are deny-oriented (no session-start context surface); Codex and
82
+ // Hermes hooks live in GLOBAL home config that project setup never writes. We NEVER
83
+ // fake parity: each non-Claude harness carries an explicit, tested skip reason.
84
+ const SESSION_START_SUPPORT = Object.freeze({
85
+ claude: Object.freeze({ rendered: true }),
86
+ cursor: Object.freeze({ rendered: false, reason: 'no-session-start-surface' }),
87
+ codex: Object.freeze({ rendered: false, reason: 'global-config' }),
88
+ hermes: Object.freeze({ rendered: false, reason: 'global-config' }),
89
+ });
90
+
91
+ // Per-harness UserPromptSubmit context-injection capability (the near-real-time inbox
92
+ // tier). Only Claude has a verified UserPromptSubmit surface that injects
93
+ // additionalContext alongside the submitted prompt. Same honesty rule as SessionStart:
94
+ // every non-Claude harness carries an explicit, tested skip reason — no faked parity.
95
+ const USER_PROMPT_SUBMIT_SUPPORT = Object.freeze({
96
+ claude: Object.freeze({ rendered: true }),
97
+ cursor: Object.freeze({ rendered: false, reason: 'no-user-prompt-surface' }),
98
+ codex: Object.freeze({ rendered: false, reason: 'global-config' }),
99
+ hermes: Object.freeze({ rendered: false, reason: 'global-config' }),
100
+ });
101
+
102
+ // Claude exposes $CLAUDE_PROJECT_DIR (absolute project root) to hook commands and
103
+ // documents it as THE cwd-independent way to reference project-local hook scripts —
104
+ // a bare relative path breaks whenever Claude runs the hook from another cwd. Cursor
105
+ // and Codex execute hooks from the workspace root, so a repo-relative path resolves.
106
+ function adapterInvocation(harness) {
107
+ if (harness === 'claude') return `node "$CLAUDE_PROJECT_DIR/${FORGE_HOOK_ADAPTER_REL}"`;
108
+ return FORGE_HOOK_ADAPTER;
109
+ }
110
+
111
+ /**
112
+ * The frozen Forge hook contract: the two enforcement intents projected onto each
113
+ * harness. Order matters — protected-path (per-write) is listed before tdd-gate
114
+ * (per-commit) so the rendered Claude PreToolUse groups read write-guard first.
115
+ */
116
+ const FORGE_HOOK_CONTRACT = Object.freeze({
117
+ schemaVersion: '1.1.0',
118
+ kind: 'forge.hookContract',
119
+ adapter: FORGE_HOOK_ADAPTER,
120
+ intents: Object.freeze([
121
+ Object.freeze({
122
+ id: 'protected-path',
123
+ kind: 'enforcement',
124
+ enforces: 'Protected-path guard: block writes/edits to Forge-protected paths (.forge/, .git/, AGENTS.md, secrets, generated artifacts).',
125
+ lifecycle: 'pre-write',
126
+ command: `${FORGE_HOOK_ADAPTER} --intent protected-path`,
127
+ }),
128
+ Object.freeze({
129
+ id: 'tdd-gate',
130
+ kind: 'enforcement',
131
+ enforces: 'TDD gate: source changes must ship with accompanying tests (blocks bare git commit).',
132
+ lifecycle: 'pre-commit',
133
+ command: `${FORGE_HOOK_ADAPTER} --intent tdd-gate`,
134
+ }),
135
+ Object.freeze({
136
+ id: 'memory-inject',
137
+ kind: 'context',
138
+ cliAction: 'session-start',
139
+ enforces: 'Memory push: inject a bounded, token-capped digest (remembered notes + top open issues) at session start. Additive and FAIL-OPEN — a missing digest never blocks a session.',
140
+ lifecycle: 'session-start',
141
+ command: `${FORGE_CLI} hooks session-start`,
142
+ }),
143
+ Object.freeze({
144
+ id: 'inbox-pickup',
145
+ kind: 'context',
146
+ cliAction: 'inbox-pickup',
147
+ // COMPLIANT COMMENT-BACK: surfaces pending targeted dashboard instruction comments
148
+ // (kernel DATA, fenced) on each prompt. Reads the user's own kernel data via a
149
+ // supported hook — NEVER injects into a running session's stdin, never drives the
150
+ // agent programmatically (Anthropic Usage Policy, kernel issue 6d10c1a1).
151
+ enforces: 'Comment-back pickup: surface a compact count+pointer nudge for pending targeted dashboard instruction comments on each UserPromptSubmit (compact to avoid additionalContext accumulation; full fenced digest is at SessionStart + `forge inbox`). Additive and FAIL-OPEN — a missing nudge never blocks a prompt.',
152
+ lifecycle: 'user-prompt-submit',
153
+ command: `${FORGE_CLI} hooks inbox-pickup`,
154
+ }),
155
+ ]),
156
+ });
157
+
158
+ /** Thrown when an existing hook config cannot be parsed — signals "do not overwrite". */
159
+ class HookConfigParseError extends Error {
160
+ constructor(message, cause) {
161
+ super(message);
162
+ this.name = 'HookConfigParseError';
163
+ this.cause = cause;
164
+ }
165
+ }
166
+
167
+ function intentById(contract, id) {
168
+ const intent = contract.intents.find(i => i.id === id);
169
+ if (!intent) throw new Error(`Forge hook contract is missing intent '${id}'`);
170
+ return intent;
171
+ }
172
+
173
+ function harnessCommand(contract, id, harness) {
174
+ const intent = intentById(contract, id);
175
+ // CONTEXT intents route to the `forge` CLI (fail-open memory injection), NOT the
176
+ // self-contained enforcement adapter. Hooks often run with a minimal PATH; if `forge`
177
+ // does not resolve the command simply fails and the harness ignores it (fail-open).
178
+ if (intent.kind === 'context') {
179
+ return `${FORGE_CLI} hooks ${intent.cliAction} --harness ${harness}`;
180
+ }
181
+ // ENFORCEMENT intents build from the harness-specific adapter invocation (Claude →
182
+ // $CLAUDE_PROJECT_DIR; Cursor/Codex → repo-relative). intent.id is the `--intent` value.
183
+ return `${adapterInvocation(harness)} --intent ${intent.id} --harness ${harness}`;
184
+ }
185
+
186
+ /**
187
+ * Report the per-harness SessionStart context-injection capability (the honest matrix).
188
+ * @param {string} harness
189
+ * @returns {{ rendered: boolean, reason?: string }}
190
+ */
191
+ function sessionStartCapability(harness) {
192
+ return SESSION_START_SUPPORT[harness] || { rendered: false, reason: 'unknown-harness' };
193
+ }
194
+
195
+ /**
196
+ * Report the per-harness UserPromptSubmit context-injection capability (the honest matrix
197
+ * for the near-real-time inbox tier).
198
+ * @param {string} harness
199
+ * @returns {{ rendered: boolean, reason?: string }}
200
+ */
201
+ function userPromptSubmitCapability(harness) {
202
+ return USER_PROMPT_SUBMIT_SUPPORT[harness] || { rendered: false, reason: 'unknown-harness' };
203
+ }
204
+
205
+ /**
206
+ * Render the Claude `.claude/settings.json` `hooks` block (PreToolUse groups only).
207
+ * Write/Edit/MultiEdit/NotebookEdit → protected-path deny; Bash → TDD gate.
208
+ * @param {object} contract
209
+ * @returns {{ PreToolUse: object[] }}
210
+ */
211
+ function renderClaudeHooks(contract) {
212
+ return {
213
+ PreToolUse: [
214
+ {
215
+ matcher: 'Write|Edit|MultiEdit|NotebookEdit',
216
+ hooks: [{ type: 'command', command: harnessCommand(contract, 'protected-path', 'claude') }],
217
+ },
218
+ {
219
+ matcher: 'Bash',
220
+ hooks: [{ type: 'command', command: harnessCommand(contract, 'tdd-gate', 'claude') }],
221
+ },
222
+ ],
223
+ // SessionStart context injection (memory push). No matcher → applies to every
224
+ // session source; the command emits { hookSpecificOutput.additionalContext }.
225
+ SessionStart: [
226
+ { hooks: [{ type: 'command', command: harnessCommand(contract, 'memory-inject', 'claude') }] },
227
+ ],
228
+ // UserPromptSubmit context injection (compliant comment-back — near-real-time tier).
229
+ // Surfaces pending targeted dashboard instruction comments (fenced kernel DATA) on each
230
+ // prompt; the command emits { hookSpecificOutput.additionalContext }. Reads the user's
231
+ // own kernel data via a supported hook — NEVER stdin injection (Anthropic Usage Policy).
232
+ UserPromptSubmit: [
233
+ { hooks: [{ type: 'command', command: harnessCommand(contract, 'inbox-pickup', 'claude') }] },
234
+ ],
235
+ };
236
+ }
237
+
238
+ /**
239
+ * Render the Cursor `.cursor/hooks.json` config. Cursor 1.7+ has NO pre-edit deny
240
+ * event, so write-blocking + commit gating run on `beforeShellExecution` (git /
241
+ * redirects), and `afterFileEdit` carries an observational protected-path audit.
242
+ * @param {object} contract
243
+ * @returns {{ version: number, hooks: object }}
244
+ */
245
+ function renderCursorHooks(contract) {
246
+ return {
247
+ version: 1,
248
+ hooks: {
249
+ beforeShellExecution: [
250
+ { command: harnessCommand(contract, 'tdd-gate', 'cursor') },
251
+ { command: harnessCommand(contract, 'protected-path', 'cursor') },
252
+ ],
253
+ afterFileEdit: [
254
+ { command: harnessCommand(contract, 'protected-path', 'cursor') },
255
+ ],
256
+ },
257
+ };
258
+ }
259
+
260
+ /**
261
+ * Render a Codex `[hooks]` TOML block (mirrors the Claude matcher-group model).
262
+ * Returned for a GLOBAL-config follow-up ONLY — never written by project setup.
263
+ * @param {object} contract
264
+ * @returns {string}
265
+ */
266
+ function renderCodexHooksToml(contract) {
267
+ const groups = [
268
+ { matcher: 'Write|Edit', command: harnessCommand(contract, 'protected-path', 'codex') },
269
+ { matcher: 'Bash', command: harnessCommand(contract, 'tdd-gate', 'codex') },
270
+ ];
271
+ const blocks = groups.map(g =>
272
+ `[[hooks.PreToolUse]]\nmatcher = ${tomlString(g.matcher)}\n\n`
273
+ + `[[hooks.PreToolUse.hooks]]\ntype = "command"\ncommand = ${tomlString(g.command)}\n`,
274
+ );
275
+ return blocks.join('\n');
276
+ }
277
+
278
+ function tomlString(value) {
279
+ return '"' + String(value).replace(/\\/g, '\\\\').replace(/"/g, '\\"') + '"';
280
+ }
281
+
282
+ /**
283
+ * Render a Hermes shell-hooks YAML block (the `hooks:` section of ~/.hermes/config.yaml).
284
+ * Hermes runs shell hooks as subprocesses over a JSON-stdin/stdout wire protocol; a
285
+ * `pre_tool_call` hook can DENY a tool call. Matchers are regexes over the Hermes
286
+ * tool_name (write_file/patch = edits → protected-path; terminal = shell → tdd-gate).
287
+ * Returned for a GLOBAL-config follow-up ONLY — Hermes reads ~/.hermes/config.yaml
288
+ * (home dir), so project setup never writes it (mirrors renderCodexHooksToml).
289
+ * @param {object} contract
290
+ * @returns {string}
291
+ */
292
+ function renderHermesHooksYaml(contract) {
293
+ const groups = [
294
+ { matcher: 'write_file|patch', command: harnessCommand(contract, 'protected-path', 'hermes') },
295
+ { matcher: 'terminal', command: harnessCommand(contract, 'tdd-gate', 'hermes') },
296
+ ];
297
+ const lines = ['hooks:', ' pre_tool_call:'];
298
+ for (const g of groups) {
299
+ lines.push(` - matcher: ${yamlString(g.matcher)}`);
300
+ lines.push(` command: ${yamlString(g.command)}`);
301
+ }
302
+ return lines.join('\n') + '\n';
303
+ }
304
+
305
+ function yamlString(value) {
306
+ return '"' + String(value).replace(/\\/g, '\\\\').replace(/"/g, '\\"') + '"';
307
+ }
308
+
309
+ /** True when a command is Forge-owned — the enforcement adapter OR a context CLI hook. */
310
+ function isForgeCommand(command) {
311
+ return typeof command === 'string'
312
+ && (command.includes(FORGE_HOOK_MARKER)
313
+ || command.includes(FORGE_CONTEXT_MARKER)
314
+ || command.includes(FORGE_INBOX_CONTEXT_MARKER));
315
+ }
316
+
317
+ /** True when a hook group/entry is Forge-owned (any inner command is Forge-owned). */
318
+ function isForgeClaudeGroup(group) {
319
+ const hooks = Array.isArray(group?.hooks) ? group.hooks : [];
320
+ return hooks.some(h => isForgeCommand(h?.command));
321
+ }
322
+
323
+ function isForgeCursorEntry(entry) {
324
+ return isForgeCommand(entry?.command);
325
+ }
326
+
327
+ function parseJsonConfig(existingText) {
328
+ if (!existingText || !existingText.trim()) return {};
329
+ let obj;
330
+ try {
331
+ obj = JSON.parse(existingText);
332
+ } catch (err) {
333
+ // DATA-LOSS GUARD: never silently discard a populated-but-unparseable config
334
+ // (JSONC comments / trailing commas). Signal the caller to back up + skip.
335
+ throw new HookConfigParseError('existing hook config is not valid JSON', err);
336
+ }
337
+ if (!obj || typeof obj !== 'object' || Array.isArray(obj)) return {};
338
+ return obj;
339
+ }
340
+
341
+ /**
342
+ * Merge Forge's hooks into an existing `.claude/settings.json` string.
343
+ * Preserves all other settings keys, all non-Forge events, and the user's own
344
+ * matcher-groups; replaces only Forge-owned groups (idempotent).
345
+ * @param {string} existingText
346
+ * @param {object} contract
347
+ * @returns {string}
348
+ */
349
+ function mergeClaudeSettings(existingText, contract) {
350
+ const obj = parseJsonConfig(existingText);
351
+ if (!obj.hooks || typeof obj.hooks !== 'object' || Array.isArray(obj.hooks)) obj.hooks = {};
352
+ const rendered = renderClaudeHooks(contract);
353
+ for (const [event, forgeGroups] of Object.entries(rendered)) {
354
+ const existingGroups = Array.isArray(obj.hooks[event]) ? obj.hooks[event] : [];
355
+ const userGroups = existingGroups.filter(group => !isForgeClaudeGroup(group));
356
+ obj.hooks[event] = [...userGroups, ...forgeGroups];
357
+ }
358
+ return JSON.stringify(obj, null, 2) + '\n';
359
+ }
360
+
361
+ /**
362
+ * Merge Forge's hooks into an existing `.cursor/hooks.json` string.
363
+ * Forces `version: 1`, preserves all non-Forge events and the user's own entries;
364
+ * replaces only Forge-owned entries (idempotent).
365
+ * @param {string} existingText
366
+ * @param {object} contract
367
+ * @returns {string}
368
+ */
369
+ function mergeCursorHooks(existingText, contract) {
370
+ const obj = parseJsonConfig(existingText);
371
+ obj.version = 1;
372
+ if (!obj.hooks || typeof obj.hooks !== 'object' || Array.isArray(obj.hooks)) obj.hooks = {};
373
+ const rendered = renderCursorHooks(contract);
374
+ for (const [event, forgeEntries] of Object.entries(rendered.hooks)) {
375
+ const existingEntries = Array.isArray(obj.hooks[event]) ? obj.hooks[event] : [];
376
+ const userEntries = existingEntries.filter(entry => !isForgeCursorEntry(entry));
377
+ obj.hooks[event] = [...userEntries, ...forgeEntries];
378
+ }
379
+ return JSON.stringify(obj, null, 2) + '\n';
380
+ }
381
+
382
+ const MERGERS = {
383
+ claude: mergeClaudeSettings,
384
+ cursor: mergeCursorHooks,
385
+ };
386
+
387
+ /**
388
+ * Render (merge) Forge's native hooks into one harness's native config on disk.
389
+ * Read → merge → write. Unparseable existing file → BACKED UP + left untouched
390
+ * (never overwritten), mirroring the MCP renderer's data-loss safety.
391
+ *
392
+ * Codex is GLOBAL-config scope: project setup cannot write it, so this returns a
393
+ * `scope: 'global-config'` skip WITHOUT touching disk (keeps Codex honest).
394
+ *
395
+ * @param {object} params
396
+ * @param {'claude'|'cursor'|'codex'} params.harness
397
+ * @param {string} params.targetRoot - Project root.
398
+ * @param {object} [params.contract] - Forge hook contract (defaults to FORGE_HOOK_CONTRACT).
399
+ * @returns {{ file?: string, existed?: boolean, skipped: boolean, wrote: boolean, backup?: string, scope?: string }}
400
+ */
401
+ function renderHookConfig({ harness, targetRoot, contract = FORGE_HOOK_CONTRACT }) {
402
+ if (harness === 'codex' || harness === 'hermes') {
403
+ // Honest: Codex (~/.codex/config.toml) and Hermes (~/.hermes/config.yaml) hooks
404
+ // both live in GLOBAL (home) config; project setup must not write global config.
405
+ // Their renderers are kept + tested for a global-config follow-up.
406
+ return { harness, scope: 'global-config', skipped: true, wrote: false };
407
+ }
408
+ const merge = MERGERS[harness];
409
+ const rel = HARNESS_HOOK_FILES[harness];
410
+ if (!merge || !rel) throw new Error(`Unknown hook harness: ${harness}`);
411
+
412
+ const filePath = path.join(targetRoot, rel);
413
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
414
+ const existed = fs.existsSync(filePath);
415
+ const existing = existed ? fs.readFileSync(filePath, 'utf-8') : '';
416
+
417
+ let merged;
418
+ try {
419
+ merged = merge(existing, contract);
420
+ } catch (err) {
421
+ if (err instanceof HookConfigParseError && existed) {
422
+ const backup = backupFile(filePath);
423
+ return { file: filePath, existed, skipped: true, wrote: false, backup };
424
+ }
425
+ throw err;
426
+ }
427
+
428
+ fs.writeFileSync(filePath, merged, 'utf-8');
429
+ return { file: filePath, existed, skipped: false, wrote: true };
430
+ }
431
+
432
+ module.exports = {
433
+ FORGE_HOOK_CONTRACT,
434
+ HARNESS_HOOK_FILES,
435
+ FORGE_HOOK_ADAPTER,
436
+ FORGE_HOOK_MARKER,
437
+ FORGE_CONTEXT_MARKER,
438
+ FORGE_INBOX_CONTEXT_MARKER,
439
+ SESSION_START_SUPPORT,
440
+ USER_PROMPT_SUBMIT_SUPPORT,
441
+ sessionStartCapability,
442
+ userPromptSubmitCapability,
443
+ HookConfigParseError,
444
+ renderClaudeHooks,
445
+ renderCursorHooks,
446
+ renderCodexHooksToml,
447
+ renderHermesHooksYaml,
448
+ mergeClaudeSettings,
449
+ mergeCursorHooks,
450
+ renderHookConfig,
451
+ };