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,484 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * PR-state adapter (`kind: 'pr-state'`).
5
+ *
6
+ * Wraps read-only GitHub/git inspection plus a small set of idempotent,
7
+ * reversible side-effects used by the PR shepherd:
8
+ * - `readState` → `gh pr view --json ...`
9
+ * - `readRequiredChecks` → `gh api repos/{o}/{r}/branches/{base}/protection/required_status_checks`
10
+ * - `readDivergence` → `git rev-list --left-right --count {baseRef}...HEAD`
11
+ * - `detectConflicts` → `git merge-tree --write-tree {baseRef} HEAD` (predict-only)
12
+ * - `rerunFailedChecks` → `gh run rerun <id> --failed`
13
+ * - `replyToThread` → shell-out to `.claude/scripts/review-resolve.sh reply` (reply ONLY,
14
+ * never resolve — resolution stays with the semantic `/review` agent)
15
+ *
16
+ * This adapter is its own SPI; it does NOT extend the review adapter and is
17
+ * validated by `validatePrStateAdapter` (lib/pr-state-validator.js).
18
+ *
19
+ * It contains no merge or rebase machinery. Divergence handling (rebase) lives
20
+ * in the core/CLI behind an opt-in flag and is injected as `rebaseOntoBase`
21
+ * when enabled — it is never a default capability of this read surface.
22
+ *
23
+ * @module adapters/pr-state-adapter
24
+ */
25
+
26
+ const { execFileSync } = require('node:child_process');
27
+
28
+ // NOTE: `reviewThreads` is NOT a valid `gh pr view --json` field — requesting it
29
+ // makes `gh` exit non-zero ("Unknown JSON field"), which crashed readState on every
30
+ // real PR. Review threads are read separately via GraphQL in readComments().
31
+ const PR_VIEW_FIELDS = [
32
+ 'headRefOid',
33
+ 'mergeable',
34
+ 'mergeStateStatus',
35
+ 'state',
36
+ 'statusCheckRollup',
37
+ // `reviewDecision` (REVIEW_REQUIRED / CHANGES_REQUESTED / APPROVED / '') and
38
+ // `isDraft` are PR-level MERGE BLOCKERS the pull-signal payload surfaces — a
39
+ // draft PR or a missing/negative review decision blocks merge even when every
40
+ // check is green. Both are valid `gh pr view --json` fields.
41
+ 'reviewDecision',
42
+ 'isDraft',
43
+ ].join(',');
44
+
45
+ /**
46
+ * Classify an error thrown by the `gh` runner so the core can react.
47
+ * Returns `null` when the error is not auth/rate related.
48
+ *
49
+ * @param {Error} error
50
+ * @returns {{ class: string, retryAfter?: number } | null}
51
+ */
52
+ function classifyAuthError(error) {
53
+ if (!error) return null;
54
+ const status = error.httpStatus
55
+ || (typeof error.status === 'number' ? error.status : undefined);
56
+ const text = `${error.stderr || ''} ${error.message || ''}`;
57
+ const retryAfter = Number(error.retryAfter) || undefined;
58
+
59
+ if (status === 401 || /HTTP 401|bad credentials|token expired/i.test(text)) {
60
+ return { class: 'expired' };
61
+ }
62
+ if (status === 403 || /HTTP 403/i.test(text)) {
63
+ if (retryAfter || /rate limit|secondary rate/i.test(text)) {
64
+ return { class: 'rate-limit', retryAfter };
65
+ }
66
+ return { class: 'insufficient-scope' };
67
+ }
68
+ return null;
69
+ }
70
+
71
+ class PrStateAdapter {
72
+ /**
73
+ * @param {object} [options]
74
+ * @param {Function} [options.gh] - Runner for `gh` (cmd, args[]) → string.
75
+ * @param {Function} [options.git] - Runner for `git` (cmd, args[]) → string.
76
+ */
77
+ constructor(options = {}) {
78
+ this.id = options.id || 'pr-state-adapter';
79
+ this.kind = 'pr-state';
80
+ this.name = options.name || this.id;
81
+ const defaultRunner = (cmd, args, opts = {}) => execFileSync(cmd, args, {
82
+ encoding: 'utf8',
83
+ timeout: options.timeout || 30000,
84
+ ...(opts.cwd ? { cwd: opts.cwd } : {}),
85
+ });
86
+ this._gh = options.gh || defaultRunner;
87
+ this._git = options.git || defaultRunner;
88
+ }
89
+
90
+ /**
91
+ * Read normalized PR/CI state.
92
+ *
93
+ * @param {string} pr - PR number or URL.
94
+ * @returns {Promise<{ headSha: string, mergeable: string, mergeStateStatus: string, checks: object[], threads: object[] }>}
95
+ */
96
+ async readState(pr) {
97
+ const raw = this._gh('gh', ['pr', 'view', String(pr), '--json', PR_VIEW_FIELDS]);
98
+ const data = JSON.parse(raw || '{}');
99
+ const rollup = Array.isArray(data.statusCheckRollup) ? data.statusCheckRollup : [];
100
+ return {
101
+ headSha: data.headRefOid || '',
102
+ state: String(data.state || 'OPEN').toUpperCase(),
103
+ mergeable: data.mergeable || 'UNKNOWN',
104
+ mergeStateStatus: data.mergeStateStatus || 'UNKNOWN',
105
+ // PR-level merge blockers (surfaced by the pull signal). `reviewDecision` is
106
+ // '' when no review is required; normalized to null so consumers can treat
107
+ // "not required" and "unknown" uniformly. `isDraft` blocks merge outright.
108
+ reviewDecision: data.reviewDecision || null,
109
+ isDraft: Boolean(data.isDraft),
110
+ // The rollup mixes two GraphQL types: a CheckRun (name/status/conclusion/
111
+ // detailsUrl) and a legacy commit StatusContext (context/state/targetUrl —
112
+ // Vercel/Netlify/other deploy+quality bots). Both are normalized into ONE
113
+ // shape here so the failing/pending classification treats them identically;
114
+ // `state` fills the conclusion slot for a StatusContext (SUCCESS/FAILURE/
115
+ // ERROR/PENDING) and `targetUrl` fills the details link.
116
+ checks: rollup.map((check) => ({
117
+ name: check.name || check.context || '',
118
+ status: check.status || check.state || '',
119
+ conclusion: check.conclusion || check.state || '',
120
+ databaseId: check.databaseId,
121
+ detailsUrl: check.detailsUrl || check.targetUrl,
122
+ })),
123
+ // gh pr view cannot return review threads; the shepherd reads them via
124
+ // readComments() (GraphQL). Kept for return-shape stability, always empty here.
125
+ threads: [],
126
+ };
127
+ }
128
+
129
+ /**
130
+ * Read the branch-protection required-checks set.
131
+ *
132
+ * Returns `null` when the protection endpoint is unreadable (e.g. 403
133
+ * insufficient scope, the branch is not protected, or the payload shape is
134
+ * unexpected) so the caller can escalate rather than guess. Re-throws non-auth
135
+ * errors.
136
+ *
137
+ * @param {{ owner: string, repo: string, base: string }} ctx
138
+ * @returns {Promise<string[] | null>}
139
+ */
140
+ async readRequiredChecks({ owner, repo, base }) {
141
+ const apiPath = `repos/${owner}/${repo}/branches/${encodeURIComponent(base)}/protection/required_status_checks`;
142
+ try {
143
+ const raw = this._gh('gh', ['api', apiPath]);
144
+ const data = JSON.parse(raw || '{}');
145
+ if (Array.isArray(data.contexts)) return data.contexts;
146
+ if (Array.isArray(data.checks)) return data.checks.map((c) => c.context).filter(Boolean);
147
+ // Unexpected/changed payload shape — treat as unreadable, not "no required
148
+ // checks", so merge readiness is never computed from bad data.
149
+ return null;
150
+ } catch (error) {
151
+ const auth = classifyAuthError(error);
152
+ if (auth) {
153
+ // Unreadable protection (auth/scope/not-protected) — caller escalates.
154
+ return null;
155
+ }
156
+ throw error;
157
+ }
158
+ }
159
+
160
+ /**
161
+ * Read ahead/behind divergence against the base ref.
162
+ *
163
+ * `cwd` is threaded through to the git runner so divergence is computed
164
+ * against the target worktree/checkout, not the process directory.
165
+ *
166
+ * @param {{ baseRef: string, cwd?: string }} ctx
167
+ * @returns {Promise<{ behind: number, ahead: number }>}
168
+ */
169
+ async readDivergence({ baseRef, cwd }) {
170
+ const out = this._git(
171
+ 'git',
172
+ ['rev-list', '--left-right', '--count', `${baseRef}...HEAD`],
173
+ cwd ? { cwd } : undefined,
174
+ );
175
+ const [behindRaw = '0', aheadRaw = '0'] = String(out).trim().split(/\s+/);
176
+ return {
177
+ behind: Number.parseInt(behindRaw, 10) || 0,
178
+ ahead: Number.parseInt(aheadRaw, 10) || 0,
179
+ };
180
+ }
181
+
182
+ /**
183
+ * Re-run failed CI for a workflow run (Tier-A: idempotent, reversible).
184
+ *
185
+ * @param {{ runId: string }} ctx
186
+ * @returns {Promise<void>}
187
+ */
188
+ async rerunFailedChecks({ runId }) {
189
+ this._gh('gh', ['run', 'rerun', String(runId), '--failed']);
190
+ }
191
+
192
+ /**
193
+ * Fetch the base ref from its remote so a subsequent `readDivergence`/
194
+ * `detectConflicts` compares HEAD against the CURRENT `origin/<base>`, not a
195
+ * stale local remote-tracking ref (audit A6: a stale ref reports a false
196
+ * `behind=0` / false "no conflict"). `baseRef` is `<remote>/<branch>` (e.g.
197
+ * `origin/master`); the remote and branch are split back out for `git fetch`.
198
+ * Read-only against the working tree — it only updates remote-tracking refs.
199
+ *
200
+ * @param {{ baseRef: string, cwd?: string }} ctx
201
+ * @returns {Promise<void>}
202
+ */
203
+ async fetchBase({ baseRef, cwd }) {
204
+ const ref = String(baseRef || '');
205
+ const slash = ref.indexOf('/');
206
+ // No `<remote>/<branch>` shape → nothing safe to fetch; leave refs as-is.
207
+ if (slash <= 0) return;
208
+ const remote = ref.slice(0, slash);
209
+ const branch = ref.slice(slash + 1);
210
+ this._git('git', ['fetch', remote, branch], cwd ? { cwd } : undefined);
211
+ }
212
+
213
+ /**
214
+ * Post a status reply to a review thread via the existing shell helper.
215
+ * Reply ONLY — never resolve (resolution is the semantic `/review` agent's job).
216
+ *
217
+ * @param {{ pr: string, commentId: string, message: string, script?: string }} ctx
218
+ * @returns {Promise<void>}
219
+ */
220
+ async replyToThread({ pr, commentId, message, script }) {
221
+ const scriptPath = script || '.claude/scripts/review-resolve.sh';
222
+ this._gh('bash', [scriptPath, 'reply', String(pr), String(commentId), String(message)]);
223
+ }
224
+
225
+ /**
226
+ * Read review threads as actionable comments. Uses GraphQL because
227
+ * `gh pr view --json reviewThreads` is unsupported. Resolved/outdated threads
228
+ * are returned WITH flags so the core can filter them (and exclude bots/self).
229
+ *
230
+ * @param {{ owner: string, repo: string, pr: string }} ctx
231
+ * @returns {Promise<object[]>}
232
+ */
233
+ async readComments({ owner, repo, pr }) {
234
+ // `id`/`path`/`line` are surfaced so a consumer (e.g. the monitor bundle in
235
+ // lib/pr-bundle.js) can hand an agent the thread id to resolve and the
236
+ // file/line to act on — not just the body. Added fields are backward
237
+ // compatible; the shepherd's existing thread filtering ignores them.
238
+ //
239
+ // Both connections are FULLY paginated (cursors, not a first:100 cap): a
240
+ // large PR must never silently drop a thread or a later reply, or the bundle
241
+ // would declare "complete" on partial data and the monitor would skip work.
242
+ const threads = this._fetchAllReviewThreads({ owner, repo, pr });
243
+ return threads.map((t) => {
244
+ const allComments = (t.comments && t.comments.nodes) || [];
245
+ return {
246
+ threadId: t.id || '',
247
+ path: t.path || null,
248
+ line: typeof t.line === 'number' ? t.line : null,
249
+ isResolved: !!t.isResolved,
250
+ isOutdated: !!t.isOutdated,
251
+ comments: allComments.map(c => ({
252
+ author: (c.author && c.author.login) || '',
253
+ body: c.body || '',
254
+ // REST comment id (needed to REPLY to the thread). `fullDatabaseId` is
255
+ // the string-encoded integer id; null when GitHub omits it.
256
+ commentId: c.fullDatabaseId ? String(c.fullDatabaseId) : null,
257
+ })),
258
+ };
259
+ });
260
+ }
261
+
262
+ /**
263
+ * Page through ALL `reviewThreads` (and each thread's nested `comments`) using
264
+ * GraphQL cursors. The per-page size stays 100, but pagination continues until
265
+ * `hasNextPage` is false, so coverage no longer caps at 100 threads/comments.
266
+ * A guard bounds the loop against a pathological/looping cursor.
267
+ *
268
+ * @param {{ owner: string, repo: string, pr: string }} ctx
269
+ * @returns {object[]} raw thread nodes (comments.nodes carries the full chain)
270
+ */
271
+ _fetchAllReviewThreads({ owner, repo, pr }) {
272
+ const query = 'query($o:String!,$n:String!,$pr:Int!,$after:String){repository(owner:$o,name:$n){pullRequest(number:$pr){reviewThreads(first:100,after:$after){pageInfo{hasNextPage endCursor} nodes{id isResolved isOutdated path line comments(first:100){pageInfo{hasNextPage endCursor} nodes{fullDatabaseId author{login} body}}}}}}}';
273
+ return this._paginateConnection(
274
+ (after) => this._ghGraphqlPage(query, { owner, repo, pr }, after)
275
+ ?.data?.repository?.pullRequest?.reviewThreads,
276
+ (t) => {
277
+ // A thread whose first comment page is truncated → fetch the rest.
278
+ if (t.comments?.pageInfo?.hasNextPage) {
279
+ t.comments = { nodes: this._fetchAllThreadComments(t.id, t.comments) };
280
+ }
281
+ return t;
282
+ },
283
+ );
284
+ }
285
+
286
+ /**
287
+ * Run ONE `gh api graphql` page request for an owner/repo/pr-shaped query and
288
+ * return the parsed JSON (or `{}`). `after` appends the opaque cursor only when
289
+ * present — omitting it on the first page tells GraphQL to start from the top.
290
+ * Extracted so the arg-building scaffold lives in exactly one place.
291
+ *
292
+ * @param {string} query
293
+ * @param {{ owner: string, repo: string, pr: string }} vars
294
+ * @param {string|null} after
295
+ * @returns {object}
296
+ */
297
+ _ghGraphqlPage(query, { owner, repo, pr }, after) {
298
+ const args = [
299
+ 'api', 'graphql', '-f', `query=${query}`,
300
+ '-F', `o=${owner}`, '-F', `n=${repo}`, '-F', `pr=${pr}`,
301
+ ];
302
+ if (after) args.push('-f', `after=${after}`);
303
+ return JSON.parse(this._gh('gh', args) || '{}');
304
+ }
305
+
306
+ /**
307
+ * Page through a cursored GraphQL connection until `hasNextPage` is false,
308
+ * accumulating `mapNode(node)` for every node across all pages. `runPage(after)`
309
+ * performs one page request and returns the connection object
310
+ * (`{ pageInfo, nodes }`) or a falsy value when unreadable. A page guard bounds
311
+ * the loop against a pathological/looping cursor. Shared by every paginated
312
+ * read so the do-while cursor scaffold is defined exactly once.
313
+ *
314
+ * @param {(after: string|null) => ({ pageInfo?: object, nodes?: object[] }|null|undefined)} runPage
315
+ * @param {(node: object) => object} mapNode
316
+ * @returns {object[]}
317
+ */
318
+ _paginateConnection(runPage, mapNode) {
319
+ const all = [];
320
+ let after = null;
321
+ let guard = 0;
322
+ const MAX_PAGES = 1000;
323
+ do {
324
+ const conn = runPage(after);
325
+ for (const node of (conn?.nodes || [])) all.push(mapNode(node));
326
+ after = conn?.pageInfo?.hasNextPage ? conn.pageInfo.endCursor : null;
327
+ guard += 1;
328
+ } while (after && guard < MAX_PAGES);
329
+ return all;
330
+ }
331
+
332
+ /**
333
+ * Page through the remaining `comments` of a single review thread (the first
334
+ * page already arrived inline with the thread). Resolves the thread by node id.
335
+ *
336
+ * @param {string} threadId
337
+ * @param {{ nodes: object[], pageInfo: { hasNextPage: boolean, endCursor: string } }} initial
338
+ * @returns {object[]} the full comment-node list (first page + all later pages)
339
+ */
340
+ _fetchAllThreadComments(threadId, initial) {
341
+ const query = 'query($id:ID!,$after:String){node(id:$id){... on PullRequestReviewThread{comments(first:100,after:$after){pageInfo{hasNextPage endCursor} nodes{fullDatabaseId author{login} body}}}}}';
342
+ const acc = [...((initial && initial.nodes) || [])];
343
+ let after = initial && initial.pageInfo && initial.pageInfo.hasNextPage ? initial.pageInfo.endCursor : null;
344
+ let guard = 0;
345
+ const MAX_PAGES = 1000;
346
+ while (after && guard < MAX_PAGES) {
347
+ const raw = this._gh('gh', [
348
+ 'api', 'graphql', '-f', `query=${query}`,
349
+ '-f', `id=${threadId}`, '-f', `after=${after}`,
350
+ ]);
351
+ const data = JSON.parse(raw || '{}');
352
+ const conn = data.data && data.data.node && data.data.node.comments;
353
+ acc.push(...((conn && conn.nodes) || []));
354
+ after = conn && conn.pageInfo && conn.pageInfo.hasNextPage ? conn.pageInfo.endCursor : null;
355
+ guard += 1;
356
+ }
357
+ return acc;
358
+ }
359
+
360
+ /**
361
+ * Read the PR's plain ISSUE comments (NOT review threads) — the surface where
362
+ * status/deploy/quality bots post their summaries: SonarCloud's Quality-Gate
363
+ * comment, Vercel/Netlify deployment comments, Codecov coverage comments. These
364
+ * are regular PR comments, never resolvable review threads, so `readComments`
365
+ * (reviewThreads GraphQL) never sees them. Fully paginated by cursor so a long
366
+ * PR never drops a bot's latest comment. Returns `{ author, body, createdAt }`.
367
+ *
368
+ * @param {{ owner: string, repo: string, pr: string }} ctx
369
+ * @returns {Promise<Array<{ author: string, body: string, createdAt: string }>>}
370
+ */
371
+ async readIssueComments({ owner, repo, pr }) {
372
+ // `author{__typename login}` surfaces the GraphQL actor TYPE ('Bot' vs 'User')
373
+ // so a non-human direct comment can be detected GENERICALLY — by mechanism,
374
+ // not by a hardcoded bot-name list — which fails closed for unknown bots.
375
+ const query = 'query($o:String!,$n:String!,$pr:Int!,$after:String){repository(owner:$o,name:$n){pullRequest(number:$pr){comments(first:100,after:$after){pageInfo{hasNextPage endCursor} nodes{fullDatabaseId author{__typename login} body createdAt}}}}}';
376
+ return this._paginateConnection(
377
+ (after) => this._ghGraphqlPage(query, { owner, repo, pr }, after)
378
+ ?.data?.repository?.pullRequest?.comments,
379
+ (c) => ({
380
+ // `id` is the stable REST comment id — the PR monitor keys `comment.posted`
381
+ // on it so a re-read never re-emits an already-seen comment. Falls back to
382
+ // author+createdAt when GitHub omits it.
383
+ id: c.fullDatabaseId ? String(c.fullDatabaseId) : `${c.author?.login || ''}:${c.createdAt || ''}`,
384
+ author: c.author?.login || '',
385
+ authorTypename: c.author?.__typename || '',
386
+ body: c.body || '',
387
+ createdAt: c.createdAt || '',
388
+ }),
389
+ );
390
+ }
391
+
392
+ /**
393
+ * Read submitted PR REVIEWS (not inline threads) — the latest review per
394
+ * author, each with the commit it was submitted against. This is the
395
+ * review-at-head signal that catches the #365 race: a CodeRabbit review from
396
+ * an earlier commit whose `commit.oid` no longer matches HEAD is STALE, so the
397
+ * post-push re-review is still pending. Uses GraphQL because `gh pr view`
398
+ * cannot return a review's target commit oid. Fully paginated by cursor.
399
+ *
400
+ * @param {{ owner: string, repo: string, pr: string }} ctx
401
+ * @returns {Promise<Array<{ author: string, state: string, submittedAt: string|null, commitOid: string|null, body: string }>>}
402
+ */
403
+ async readReviews({ owner, repo, pr }) {
404
+ const query = 'query($o:String!,$n:String!,$pr:Int!,$after:String){repository(owner:$o,name:$n){pullRequest(number:$pr){reviews(first:100,after:$after){pageInfo{hasNextPage endCursor} nodes{author{__typename login} state submittedAt commit{oid} body}}}}}';
405
+ const all = this._paginateConnection(
406
+ (after) => this._ghGraphqlPage(query, { owner, repo, pr }, after)
407
+ ?.data?.repository?.pullRequest?.reviews,
408
+ (r) => ({
409
+ author: String(r.author?.login || '').toLowerCase(),
410
+ authorTypename: r.author?.__typename || '',
411
+ state: String(r.state || '').toUpperCase(),
412
+ submittedAt: r.submittedAt || null,
413
+ commitOid: r.commit?.oid || null,
414
+ body: r.body || '',
415
+ }),
416
+ );
417
+ // Keep only the LATEST review per author (first:100 yields oldest→newest, so
418
+ // a later entry supersedes an earlier one from the same login).
419
+ const latest = new Map();
420
+ for (const r of all) {
421
+ if (!r.author) continue;
422
+ latest.set(r.author, r);
423
+ }
424
+ return Array.from(latest.values());
425
+ }
426
+
427
+ /**
428
+ * Read the HEAD commit's committed timestamp (epoch ms), or null when
429
+ * unreadable. Anchors the pull-signal settle window to when the code last
430
+ * changed — so a freshly-pushed PR whose CI passes BEFORE the review bots have
431
+ * even run is REVIEW-PENDING, not CLEAN (#365 "never-ran" variant), and a bot
432
+ * comment older than the last push is treated as stale, not a live blocker.
433
+ *
434
+ * @param {{ pr: string }} ctx
435
+ * @returns {Promise<number|null>} epoch ms of the head commit, or null.
436
+ */
437
+ async readHeadCommitTime({ pr }) {
438
+ const raw = this._gh('gh', ['pr', 'view', String(pr), '--json', 'commits', '-q', '.commits[-1].committedDate']);
439
+ const t = Date.parse(String(raw || '').trim());
440
+ return Number.isFinite(t) ? t : null;
441
+ }
442
+
443
+ /**
444
+ * Predict files that would conflict when merging `baseRef` into HEAD WITHOUT
445
+ * touching the working tree, via `git merge-tree --write-tree`. Returns
446
+ * `{ supported: false, reason }` when conflict prediction is unavailable (git
447
+ * < 2.38, an unreadable ref, or a non-conflict error) so the bundle degrades
448
+ * gracefully rather than failing the whole gather.
449
+ *
450
+ * @param {{ baseRef: string, cwd?: string }} ctx
451
+ * @returns {Promise<{ supported: boolean, conflicted?: boolean, files?: string[], reason?: string }>}
452
+ */
453
+ async detectConflicts({ baseRef, cwd }) {
454
+ const opts = cwd ? { cwd } : undefined;
455
+ try {
456
+ // Exit 0 = clean merge. `--name-only` reduces the conflict report to bare
457
+ // paths; `--no-messages` suppresses the human-readable conflict prose.
458
+ this._git(
459
+ 'git',
460
+ ['merge-tree', '--write-tree', '--name-only', '--no-messages', baseRef, 'HEAD'],
461
+ opts,
462
+ );
463
+ return { supported: true, conflicted: false, files: [] };
464
+ } catch (error) {
465
+ // Exit 1 = mergeable-with-conflicts: stdout is the written tree OID on the
466
+ // first line followed by the conflicted paths. Any other status (e.g. 128
467
+ // for an unknown ref, or `--write-tree` unsupported on git < 2.38) means
468
+ // conflict prediction is unsupported in this environment.
469
+ const status = typeof error.status === 'number' ? error.status : undefined;
470
+ const stdout = String(error.stdout || '');
471
+ if (status === 1 && stdout.trim()) {
472
+ const lines = stdout.split('\n').map((s) => s.trim()).filter(Boolean);
473
+ return { supported: true, conflicted: true, files: lines.slice(1) };
474
+ }
475
+ return { supported: false, reason: error.message || 'git merge-tree unavailable' };
476
+ }
477
+ }
478
+ }
479
+
480
+ module.exports = {
481
+ PrStateAdapter,
482
+ classifyAuthError,
483
+ PR_VIEW_FIELDS,
484
+ };
@@ -0,0 +1,126 @@
1
+ const YAML = require('yaml');
2
+
3
+ const ADOPTION_VERSION = '0.0.15';
4
+ const RUNTIME_ANCESTRY = 'forge.runtimeGraph.currentCommandFlow@0.0.17';
5
+ const WORKFLOW_PHASES = Object.freeze(['plan', 'dev', 'validate', 'ship']);
6
+ const WORKFLOW_GATES = Object.freeze(['gate.plan-exit', 'gate.dev-exit', 'gate.validate-exit', 'gate.ship-entry']);
7
+
8
+ function enabledEntries(names, enabled = true) {
9
+ return Object.fromEntries(names.map(name => [name, { enabled }]));
10
+ }
11
+
12
+ function issueAdapter(enabled) {
13
+ if (!enabled) {
14
+ return {
15
+ enabled: false,
16
+ primary: 'none',
17
+ mirrors: [],
18
+ };
19
+ }
20
+
21
+ return {
22
+ enabled: true,
23
+ primary: 'kernel',
24
+ mirrors: ['github'],
25
+ };
26
+ }
27
+
28
+ function harnessAdapter(targets) {
29
+ return {
30
+ enabled: targets.length > 0,
31
+ targets,
32
+ };
33
+ }
34
+
35
+ function adoptionConfig({ gatesEnabled, issueEnabled, harnessTargets, protectedPaths, rails }) {
36
+ return {
37
+ ...(rails ? { rails } : {}),
38
+ workflow: {
39
+ phases: enabledEntries(WORKFLOW_PHASES),
40
+ gates: enabledEntries(WORKFLOW_GATES, gatesEnabled),
41
+ },
42
+ adapters: {
43
+ issue: issueAdapter(issueEnabled),
44
+ harness: harnessAdapter(harnessTargets),
45
+ },
46
+ protectedPaths,
47
+ };
48
+ }
49
+
50
+ const PROFILE_CONFIGS = Object.freeze({
51
+ minimal: {
52
+ description: 'Config-only adoption for a clean repository.',
53
+ config: adoptionConfig({
54
+ gatesEnabled: false,
55
+ issueEnabled: false,
56
+ harnessTargets: [],
57
+ protectedPaths: ['.forge/config.yaml'],
58
+ }),
59
+ },
60
+ standard: {
61
+ description: 'Default Forge command flow with Kernel issue tracking and GitHub mirror metadata.',
62
+ config: adoptionConfig({
63
+ gatesEnabled: true,
64
+ issueEnabled: true,
65
+ harnessTargets: ['codex', 'claude', 'cursor'],
66
+ protectedPaths: ['.forge/config.yaml', 'AGENTS.md'],
67
+ }),
68
+ },
69
+ full: {
70
+ description: 'Full adoption scaffold with all default rails explicit.',
71
+ config: adoptionConfig({
72
+ rails: {
73
+ tdd_intent: { enabled: true },
74
+ secret_scan: { enabled: true },
75
+ branch_protection: { enabled: true },
76
+ signed_commits: { enabled: true },
77
+ schema_integrity: { enabled: true },
78
+ },
79
+ gatesEnabled: true,
80
+ issueEnabled: true,
81
+ harnessTargets: ['codex', 'claude', 'cursor'],
82
+ protectedPaths: ['.forge/config.yaml', 'AGENTS.md', '.github/workflows/**'],
83
+ }),
84
+ },
85
+ });
86
+
87
+ const ADOPTION_PROFILE_NAMES = Object.freeze(Object.keys(PROFILE_CONFIGS));
88
+
89
+ function clone(value) {
90
+ return structuredClone(value);
91
+ }
92
+
93
+ function assertProfile(profile) {
94
+ if (!Object.hasOwn(PROFILE_CONFIGS, profile)) {
95
+ throw new Error(`Unknown adoption profile '${profile}'. Available profiles: ${ADOPTION_PROFILE_NAMES.join(', ')}`);
96
+ }
97
+ }
98
+
99
+ function buildAdoptionConfig(profile = 'standard') {
100
+ assertProfile(profile);
101
+ const definition = PROFILE_CONFIGS[profile];
102
+ return {
103
+ template: {
104
+ kind: 'forge.adoptionTemplate',
105
+ version: ADOPTION_VERSION,
106
+ profile,
107
+ description: definition.description,
108
+ ancestry: [
109
+ RUNTIME_ANCESTRY,
110
+ `forge.profile.${profile}@${ADOPTION_VERSION}`,
111
+ ],
112
+ },
113
+ ...clone(definition.config),
114
+ };
115
+ }
116
+
117
+ function renderAdoptionConfigYaml(profile = 'standard') {
118
+ return YAML.stringify(buildAdoptionConfig(profile));
119
+ }
120
+
121
+ module.exports = {
122
+ ADOPTION_PROFILE_NAMES,
123
+ ADOPTION_VERSION,
124
+ buildAdoptionConfig,
125
+ renderAdoptionConfigYaml,
126
+ };
@@ -94,17 +94,13 @@ Legacy `capabilities.hooks: true/false` values are still accepted and normalized
94
94
 
