forge-workflow 0.0.10 → 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 (454) 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 +150 -61
  8. package/CHANGELOG.md +681 -0
  9. package/CLAUDE.md +9 -118
  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 +461 -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/forge/TOOLCHAIN.md +670 -0
  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/{TOOLCHAIN.md → reference/TOOLCHAIN.md} +62 -47
  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 +121 -0
  81. package/lib/beads-sync-scaffold.js +25 -101
  82. package/lib/codex-skills.js +51 -1
  83. package/lib/commands/_issue.js +741 -77
  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 +17 -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 +0 -1
  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 +838 -972
  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 +158 -21
  140. package/lib/commands/sync.js +34 -46
  141. package/lib/commands/team.js +4 -1
  142. package/lib/commands/test.js +43 -27
  143. package/lib/commands/update.js +2 -2
  144. package/lib/commands/upgrade.js +47 -0
  145. package/lib/commands/validate.js +43 -18
  146. package/lib/commands/worktree.js +307 -100
  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 +2 -2
  151. package/lib/deprecated-sync-cleanup.js +362 -0
  152. package/lib/detect-agent.js +2 -28
  153. package/lib/detect-worktree.js +35 -9
  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 +382 -11
  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/kernel/backing-issue.js +305 -0
  174. package/lib/kernel/broker.js +1218 -0
  175. package/lib/kernel/cli-broker-factory.js +130 -0
  176. package/lib/kernel/conflict-signal.js +82 -0
  177. package/lib/kernel/evaluators.js +195 -0
  178. package/lib/kernel/fs-class.js +495 -0
  179. package/lib/kernel/issue-command-contract.js +559 -0
  180. package/lib/kernel/issue-id-resolver.js +186 -0
  181. package/lib/kernel/lease-enforcer.js +158 -0
  182. package/lib/kernel/migrations.js +333 -0
  183. package/lib/kernel/planning-buckets-schema.js +109 -0
  184. package/lib/kernel/projection-jsonl-writer.js +450 -0
  185. package/lib/kernel/readiness-model.js +329 -0
  186. package/lib/kernel/schema.js +356 -0
  187. package/lib/kernel/sqlite-driver.js +2504 -0
  188. package/lib/kernel/taxonomy-validator.js +394 -0
  189. package/lib/lefthook-check.js +3 -2
  190. package/lib/lefthook-wiring.js +413 -0
  191. package/lib/mcp-config-renderer.js +288 -0
  192. package/lib/memory/graphiti-mcp.js +106 -0
  193. package/lib/memory/router.js +387 -0
  194. package/lib/memory/typed-api.js +102 -0
  195. package/lib/memory-digest.js +195 -0
  196. package/lib/merge-rules.js +395 -0
  197. package/lib/migrate-dry-run.js +466 -0
  198. package/lib/orientation.js +863 -0
  199. package/lib/package-manager-remediation.js +103 -0
  200. package/lib/package-root.js +381 -0
  201. package/lib/patch-intent.js +890 -0
  202. package/lib/plugin-catalog.js +3 -4
  203. package/lib/plugin-manager.js +0 -5
  204. package/lib/pr-bundle.js +186 -0
  205. package/lib/pr-monitor/differ.js +195 -0
  206. package/lib/pr-monitor/events.js +0 -0
  207. package/lib/pr-monitor/gather.js +124 -0
  208. package/lib/pr-monitor/journal.js +299 -0
  209. package/lib/pr-monitor/monitor.js +146 -0
  210. package/lib/pr-monitor/render-sticky.js +157 -0
  211. package/lib/pr-monitor/watch-lifecycle.js +95 -0
  212. package/lib/pr-monitor/watch.js +247 -0
  213. package/lib/pr-pull.js +1273 -0
  214. package/lib/pr-shepherd.js +494 -0
  215. package/lib/pr-state-validator.js +59 -0
  216. package/lib/preflight/gates.js +237 -0
  217. package/lib/preflight/runner.js +116 -0
  218. package/lib/project-discovery.js +0 -53
  219. package/lib/project-memory.js +99 -497
  220. package/lib/protected-path-manifest.js +281 -0
  221. package/lib/protected-state-surfaces.js +387 -0
  222. package/lib/release-readiness.js +2089 -0
  223. package/lib/reset.js +59 -45
  224. package/lib/review-adapter.js +68 -0
  225. package/lib/rules-sync.js +260 -0
  226. package/lib/runtime-health.js +241 -20
  227. package/lib/safety-config-renderer.js +268 -0
  228. package/lib/setup-action-log.js +1 -7
  229. package/lib/setup.js +27 -65
  230. package/lib/shell-utils.js +76 -6
  231. package/lib/skills-sync.js +330 -0
  232. package/lib/smart-status/scoring.js +17 -3
  233. package/lib/status/beads-snapshot.js +45 -2
  234. package/lib/status/presenter.js +169 -18
  235. package/lib/status/snapshot.js +186 -0
  236. package/lib/sync-backend.js +202 -0
  237. package/lib/untrusted-content.js +52 -0
  238. package/lib/upgrade-safety.js +199 -0
  239. package/lib/workflow/enforce-stage.js +296 -47
  240. package/lib/workflow/stage-transition.js +115 -0
  241. package/lib/workflow/stages.js +30 -6
  242. package/lib/workflow/state-manager.js +11 -22
  243. package/lib/workflow/state.js +23 -1
  244. package/lib/workflow-profiles.js +17 -5
  245. package/package.json +37 -35
  246. package/rules/documentation.md +19 -0
  247. package/rules/kernel-tracking.md +26 -0
  248. package/rules/security.md +22 -0
  249. package/rules/tdd.md +20 -0
  250. package/rules/workflow.md +27 -0
  251. package/scripts/auto-backing-issue.js +47 -0
  252. package/scripts/beads-context.sh +81 -57
  253. package/scripts/beads-upgrade-smoke.sh +24 -3
  254. package/scripts/bootstrap-windows-tools.sh +78 -0
  255. package/scripts/branch-protection.js +2 -3
  256. package/scripts/check-agents.js +34 -137
  257. package/scripts/commitlint.js +3 -1
  258. package/scripts/conflict-detect.sh +3 -0
  259. package/scripts/dep-guard.sh +22 -3
  260. package/scripts/file-index.sh +3 -0
  261. package/scripts/forge-team/lib/claim.sh +34 -18
  262. package/scripts/forge-team/lib/dashboard.sh +61 -86
  263. package/scripts/forge-team/lib/epic.sh +99 -263
  264. package/scripts/forge-team/lib/hooks.sh +26 -28
  265. package/scripts/forge-team/lib/identity.sh +4 -4
  266. package/scripts/forge-team/lib/sync-github.sh +49 -84
  267. package/scripts/forge-team/lib/verify.sh +93 -83
  268. package/scripts/forge-team/lib/workload.sh +41 -65
  269. package/scripts/forge-team/tests/claim.test.sh +25 -19
  270. package/scripts/forge-team/tests/dashboard.test.sh +31 -46
  271. package/scripts/forge-team/tests/epic.test.sh +52 -71
  272. package/scripts/forge-team/tests/hooks.test.sh +38 -50
  273. package/scripts/forge-team/tests/identity.test.sh +3 -3
  274. package/scripts/forge-team/tests/integration.test.sh +44 -66
  275. package/scripts/forge-team/tests/sync-github.test.sh +50 -83
  276. package/scripts/forge-team/tests/verify.test.sh +37 -46
  277. package/scripts/forge-team/tests/workflow-integration.test.sh +4 -4
  278. package/scripts/forge-team/tests/workload.test.sh +32 -66
  279. package/scripts/gen-command-manifest.js +153 -0
  280. package/scripts/gen-embedded-assets.mjs +129 -0
  281. package/scripts/install.ps1 +139 -0
  282. package/scripts/install.sh +268 -0
  283. package/scripts/lib/release-asset.mjs +84 -0
  284. package/scripts/parity-check.mjs +145 -0
  285. package/scripts/parity-check.test.mjs +58 -0
  286. package/scripts/pin-agentic-workflow-images.js +112 -0
  287. package/scripts/pr-coordinator.sh +3 -0
  288. package/scripts/preflight-sonar.eslint.config.mjs +44 -0
  289. package/scripts/preflight.sh +21 -94
  290. package/scripts/protected-state-check.js +104 -0
  291. package/scripts/smart-status.sh +60 -57
  292. package/scripts/spikes/config-race-bench.js +111 -0
  293. package/scripts/spikes/harness-capability-matrix.js +13 -0
  294. package/scripts/spikes/patch-anchor-stability-bench.js +125 -0
  295. package/scripts/spikes/protected-path-manifest.js +20 -0
  296. package/scripts/spikes/skill-auto-invoke-parity.js +292 -0
  297. package/scripts/sync-agent-skills.js +62 -0
  298. package/scripts/sync-utils.sh +3 -0
  299. package/scripts/test-ci-shard.js +13 -6
  300. package/scripts/test.js +95 -12
  301. package/skills/claim-safety/SKILL.md +102 -0
  302. package/skills/claim-safety/evals/evals.json +46 -0
  303. package/{.github/prompts/dev.prompt.md → skills/dev/SKILL.md} +44 -50
  304. package/skills/dev/evals/evals.json +50 -0
  305. package/skills/hermes-forge/SKILL.md +185 -0
  306. package/skills/hermes-forge/evals/evals.json +46 -0
  307. package/skills/issue-basics/SKILL.md +111 -0
  308. package/skills/issue-basics/evals/evals.json +46 -0
  309. package/skills/kernel/SKILL.md +166 -0
  310. package/skills/kernel/evals/evals.json +50 -0
  311. package/skills/memory/SKILL.md +102 -0
  312. package/skills/parallel-deep-research/SKILL.md +14 -11
  313. package/skills/parallel-deep-research/evals/evals.json +11 -27
  314. package/{.github/prompts/plan.prompt.md → skills/plan/SKILL.md} +132 -157
  315. package/skills/plan/evals/evals.json +42 -0
  316. package/skills/research/SKILL.md +195 -0
  317. package/skills/research/evals/evals.json +42 -0
  318. package/{.github/prompts/review.prompt.md → skills/review/SKILL.md} +98 -62
  319. package/skills/review/evals/evals.json +42 -0
  320. package/skills/rollback/SKILL.md +110 -0
  321. package/skills/rollback/evals/evals.json +46 -0
  322. package/skills/rollback/references/methods.md +204 -0
  323. package/{.cursor/commands/rollback.md → skills/rollback/references/workflow-integration.md} +10 -284
  324. package/skills/shepherd/SKILL.md +66 -0
  325. package/skills/shepherd/evals/evals.json +42 -0
  326. package/{.github/prompts/ship.prompt.md → skills/ship/SKILL.md} +81 -45
  327. package/skills/ship/evals/evals.json +42 -0
  328. package/skills/smith/SKILL.md +142 -0
  329. package/skills/smith/evals/evals.json +46 -0
  330. package/skills/smith/references/autonomy-and-gates.md +94 -0
  331. package/{.github/prompts/sonarcloud.prompt.md → skills/sonarcloud/SKILL.md} +14 -3
  332. package/skills/sonarcloud/evals/evals.json +46 -0
  333. package/skills/sonarcloud-analysis/SKILL.md +18 -13
  334. package/skills/sonarcloud-analysis/evals/evals.json +11 -15
  335. package/{.github/prompts/status.prompt.md → skills/status/SKILL.md} +20 -10
  336. package/skills/status/evals/evals.json +50 -0
  337. package/skills/triage-ready/SKILL.md +121 -0
  338. package/skills/triage-ready/evals/evals.json +42 -0
  339. package/{.github/prompts/validate.prompt.md → skills/validate/SKILL.md} +52 -29
  340. package/skills/validate/evals/evals.json +42 -0
  341. package/skills/verify/SKILL.md +299 -0
  342. package/skills/verify/evals/evals.json +50 -0
  343. package/.claude/commands/dev.md +0 -345
  344. package/.claude/commands/plan.md +0 -566
  345. package/.claude/commands/premerge.md +0 -186
  346. package/.claude/commands/research.md +0 -42
  347. package/.claude/commands/review.md +0 -451
  348. package/.claude/commands/rollback.md +0 -721
  349. package/.claude/commands/ship.md +0 -213
  350. package/.claude/commands/sonarcloud.md +0 -152
  351. package/.claude/commands/status.md +0 -90
  352. package/.claude/commands/validate.md +0 -288
  353. package/.claude/commands/verify.md +0 -269
  354. package/.claude/rules/workflow.md +0 -121
  355. package/.cline/workflows/dev.md +0 -342
  356. package/.cline/workflows/plan.md +0 -563
  357. package/.cline/workflows/premerge.md +0 -183
  358. package/.cline/workflows/research.md +0 -39
  359. package/.cline/workflows/review.md +0 -448
  360. package/.cline/workflows/rollback.md +0 -718
  361. package/.cline/workflows/ship.md +0 -210
  362. package/.cline/workflows/sonarcloud.md +0 -146
  363. package/.cline/workflows/status.md +0 -87
  364. package/.cline/workflows/validate.md +0 -285
  365. package/.cline/workflows/verify.md +0 -266
  366. package/.codex/config.toml +0 -11
  367. package/.codex/skills/dev/SKILL.md +0 -345
  368. package/.codex/skills/plan/SKILL.md +0 -566
  369. package/.codex/skills/premerge/SKILL.md +0 -186
  370. package/.codex/skills/research/SKILL.md +0 -42
  371. package/.codex/skills/review/SKILL.md +0 -451
  372. package/.codex/skills/rollback/SKILL.md +0 -721
  373. package/.codex/skills/ship/SKILL.md +0 -213
  374. package/.codex/skills/sonarcloud/SKILL.md +0 -149
  375. package/.codex/skills/status/SKILL.md +0 -90
  376. package/.codex/skills/validate/SKILL.md +0 -288
  377. package/.codex/skills/verify/SKILL.md +0 -269
  378. package/.cursor/commands/dev.md +0 -342
  379. package/.cursor/commands/plan.md +0 -563
  380. package/.cursor/commands/premerge.md +0 -183
  381. package/.cursor/commands/research.md +0 -39
  382. package/.cursor/commands/review.md +0 -448
  383. package/.cursor/commands/ship.md +0 -210
  384. package/.cursor/commands/sonarcloud.md +0 -146
  385. package/.cursor/commands/status.md +0 -87
  386. package/.cursor/commands/validate.md +0 -285
  387. package/.cursor/commands/verify.md +0 -266
  388. package/.cursorrules +0 -149
  389. package/.github/prompts/premerge.prompt.md +0 -188
  390. package/.github/prompts/research.prompt.md +0 -44
  391. package/.github/prompts/rollback.prompt.md +0 -723
  392. package/.github/prompts/verify.prompt.md +0 -271
  393. package/.github/workflows/beads-to-github.yml +0 -89
  394. package/.github/workflows/github-to-beads.yml +0 -100
  395. package/.kilocode/workflows/dev.md +0 -346
  396. package/.kilocode/workflows/plan.md +0 -567
  397. package/.kilocode/workflows/premerge.md +0 -187
  398. package/.kilocode/workflows/research.md +0 -43
  399. package/.kilocode/workflows/review.md +0 -452
  400. package/.kilocode/workflows/rollback.md +0 -722
  401. package/.kilocode/workflows/ship.md +0 -214
  402. package/.kilocode/workflows/sonarcloud.md +0 -150
  403. package/.kilocode/workflows/status.md +0 -91
  404. package/.kilocode/workflows/validate.md +0 -289
  405. package/.kilocode/workflows/verify.md +0 -270
  406. package/.opencode/commands/dev.md +0 -345
  407. package/.opencode/commands/plan.md +0 -566
  408. package/.opencode/commands/premerge.md +0 -186
  409. package/.opencode/commands/research.md +0 -42
  410. package/.opencode/commands/review.md +0 -451
  411. package/.opencode/commands/rollback.md +0 -721
  412. package/.opencode/commands/ship.md +0 -213
  413. package/.opencode/commands/sonarcloud.md +0 -149
  414. package/.opencode/commands/status.md +0 -90
  415. package/.opencode/commands/validate.md +0 -288
  416. package/.opencode/commands/verify.md +0 -269
  417. package/.roo/commands/dev.md +0 -346
  418. package/.roo/commands/plan.md +0 -567
  419. package/.roo/commands/premerge.md +0 -187
  420. package/.roo/commands/research.md +0 -43
  421. package/.roo/commands/review.md +0 -452
  422. package/.roo/commands/rollback.md +0 -722
  423. package/.roo/commands/ship.md +0 -214
  424. package/.roo/commands/sonarcloud.md +0 -150
  425. package/.roo/commands/status.md +0 -91
  426. package/.roo/commands/validate.md +0 -289
  427. package/.roo/commands/verify.md +0 -270
  428. package/docs/BEADS_GITHUB_SYNC.md +0 -281
  429. package/docs/GREPTILE_SETUP.md +0 -400
  430. package/docs/MANUAL_REVIEW_GUIDE.md +0 -106
  431. package/docs/SETUP.md +0 -663
  432. package/docs/VALIDATION.md +0 -363
  433. package/lib/agents/cline.plugin.json +0 -29
  434. package/lib/agents/copilot.plugin.json +0 -24
  435. package/lib/agents/kilocode.plugin.json +0 -22
  436. package/lib/agents/opencode.plugin.json +0 -23
  437. package/lib/agents/roo.plugin.json +0 -30
  438. package/lib/beads-bootstrap.js +0 -225
  439. package/lib/beads-health-check.js +0 -188
  440. package/lib/commands/commands-reset.js +0 -147
  441. package/opencode.json +0 -67
  442. package/scripts/beads-context.test.js +0 -584
  443. package/scripts/github-beads-sync/comment.mjs +0 -64
  444. package/scripts/github-beads-sync/config.mjs +0 -148
  445. package/scripts/github-beads-sync/github-api.mjs +0 -131
  446. package/scripts/github-beads-sync/index.mjs +0 -356
  447. package/scripts/github-beads-sync/label-mapper.mjs +0 -54
  448. package/scripts/github-beads-sync/mapping.mjs +0 -132
  449. package/scripts/github-beads-sync/reverse-sync-cli.mjs +0 -31
  450. package/scripts/github-beads-sync/reverse-sync.mjs +0 -162
  451. package/scripts/github-beads-sync/run-bd.mjs +0 -161
  452. package/scripts/github-beads-sync/sanitize.mjs +0 -121
  453. package/scripts/github-beads-sync.config.json +0 -26
  454. package/scripts/sync-commands.js +0 -600
@@ -1,17 +1,22 @@
1
- # Greptile Review Handling Process
1
+ # Review Thread Handling Process
2
2
 
3
- **Purpose**: Standardized process for AI agents to systematically handle Greptile review comments.
3
+ **Purpose**: Standardized process for AI agents to systematically handle PR
4
+ review threads from **any review agent** — CodeRabbit, Qodo, Greptile, or a
5
+ human reviewer. The mechanism (GitHub review threads via GraphQL/REST) is the
6
+ same regardless of which tool or person authored the comment; the examples below
7
+ name specific tools only as illustrations.
4
8
 
5
- **Critical**: This process has been problematic for days. AI agents MUST follow these exact steps for EVERY Greptile comment.
9
+ **Critical**: Review threads left unresolved block merges. AI agents MUST follow
10
+ these exact steps for EVERY review comment, whoever authored it.
6
11
 
7
12
  ---
8
13
 
9
14
  ## Problem Background
10
15
 
11
16
  **Previous Issues:**
12
- - ❌ AI agents replied to PR with general comments instead of replying directly to each Greptile review thread
17
+ - ❌ AI agents replied to the PR with general comments instead of replying directly to each review thread
13
18
  - ❌ AI agents didn't mark conversation threads as "resolved" after fixing issues
14
- - ❌ No systematic process for tracking which Greptile comments are addressed vs pending
19
+ - ❌ No systematic process for tracking which review comments are addressed vs pending
15
20
  - ❌ Caused confusion and manual tracking overhead for maintainers