95
95
  ## Supported Agents
96
96
 
97
- Currently supported AI coding agents:
97
+ Forge currently supports Claude Code, Codex, and Cursor. Hermes support is planned.
98
98
 
99
99
  | Agent | ID | Description |
100
100
  |-------|----|-----------|
101
101
  | Claude Code | `claude` | Anthropic's CLI agent |
102
+ | Codex | `codex` | OpenAI's CLI agent |
102
103
  | Cursor | `cursor` | AI-first code editor |
103
- | Kilo Code | `kilocode` | VS Code extension |
104
- | GitHub Copilot | `copilot` | GitHub's AI assistant |
105
- | OpenCode | `opencode` | Open-source agent |
106
- | Cline | `cline` | VS Code agent extension |
107
- | Roo Code | `roo` | Cline fork with modes |
108
104
 
109
105
  ## Adding a New Agent
110
106
 
@@ -5,23 +5,18 @@
5
5
  "description": "Anthropic's CLI agent",
6
6
  "homepage": "https://claude.ai/code",
7
7
  "capabilities": {
8
- "commands": true,
8
+ "commands": false,
9
9
  "skills": true,
10
10
  "hooks": true
11
11
  },
12
12
  "directories": {
13
- "commands": ".claude/commands",
14
- "rules": ".claude/rules",
15
- "skills": ".claude/skills/forge-workflow",
13
+ "skills": ".claude/skills",
16
14
  "scripts": ".claude/scripts"
17
15
  },
18
16
  "files": {
19
- "rootConfig": "CLAUDE.md",
20
- "skillDefinition": ".claude/skills/forge-workflow/SKILL.md"
17
+ "rootConfig": "CLAUDE.md"
21
18
  },
22
19
  "setup": {
23
- "copyCommands": true,
24
- "copyRules": true,
25
20
  "copyScripts": true,
26
21
  "createSkill": true
27
22
  }
@@ -7,7 +7,15 @@
7
7
  "capabilities": {
8
8
  "commands": true,
9
9
  "skills": true,
10
- "hooks": false
10
+ "hooks": true,
11
+ "hookEvents": [
12
+ "SessionStart",
13
+ "UserPromptSubmit",
14
+ "PreToolUse",
15
+ "PermissionRequest",
16
+ "PostToolUse",
17
+ "Stop"
18
+ ]
11
19
  },
12
20
  "directories": {
13
21
  "skills": ".codex/skills"