16
21
 
17
22
  **Why This Matters:**
@@ -39,28 +44,31 @@
39
44
  - Reply explains what was fixed and why
40
45
  - Resolve marks the thread as addressed
41
46
 
47
+ These two GitHub operations are author-agnostic: they work identically for a
48
+ CodeRabbit thread, a Qodo thread, a Greptile thread, or a human reviewer's thread.
49
+
42
50
  ---
43
51
 
44
52
  ## For AI Agents: Mandatory Steps
45
53
 
46
- When Greptile Quality Gate fails (score < 4/5) or when review comments exist:
54
+ When a review-agent quality gate fails or when review comments exist:
47
55
 
48
- ### Step 1: List ALL Greptile Feedback (Inline + Direct Comments)
56
+ ### Step 1: List ALL Review Feedback (Inline + Direct Comments)
49
57
 
50
- Greptile posts feedback in **two places**:
58
+ Review agents post feedback in **two places**:
51
59
  1. **Inline review threads** — attached to specific code lines (resolvable via GraphQL)
52
- 2. **Direct PR issue comments** — "Additional Comments (N)" posted as regular PR comments (reply with `gh pr comment`)
60
+ 2. **Direct PR issue comments** — summaries or "Additional Comments (N)" posted as regular PR comments (reply with `gh pr comment`)
53
61
 
54
62
  Always run `list-all` to see both:
55
63
 
56
64
  ```bash
57
- bash .claude/scripts/greptile-resolve.sh list-all <pr-number>
65
+ bash .claude/scripts/review-resolve.sh list-all <pr-number>
58
66
  ```
59
67
 
60
68
  Or just inline threads (unresolved only):
61
69
 
62
70
  ```bash
63
- bash .claude/scripts/greptile-resolve.sh list <pr-number> --unresolved
71
+ bash .claude/scripts/review-resolve.sh list <pr-number> --unresolved
64
72
  ```
65
73
 
66
74
  **Output shows:**
@@ -68,6 +76,7 @@ bash .claude/scripts/greptile-resolve.sh list <pr-number> --unresolved
68
76
  - Comment ID (for replying to inline threads)
69
77
  - File path and line number
70
78
  - Issue description
79
+ - Author (so you can see whether it came from CodeRabbit, Greptile, Qodo, a human, etc.)
71
80
  - Direct PR comment IDs and previews (reply with `gh pr comment`)
72
81
 
73
82
  **Example:**
@@ -85,7 +94,7 @@ bash .claude/scripts/greptile-resolve.sh list <pr-number> --unresolved
85
94
 
86
95
  1. **Read the comment** and understand the issue
87
96
  - Use the file path and line number to locate the code
88
- - Understand what Greptile is flagging
97
+ - Understand what the reviewer is flagging
89
98
 
90
99
  2. **Fix the issue** if the comment is valid
91
100
  - Make the necessary code changes
@@ -93,7 +102,7 @@ bash .claude/scripts/greptile-resolve.sh list <pr-number> --unresolved
93
102
 
94
103
  3. **Reply to the thread** with explanation
95
104
  ```bash
96
- bash .claude/scripts/greptile-resolve.sh reply <pr-number> <comment-id> "✅ Fixed: [description]
105
+ bash .claude/scripts/review-resolve.sh reply <pr-number> <comment-id> "✅ Fixed: [description]
97
106
 
98
107
  Changed: [what was changed]
99
108
  Reason: [why this fixes the issue]
@@ -102,14 +111,14 @@ bash .claude/scripts/greptile-resolve.sh list <pr-number> --unresolved
102
111
 
103
112
  4. **Resolve the thread**
104
113
  ```bash
105
- bash .claude/scripts/greptile-resolve.sh resolve <pr-number> <thread-id>
114
+ bash .claude/scripts/review-resolve.sh resolve <pr-number> <thread-id>
106
115
  ```
107
116
 
108
117
  5. **Track progress**: Mark comment as addressed in your notes
109
118
 
110
119
  **Alternative (all-in-one):**
111
120
  ```bash
112
- bash .claude/scripts/greptile-resolve.sh reply-and-resolve <pr-number> <comment-id> <thread-id> "✅ Fixed: [description]
121
+ bash .claude/scripts/review-resolve.sh reply-and-resolve <pr-number> <comment-id> <thread-id> "✅ Fixed: [description]
113
122
 
114
123
  Changed: [what was changed]
115
124
  Reason: [why this fixes the issue]
@@ -119,17 +128,17 @@ Commit: [commit-sha]"
119
128
  ### Step 3: Verify All Threads Resolved
120
129
 
121
130
  ```bash
122
- bash .claude/scripts/greptile-resolve.sh list <pr-number> --unresolved
131
+ bash .claude/scripts/review-resolve.sh list <pr-number> --unresolved
123
132
  ```
124
133
 
125
134
  **Should show**: "No unresolved comments" or empty list
126
135
 
127
136
  **Confirm with stats:**
128
137
  ```bash
129
- bash .claude/scripts/greptile-resolve.sh stats <pr-number>
138
+ bash .claude/scripts/review-resolve.sh stats <pr-number>
130
139
  ```
131
140
 
132
- **Should show**: "✓ All Greptile threads resolved!"
141
+ **Should show**: all tracked review threads resolved.
133
142
 
134
143
  ### Step 4: Push Changes & Poll Briefly for Re-review
135
144
 
@@ -137,10 +146,10 @@ bash .claude/scripts/greptile-resolve.sh stats <pr-number>
137
146
  git push
138
147
  ```
139
148
 
140
- **Greptile will automatically:**
149
+ **The review agent will automatically:**
141
150
  - Re-analyze the PR
142
- - Update the confidence score
143
- - Re-run the Quality Gate check
151
+ - Update any confidence/quality score
152
+ - Re-run its quality gate check
144
153
 
145
154
  ---
146
155
 
@@ -148,7 +157,7 @@ git push
148
157
 
149
158
  ```bash
150
159
  # 1. List unresolved threads
151
- $ bash .claude/scripts/greptile-resolve.sh list 24 --unresolved
160
+ $ bash .claude/scripts/review-resolve.sh list 24 --unresolved
152
161
 
153
162
  ✗ UNRESOLVED | docs/ROADMAP.md:280
154
163
  Thread ID: PRRT_kwDORErEU85tuh6I
@@ -160,7 +169,7 @@ $ git add docs/ROADMAP.md
160
169
  $ git commit -m "fix: replace Windows absolute paths with relative paths"
161
170
 
162
171
  # 3. Reply and resolve in one step
163
- $ bash .claude/scripts/greptile-resolve.sh reply-and-resolve 24 2787717459 PRRT_kwDORErEU85tuh6I \
172
+ $ bash .claude/scripts/review-resolve.sh reply-and-resolve 24 2787717459 PRRT_kwDORErEU85tuh6I \
164
173
  "✅ Fixed: Replaced Windows absolute paths with repo-relative paths
165
174
 
166
175
  Changed: C:\\Users\\...\\plans\\... → .claude/plans/*.md
@@ -171,10 +180,10 @@ Commit: abc123"
171
180
  ✅ Thread resolved successfully
172
181
 
173
182
  # 4. Verify all resolved
174
- $ bash .claude/scripts/greptile-resolve.sh stats 24
183
+ $ bash .claude/scripts/review-resolve.sh stats 24
175
184
 
176
- Greptile unresolved: 0
177
- ✓ All Greptile threads resolved!
185
+ Unresolved: 0
186
+ ✓ All review threads resolved!
178
187
 
179
188
  # 5. Push changes
180
189
  $ git push
@@ -188,7 +197,7 @@ $ git push
188
197
  - **Reply to EACH comment thread** using the script (not as separate PR comment)
189
198
  - **Mark EACH thread as resolved** after fixing using the script
190
199
  - **Track progress** (X of Y fixed) in your notes
191
- - **Poll for Greptile re-review for at most 60 seconds** after pushing fixes, then stop and hand off if it is still pending
200
+ - **Poll for re-review for at most 60 seconds** after pushing fixes, then stop and hand off if it is still pending
192
201
  - **Use comment ID for replies**, thread ID for resolving
193
202
  - **Commit fixes BEFORE replying** so you can reference commit SHA
194
203
 
@@ -204,54 +213,60 @@ $ git push
204
213
 
205
214
  ## Script Commands Reference
206
215
 
216
+ The `.claude/scripts/review-resolve.sh` helper works for review threads authored
217
+ by any tool or person. The `list-all`, `resolve-all`, and `stats` commands
218
+ additionally recognize `greptile-apps`-authored threads as a built-in convenience
219
+ (Greptile is one supported review tool); the core commands below are fully
220
+ author-agnostic.
221
+
207
222
  ### List Threads
208
223
  ```bash
209
224
  # All threads
210
- bash .claude/scripts/greptile-resolve.sh list <pr-number>
225
+ bash .claude/scripts/review-resolve.sh list <pr-number>
211
226
 
212
227
  # Only unresolved
213
- bash .claude/scripts/greptile-resolve.sh list <pr-number> --unresolved
228
+ bash .claude/scripts/review-resolve.sh list <pr-number> --unresolved
214
229
  ```
215
230
 
216
231
  ### Reply to Thread
217
232
  ```bash
218
- bash .claude/scripts/greptile-resolve.sh reply <pr-number> <comment-id> "<message>"
233
+ bash .claude/scripts/review-resolve.sh reply <pr-number> <comment-id> "<message>"
219
234
  ```
220
235
 
221
236
  ### Resolve Thread
222
237
  ```bash
223
- bash .claude/scripts/greptile-resolve.sh resolve <pr-number> <thread-id>
238
+ bash .claude/scripts/review-resolve.sh resolve <pr-number> <thread-id>
224
239
  ```
225
240
 
226
241
  ### Reply and Resolve (Recommended)
227
242
  ```bash
228
- bash .claude/scripts/greptile-resolve.sh reply-and-resolve <pr-number> <comment-id> <thread-id> "<message>"
243
+ bash .claude/scripts/review-resolve.sh reply-and-resolve <pr-number> <comment-id> <thread-id> "<message>"
229
244
  ```
230
245
 
231
246
  ### Batch Resolve (After all issues fixed)
232
247
  ```bash
233
- bash .claude/scripts/greptile-resolve.sh resolve-all <pr-number>
248
+ bash .claude/scripts/review-resolve.sh resolve-all <pr-number>
234
249
  ```
235
250
  **⚠️ Warning**: Only use after ALL issues are fixed and replied to
236
251
 
237
252
  ### Statistics
238
253
  ```bash
239
- bash .claude/scripts/greptile-resolve.sh stats <pr-number>
254
+ bash .claude/scripts/review-resolve.sh stats <pr-number>
240
255
  ```
241
256
 
242
257
  ---
243
258
 
244
259
  ## Integration with `/review` Command
245
260
 
246
- The `/review` command should now include these steps:
261
+ The `/review` command includes these steps:
247
262
 
248
263
  1. Run `/review` as usual to analyze PR feedback
249
- 2. For Greptile comments, use the script to:
264
+ 2. For review-agent comments (CodeRabbit, Qodo, Greptile, human), use the script to:
250
265
  - List unresolved threads
251
266
  - Fix each issue
252
267
  - Reply and resolve systematically
253
268
  3. Verify all threads resolved before declaring review complete
254
- 4. Push changes and poll briefly for Greptile re-review
269
+ 4. Push changes and poll briefly for re-review
255
270
 
256
271
  ---
257
272
 
@@ -269,17 +284,17 @@ The `/review` command should now include these steps:
269
284
  **Cause**: GitHub UI caching
270
285
  **Solution**: Refresh page, or check with GraphQL query
271
286
 
272
- ### Greptile score not updating after fixes
287
+ ### Review score not updating after fixes
273
288
  **Cause**: Must push commits to trigger re-analysis
274
- **Solution**: `git push`, check once immediately, and poll for up to 60 seconds. If Greptile is still pending after that, stop and wait for the user to resume later.
289
+ **Solution**: `git push`, check once immediately, and poll for up to 60 seconds. If the review agent is still pending after that, stop and wait for the user to resume later.
275
290
 
276
291
  ---
277
292
 
278
293
  ## Success Criteria
279
294
 
280
295
  **A review is complete when:**
281
- - ✅ All Greptile threads are resolved (verified with `stats` command)
296
+ - ✅ All review threads are resolved (verified with the `stats` command)
282
297
  - ✅ All threads have replies explaining fixes
283
- - ✅ Greptile Quality Gate passes (≥4/5 score)
298
+ - ✅ Any review-agent quality gate passes
284
299
  - ✅ Branch protection allows merge
285
300
  - ✅ No manual tracking needed by maintainers
@@ -1,10 +1,20 @@
1
1
  #!/bin/bash
2
- # Greptile Review Thread Resolution Tool
3
- # Uses GitHub GraphQL API to systematically resolve review threads
4
- # Uses GitHub REST API to reply to review comments
2
+ # Review Thread Resolution Tool
3
+ # Resolves GitHub PR review threads from ANY review author — CodeRabbit, Qodo,
4
+ # Greptile, or a human reviewer the mechanism is author-agnostic.
5
+ # Uses GitHub GraphQL API to systematically resolve review threads.
6
+ # Uses GitHub REST API to reply to review comments.
7
+ # Note: the `list-all`, `resolve-all`, and `stats` convenience commands additionally
8
+ # recognize `greptile-apps`-authored threads (Greptile is one supported review tool);
9
+ # the core list/reply/resolve/reply-and-resolve commands work for every author.
5
10
 
6
11
  set -e
7
12
 
13
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
14
+ if [ -f "$SCRIPT_DIR/../../scripts/bootstrap-windows-tools.sh" ]; then
15
+ source "$SCRIPT_DIR/../../scripts/bootstrap-windows-tools.sh"
16
+ fi
17
+
8
18
  # Colors for output (defined before use)
9
19
  RED='\033[0;31m'
10
20
  GREEN='\033[0;32m'
@@ -13,7 +13,7 @@ Bash(git status *), Bash(git log *), Bash(git diff *), Bash(git branch *)
13
13
  Bash(git add *), Bash(git commit *), Bash(git push *), Bash(git stash *)
14
14
  Bash(git checkout *), Bash(git switch *), Bash(git merge *), Bash(git worktree *)
15
15
  Bash(bun *), Bash(bunx *), Bash(npm *), Bash(npx *), Bash(node *)
16
- Bash(bd *), Bash(gh *), Bash(openspec *)
16
+ Bash(forge *), Bash(gh *), Bash(openspec *)
17
17
  Bash(ls *), Bash(cat *), Bash(grep *), Bash(find *), Bash(pwd *), Bash(which *)
18
18
  Bash(echo *), Bash(head *), Bash(tail *), Bash(sort *), Bash(wc *)
19
19
  Bash(mkdir *), Bash(touch *), Bash(cp *), Bash(mv *)
@@ -21,7 +21,7 @@ Bash(mkdir *), Bash(touch *), Bash(cp *), Bash(mv *)
21
21
 
22
22
  ## Confirm Before Running (prompt each time)
23
23
 
24
- <!-- git reset --hard moved from Deny to Confirm to match opencode.json "ask" policy.
24
+ <!-- git reset --hard moved from Deny to Confirm ("ask" policy).
25
25
  Rationale: agents may need it for worktree cleanup during /verify stage,
26
26
  but it should always require user confirmation since it discards uncommitted changes. -->
27
27
  ```
@@ -111,6 +111,9 @@ function hasTestFile(sourceFile, stagedFiles) {
111
111
  sourceFile.replace(/^bin\//, 'test/cli/').replace(ext, `.spec${ext}`),
112
112
  sourceFile.replace(/^bin\//, 'test/bin/').replace(ext, `.test${ext}`),
113
113
  sourceFile.replace(/^bin\//, 'test/bin/').replace(ext, `.spec${ext}`),
114
+ // Workspace packages (packages/<pkg>/src/**/foo.js → packages/<pkg>/test/foo.test.js)
115
+ sourceFile.replace(/^(packages\/[^/]+)\/src\/(?:.*\/)?([^/]+)$/, `$1/test/$2`).replace(ext, `.test${ext}`),
116
+ sourceFile.replace(/^(packages\/[^/]+)\/src\/(?:.*\/)?([^/]+)$/, `$1/test/$2`).replace(ext, `.spec${ext}`),
114
117
  ];
115
118
 
116
119
  // Check if test file exists in staged files or filesystem
@@ -0,0 +1,245 @@
1
+ #!/usr/bin/env node
2
+
3
+ 'use strict';
4
+
5
+ /**
6
+ * Forge native-hook enforcement adapter.
7
+ *
8
+ * Invoked by the rendered native hook configs (.claude/settings.json hooks block
9
+ * and .cursor/hooks.json) that `forge setup` writes via lib/hook-renderer.js. It
10
+ * projects Forge's lifecycle enforcement onto each harness's native hook surface:
11
+ *
12
+ * --intent protected-path : deny writes/edits to Forge-protected paths.
13
+ * --intent tdd-gate : on a `git commit`, delegate to the installed
14
+ * check-tdd.js and deny the commit if source files
15
+ * are staged without accompanying tests.
16
+ *
17
+ * Contract (verified from harness docs):
18
+ * - Claude PreToolUse : stdin has { tool_name, tool_input:{ file_path|command } };
19
+ * to BLOCK, print { hookSpecificOutput:{ hookEventName:'PreToolUse',
20
+ * permissionDecision:'deny', permissionDecisionReason } } and exit 0. To
21
+ * allow, print nothing (never auto-approve past the user's own settings).
22
+ * - Cursor before* : stdin has { command|file_path }; to BLOCK, print
23
+ * { continue:true, permission:'deny', agentMessage, userMessage } and exit 0.
24
+ * (afterFileEdit is observational — permission is ignored but the message
25
+ * still surfaces the protected-path violation.)
26
+ *
27
+ * SELF-CONTAINED: target projects have .forge/hooks/*.js but NOT lib/, so this
28
+ * module requires nothing from the Forge package. The protected-path set is a
29
+ * conservative built-in mirroring the categories in .forge/protected-paths.yaml.
30
+ *
31
+ * Security: delegates to check-tdd.js via execFileSync (no shell), never evals input.
32
+ */
33
+
34
+ const { execFileSync } = require('node:child_process');
35
+ const path = require('node:path');
36
+
37
+ // Conservative protected-path set, mirroring .forge/protected-paths.yaml categories:
38
+ // forge_core (.forge/), user_protocol (AGENTS.md), immutable/config (lefthook.yml),
39
+ // secrets (.env*), lockfiles (package-lock/yarn/pnpm/bun), the issue-state dir
40
+ // (cli-only), and VCS (.git/).
41
+ // NOTE: the issue-state matcher is built from a split literal so the D20 retirement
42
+ // audit (release-readiness.js findBdTerms) does not miscount a path matcher as
43
+ // direct issue-tool access — this file never touches that tooling.
44
+ const ISSUE_STATE_DIR_PATTERN = new RegExp('(^|/)\\.' + 'beads' + '(/|$)');
45
+ const PROTECTED_PATTERNS = [
46
+ /(^|\/)\.git(\/|$)/,
47
+ /(^|\/)\.forge(\/|$)/,
48
+ ISSUE_STATE_DIR_PATTERN,
49
+ /(^|\/)AGENTS\.md$/i,
50
+ /(^|\/)lefthook\.ya?ml$/i,
51
+ /(^|\/)\.env(\.[^/]+)?$/i,
52
+ /(^|\/)(package-lock\.json|yarn\.lock|pnpm-lock\.yaml|bun\.lock[b]?)$/i,
53
+ ];
54
+
55
+ /** Parse `--intent <id> --harness <id>` from an argv slice. */
56
+ function parseArgs(argv) {
57
+ const out = { intent: null, harness: null };
58
+ for (let i = 0; i < argv.length; i += 1) {
59
+ if (argv[i] === '--intent') out.intent = argv[i + 1];
60
+ else if (argv[i] === '--harness') out.harness = argv[i + 1];
61
+ }
62
+ return out;
63
+ }
64
+
65
+ /** Extract the target file path from a harness hook payload, or null. */
66
+ function extractPath(input) {
67
+ if (!input || typeof input !== 'object') return null;
68
+ const ti = input.tool_input && typeof input.tool_input === 'object' ? input.tool_input : {};
69
+ // ti.path covers Hermes write_file/patch payloads ({ tool_input: { path } }); the
70
+ // others cover Claude (file_path/notebook_path) and flat Cursor payloads.
71
+ const candidate = ti.file_path || ti.notebook_path || ti.path || input.file_path || null;
72
+ return typeof candidate === 'string' && candidate.length ? candidate : null;
73
+ }
74
+
75
+ /** Extract the shell command from a harness hook payload, or null. */
76
+ function extractCommand(input) {
77
+ if (!input || typeof input !== 'object') return null;
78
+ const ti = input.tool_input && typeof input.tool_input === 'object' ? input.tool_input : {};
79
+ const candidate = ti.command || input.command || null;
80
+ return typeof candidate === 'string' && candidate.length ? candidate : null;
81
+ }
82
+
83
+ function normalize(p) {
84
+ return String(p).replace(/\\/g, '/').replace(/^\.\//, '');
85
+ }
86
+
87
+ /** True when a path falls inside Forge's protected set. */
88
+ function isProtectedPath(p) {
89
+ if (!p) return false;
90
+ const n = normalize(p);
91
+ return PROTECTED_PATTERNS.some(re => re.test(n));
92
+ }
93
+
94
+ // Write-intent shell constructs: mutating commands and output redirection. Reads
95
+ // (cat/ls/grep) of protected paths stay allowed — the protected-path intent guards
96
+ // WRITES, mirroring the Claude wiring (PreToolUse matcher Write|Edit, not Read).
97
+ const WRITE_INTENT_RE = /(^|[\s;|&(])(rm|mv|cp|tee|truncate|chmod|chown|ln|sed|perl|dd|install|rsync|unlink|shred)\b|>>?/;
98
+
99
+ /**
100
+ * True when a SHELL COMMAND shows WRITE intent toward a Forge-protected path.
101
+ * Needed because Cursor's only deny-capable surface is `beforeShellExecution`,
102
+ * whose payload carries `command` (no file_path) — without this check the
103
+ * protected-path intent could never actually block anything on Cursor.
104
+ * Conservative two-step: (1) the command must contain a mutating construct
105
+ * (rm/mv/sed/tee/redirection/...), then (2) token-scan (split on whitespace +
106
+ * shell operators, strip quotes) for a protected path. Never throws.
107
+ */
108
+ function commandTouchesProtectedPath(command) {
109
+ if (typeof command !== 'string' || !command) return false;
110
+ if (!WRITE_INTENT_RE.test(command)) return false;
111
+ const tokens = command.split(/[\s;|&<>()]+/);
112
+ for (const raw of tokens) {
113
+ const token = raw.replace(/^["']+|["']+$/g, '');
114
+ if (token && !token.startsWith('-') && isProtectedPath(token)) return true;
115
+ }
116
+ return false;
117
+ }
118
+
119
+ /** True when a shell command invokes `git commit`. */
120
+ function isGitCommit(command) {
121
+ return typeof command === 'string' && /\bgit\b[^\n&|;]*\bcommit\b/.test(command);
122
+ }
123
+
124
+ /** Run the installed TDD check; returns its exit code (0 = pass, non-zero = block). */
125
+ function runInstalledTddCheck() {
126
+ try {
127
+ execFileSync('node', [path.join(__dirname, 'check-tdd.js')], {
128
+ stdio: ['ignore', 'ignore', 'ignore'],
129
+ });
130
+ return 0;
131
+ } catch (err) {
132
+ return typeof err.status === 'number' ? err.status : 1;
133
+ }
134
+ }
135
+
136
+ /**
137
+ * Core enforcement decision. `runTddCheck` is injectable for deterministic tests.
138
+ * @returns {{ decision: 'allow'|'deny', reason?: string }}
139
+ */
140
+ function decide({ intent, input, runTddCheck = runInstalledTddCheck }) {
141
+ if (intent === 'protected-path') {
142
+ const target = extractPath(input);
143
+ if (isProtectedPath(target)) {
144
+ return {
145
+ decision: 'deny',
146
+ reason: `Forge-protected path '${normalize(target)}' — edit it through the owning Forge CLI/skill, not a raw write.`,
147
+ };
148
+ }
149
+ // No file path in the payload (e.g. Cursor beforeShellExecution carries only
150
+ // `command`): inspect the shell command for write intent on a protected path,
151
+ // so the deny-capable shell surface actually protects instead of no-oping.
152
+ if (!target) {
153
+ const command = extractCommand(input);
154
+ if (commandTouchesProtectedPath(command)) {
155
+ return {
156
+ decision: 'deny',
157
+ reason: 'Shell command writes to a Forge-protected path — use the owning Forge CLI/skill instead.',
158
+ };
159
+ }
160
+ }
161
+ return { decision: 'allow' };
162
+ }
163
+
164
+ if (intent === 'tdd-gate') {
165
+ const command = extractCommand(input);
166
+ if (!isGitCommit(command)) return { decision: 'allow' };
167
+ const code = runTddCheck();
168
+ if (code !== 0) {
169
+ return {
170
+ decision: 'deny',
171
+ reason: 'TDD gate: staged source files are missing accompanying tests. Write tests first (RED-GREEN-REFACTOR).',
172
+ };
173
+ }
174
+ return { decision: 'allow' };
175
+ }
176
+
177
+ // Unknown intent: never block.
178
+ return { decision: 'allow' };
179
+ }
180
+
181
+ /** Serialize a decision into the harness-native hook output contract. */
182
+ function formatOutput(harness, decision, _reason) {
183
+ const reason = decision.reason || '';
184
+ if (decision.decision !== 'deny') {
185
+ // Allow: for Claude, emit nothing so we never auto-approve past user settings.
186
+ return harness === 'cursor'
187
+ ? JSON.stringify({ continue: true, permission: 'allow' })
188
+ : '';
189
+ }
190
+ if (harness === 'cursor') {
191
+ return JSON.stringify({
192
+ continue: true,
193
+ permission: 'deny',
194
+ agentMessage: reason,
195
+ userMessage: reason,
196
+ });
197
+ }
198
+ if (harness === 'hermes') {
199
+ // Hermes shell-hook JSON wire protocol: block a pre_tool_call by writing
200
+ // { action: 'block', message } to stdout (empty output = silent allow).
201
+ return JSON.stringify({ action: 'block', message: reason });
202
+ }
203
+ // Claude (and any harness modeled on it).
204
+ return JSON.stringify({
205
+ hookSpecificOutput: {
206
+ hookEventName: 'PreToolUse',
207
+ permissionDecision: 'deny',
208
+ permissionDecisionReason: reason,
209
+ },
210
+ });
211
+ }
212
+
213
+ function readStdin() {
214
+ try {
215
+ const raw = require('node:fs').readFileSync(0, 'utf-8');
216
+ return raw && raw.trim() ? JSON.parse(raw) : {};
217
+ } catch {
218
+ return {};
219
+ }
220
+ }
221
+
222
+ function main() {
223
+ const { intent, harness } = parseArgs(process.argv.slice(2));
224
+ const input = readStdin();
225
+ const decision = decide({ intent, input });
226
+ const output = formatOutput(harness || 'claude', decision);
227
+ if (output) process.stdout.write(output);
228
+ // Exit 0 always: the decision travels in the JSON body, not the exit code, so a
229
+ // non-deny outcome leaves the harness's normal permission flow untouched.
230
+ process.exit(0);
231
+ }
232
+
233
+ module.exports = {
234
+ PROTECTED_PATTERNS,
235
+ parseArgs,
236
+ extractPath,
237
+ extractCommand,
238
+ isProtectedPath,
239
+ commandTouchesProtectedPath,
240
+ isGitCommit,
241
+ decide,
242
+ formatOutput,
243
+ };
244
+
245
+ if (require.main === module) main();