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
@@ -8,12 +8,26 @@ const {
8
8
  getAllowedTransitionsForWorkflowState,
9
9
  } = require('../workflow/state');
10
10
  const { STAGE_IDS } = require('../workflow/stages');
11
+ const { detectWorktree } = require('../detect-worktree');
12
+ const { readStatusSnapshot } = require('../status/snapshot');
13
+ const {
14
+ buildPersonalStatusJson,
15
+ formatRunNextLines,
16
+ formatZeroArgStatus,
17
+ } = require('../status/presenter');
11
18
  const {
12
19
  loadState,
13
20
  extractWorkflowStateFromComments,
14
21
  readWorkflowStateFromBeads,
15
22
  } = require('../workflow/state-manager');
16
-
23
+ const { secureExecFileSync } = require('../shell-utils.js');
24
+
25
+ // Lifecycle progression used for status display. Stage 8 'Merge' is the merge
26
+ // EVENT (a human GitHub action — Forge exposes no `merge` command) that lands after
27
+ // the review stage and before post-merge /verify. Pre-merge doc/CI/handoff checks
28
+ // are an embedded gate inside /ship and /review, not a standalone stage, so this
29
+ // ladder never gains a premerge stage. (AUTHORITATIVE_STAGE_NAMES below still maps
30
+ // the legacy 'premerge' id purely so historical workflow state round-trips.)
17
31
  const WORKFLOW_STAGES = {
18
32
  1: { name: 'Fresh Start', nextCommand: 'research' },
19
33
  2: { name: 'Research', nextCommand: 'research' },
@@ -26,6 +40,10 @@ const WORKFLOW_STAGES = {
26
40
  9: { name: 'Verification', nextCommand: 'verify' },
27
41
  };
28
42
 
43
+ // Legacy read-path name map. `premerge` is retired as a standalone stage (it is now
44
+ // an embedded gate in /ship and /review) but is intentionally KEPT here so a stored
45
+ // `currentStage: 'premerge'` from a schemaVersion 1 workflow still resolves a name
46
+ // on read — deleting it would break upgraders (see test/status-command.test.js).
29
47
  const AUTHORITATIVE_STAGE_NAMES = {
30
48
  plan: 'Planning',
31
49
  dev: 'Development',
@@ -325,10 +343,15 @@ function detectStage(context) {
325
343
 
326
344
  function parseStatusInputs(args = [], flags = {}) {
327
345
  const statusArgs = Array.isArray(args) ? args : [];
346
+ const firstDefined = (...values) => values.find(value => value !== undefined && value !== null);
328
347
  const getNextValue = (flagName) => {
329
348
  const index = statusArgs.indexOf(flagName);
330
349
  if (index !== -1 && index + 1 < statusArgs.length) {
331
- return statusArgs[index + 1];
350
+ const candidate = statusArgs[index + 1];
351
+ if (typeof candidate === 'string' && candidate.startsWith('--')) {
352
+ return null;
353
+ }
354
+ return candidate;
332
355
  }
333
356
  return null;
334
357
  };
@@ -339,13 +362,246 @@ function parseStatusInputs(args = [], flags = {}) {
339
362
  };
340
363
 
341
364
  return {
342
- issueId: flags.issueId || flags['--issue-id'] || getInlineValue('--issue-id') || getNextValue('--issue-id'),
343
- workflowState: flags.workflowState || flags['--workflow-state'] || getInlineValue('--workflow-state') || getNextValue('--workflow-state'),
344
- bdComments: flags.bdComments || flags['--bd-comments'] || getInlineValue('--bd-comments') || getNextValue('--bd-comments'),
345
- projectRoot: flags.projectRoot || flags['--project-root'] || getInlineValue('--project-root') || getNextValue('--project-root') || null,
365
+ issueId: firstDefined(flags.issueId, flags['--issue-id'], getInlineValue('--issue-id'), getNextValue('--issue-id')),
366
+ issueBackend: firstDefined(flags.issueBackend, flags['--issue-backend'], getInlineValue('--issue-backend'), getNextValue('--issue-backend')) || null,
367
+ workflowState: firstDefined(flags.workflowState, flags['--workflow-state'], getInlineValue('--workflow-state'), getNextValue('--workflow-state')),
368
+ bdComments: firstDefined(flags.bdComments, flags['--bd-comments'], getInlineValue('--bd-comments'), getNextValue('--bd-comments')),
369
+ projectRoot: firstDefined(flags.projectRoot, flags['--project-root'], getInlineValue('--project-root'), getNextValue('--project-root')) || null,
370
+ json: flags.json === true || flags['--json'] === true || statusArgs.includes('--json'),
371
+ full: flags.full === true || flags['--full'] === true || statusArgs.includes('--full'),
372
+ now: firstDefined(flags.now, flags['--now'], getInlineValue('--now'), getNextValue('--now')),
373
+ staleAfterDays: firstDefined(
374
+ flags.staleAfterDays,
375
+ flags['--stale-after-days'],
376
+ flags['--staleAfterDays'],
377
+ getInlineValue('--stale-after-days'),
378
+ getInlineValue('--staleAfterDays'),
379
+ getNextValue('--stale-after-days'),
380
+ getNextValue('--staleAfterDays')
381
+ ),
382
+ };
383
+ }
384
+
385
+ function runGitCommand(projectRoot, args) {
386
+ if (!projectRoot) {
387
+ return '';
388
+ }
389
+
390
+ try {
391
+ return secureExecFileSync('git', args, {
392
+ encoding: 'utf8',
393
+ cwd: projectRoot,
394
+ stdio: ['pipe', 'pipe', 'pipe'],
395
+ }).trim();
396
+ } catch (_error) {
397
+ return '';
398
+ }
399
+ }
400
+
401
+ function detectRepoContext(projectRoot = process.cwd()) {
402
+ const worktree = detectWorktree(projectRoot);
403
+ const branch = worktree.branch || runGitCommand(projectRoot, ['branch', '--show-current']) || 'unknown';
404
+ const statusOutput = runGitCommand(projectRoot, ['status', '--short']);
405
+ const changedEntries = statusOutput ? statusOutput.split(/\r?\n/).filter(Boolean) : [];
406
+ const clean = changedEntries.length === 0;
407
+ const summary = clean ? 'clean' : `${changedEntries.length} uncommitted change${changedEntries.length === 1 ? '' : 's'}`;
408
+
409
+ return {
410
+ branch,
411
+ inWorktree: worktree.inWorktree === true,
412
+ worktreePath: worktree.currentWorktree || projectRoot,
413
+ mainWorktree: worktree.mainWorktree || projectRoot,
414
+ workingTree: {
415
+ clean,
416
+ summary,
417
+ },
346
418
  };
347
419
  }
348
420
 
421
+ function readIssueDetails(issueId, projectRoot = process.cwd()) {
422
+ if (!issueId) {
423
+ return null;
424
+ }
425
+
426
+ try {
427
+ const raw = secureExecFileSync('bd', ['show', issueId, '--json'], {
428
+ encoding: 'utf8',
429
+ cwd: projectRoot,
430
+ stdio: ['pipe', 'pipe', 'pipe'],
431
+ }).trim();
432
+ if (!raw) {
433
+ return null;
434
+ }
435
+
436
+ const parsed = JSON.parse(raw);
437
+ return Array.isArray(parsed) ? parsed[0] || null : parsed;
438
+ } catch (_error) {
439
+ return null;
440
+ }
441
+ }
442
+
443
+ function stripDatePrefix(value) {
444
+ const datePrefixLength = 11;
445
+ const datePrefix =
446
+ value.length > datePrefixLength &&
447
+ value[4] === '-' &&
448
+ value[7] === '-' &&
449
+ value[10] === '-';
450
+ return datePrefix ? value.slice(datePrefixLength) : value;
451
+ }
452
+
453
+ function trimTrailingPathPunctuation(value) {
454
+ const trailingPunctuation = '.,;:';
455
+ let end = value.length;
456
+ while (end > 0 && trailingPunctuation.includes(value[end - 1])) {
457
+ end -= 1;
458
+ }
459
+ return value.slice(0, end);
460
+ }
461
+
462
+ function stripMarkdownSuffixes(value) {
463
+ let stripped = value.endsWith('.md') ? value.slice(0, -3) : value;
464
+ for (const suffix of ['-design', '-plan', '-tasks', '-decisions']) {
465
+ if (stripped.endsWith(suffix)) {
466
+ stripped = stripped.slice(0, -suffix.length);
467
+ break;
468
+ }
469
+ }
470
+ return stripped;
471
+ }
472
+
473
+ function extractSlugFromDesignPath(candidatePath) {
474
+ const parts = trimTrailingPathPunctuation(candidatePath).replaceAll('\\', '/').split('/').filter(Boolean);
475
+ const docsIndex = parts.indexOf('docs');
476
+ if (docsIndex === -1) {
477
+ return '';
478
+ }
479
+ const root = parts[docsIndex + 1];
480
+
481
+ if (root === 'plans') {
482
+ const fileName = parts[docsIndex + 2] || '';
483
+ return stripDatePrefix(stripMarkdownSuffixes(fileName));
484
+ }
485
+
486
+ if (root === 'work') {
487
+ return stripDatePrefix(parts[docsIndex + 2] || '');
488
+ }
489
+
490
+ return '';
491
+ }
492
+
493
+ function isPathCandidateChar(char) {
494
+ return (
495
+ (char >= 'a' && char <= 'z') ||
496
+ (char >= 'A' && char <= 'Z') ||
497
+ (char >= '0' && char <= '9') ||
498
+ char === '/' ||
499
+ char === '\\' ||
500
+ char === '.' ||
501
+ char === '-' ||
502
+ char === '_'
503
+ );
504
+ }
505
+
506
+ function extractPathCandidates(text) {
507
+ const candidates = [];
508
+ let index = 0;
509
+
510
+ while (index < text.length) {
511
+ const hasForwardPath = text.startsWith('docs/', index);
512
+ const hasBackwardPath = text.startsWith('docs\\', index);
513
+
514
+ if (!hasForwardPath && !hasBackwardPath) {
515
+ index += 1;
516
+ continue;
517
+ }
518
+
519
+ let end = index;
520
+ while (end < text.length && isPathCandidateChar(text[end])) {
521
+ end += 1;
522
+ }
523
+ candidates.push(text.slice(index, end));
524
+ index = end;
525
+ }
526
+
527
+ return candidates;
528
+ }
529
+
530
+ function extractDesignSlugs(design) {
531
+ if (typeof design !== 'string' || design.trim() === '') {
532
+ return [];
533
+ }
534
+
535
+ const slugs = new Set();
536
+ for (const candidate of extractPathCandidates(design)) {
537
+ const slug = extractSlugFromDesignPath(candidate);
538
+ if (slug) {
539
+ slugs.add(slug);
540
+ }
541
+ }
542
+
543
+ return [...slugs];
544
+ }
545
+
546
+ function designMatchesFeatureSlug(issue, featureSlug) {
547
+ if (!issue || !featureSlug) {
548
+ return false;
549
+ }
550
+
551
+ return extractDesignSlugs(issue.design).includes(featureSlug);
552
+ }
553
+
554
+ function discoverCurrentIssue(snapshot, context, projectRoot = process.cwd()) {
555
+ const activeAssigned = snapshot.activeAssigned || [];
556
+ if (activeAssigned.length === 0) {
557
+ return null;
558
+ }
559
+
560
+ const branchInfo = analyzeBranch(context.branch || 'master');
561
+ const hydratedIssues = new Map();
562
+
563
+ function hydrateIssue(issue) {
564
+ if (!issue || !issue.id) {
565
+ return issue;
566
+ }
567
+
568
+ if (hydratedIssues.has(issue.id)) {
569
+ return hydratedIssues.get(issue.id);
570
+ }
571
+
572
+ const hasSufficientDetails = Boolean(issue.comments) && Boolean(issue.design);
573
+ const details = hasSufficientDetails ? issue : readIssueDetails(issue.id, projectRoot);
574
+ const hydratedIssue = details ? { ...issue, ...details } : issue;
575
+ hydratedIssues.set(issue.id, hydratedIssue);
576
+ return hydratedIssue;
577
+ }
578
+
579
+ if (branchInfo.featureSlug) {
580
+ const slugMatches = [];
581
+
582
+ for (const issue of activeAssigned) {
583
+ const candidate = issue.design ? issue : hydrateIssue(issue);
584
+ if (designMatchesFeatureSlug(candidate, branchInfo.featureSlug)) {
585
+ slugMatches.push(candidate);
586
+ }
587
+ }
588
+
589
+ if (slugMatches.length === 1) {
590
+ return hydrateIssue(slugMatches[0]);
591
+ }
592
+
593
+ if (slugMatches.length > 1) {
594
+ return null;
595
+ }
596
+ }
597
+
598
+ if (activeAssigned.length === 1) {
599
+ return hydrateIssue(activeAssigned[0]);
600
+ }
601
+
602
+ return null;
603
+ }
604
+
349
605
  function resolveWorkflowState(inputs) {
350
606
  try {
351
607
  if (inputs.workflowState) {
@@ -354,7 +610,9 @@ function resolveWorkflowState(inputs) {
354
610
 
355
611
  const { state } = loadState(inputs.projectRoot, {
356
612
  issueId: inputs.issueId,
613
+ issue: inputs.issue,
357
614
  comments: inputs.bdComments,
615
+ preferBeads: inputs.preferBeads,
358
616
  });
359
617
 
360
618
  return { workflowState: state, fallbackReason: null };
@@ -440,11 +698,8 @@ function collectCompletedChecks(result) {
440
698
  function formatAuthoritativeStatus(result) {
441
699
  const completedStages = result.workflowState.completedStages.map(stageId => stageId);
442
700
  const allowedCommands = result.nextStages.map(stageId => '/' + stageId).join(', ');
443
- const nextLines = [`Run now: /${result.runCommand}`];
701
+ const nextLines = formatRunNextLines(result);
444
702
 
445
- if (result.nextCommand) {
446
- nextLines.push(`Next after this: /${result.nextCommand}`);
447
- }
448
703
  if (allowedCommands) {
449
704
  nextLines.push(`Allowed after this: ${allowedCommands}`);
450
705
  }
@@ -495,14 +750,74 @@ function formatStatus(result) {
495
750
 
496
751
  module.exports = {
497
752
  name: 'status',
498
- description: 'Intelligent stage detection with confidence scoring',
753
+ description: 'One-glance orientation: where you are, what to run next, and your work',
754
+ usage: 'forge status [--full] [--json]',
755
+ flags: {
756
+ '--full': 'Also show blocked, stale, and recently completed issues',
757
+ '--json': 'Emit the machine-readable status envelope',
758
+ },
499
759
  handler: async (args, flags, projectRoot) => {
500
760
  const inputs = parseStatusInputs(args, flags);
761
+ const isZeroArgStatus = !inputs.issueId && !inputs.workflowState && !inputs.bdComments;
762
+ inputs.preferBeads = !isZeroArgStatus && Boolean(inputs.issueId || inputs.bdComments);
501
763
  if (!inputs.projectRoot && projectRoot) {
502
764
  inputs.projectRoot = projectRoot;
503
765
  }
766
+ const effectiveProjectRoot = inputs.projectRoot || process.cwd();
767
+ let context = null;
768
+ let snapshot = null;
769
+
770
+ if (isZeroArgStatus) {
771
+ context = detectRepoContext(effectiveProjectRoot);
772
+ // Read from the active issue backend (Kernel by default; Beads only when
773
+ // explicitly selected). Previously hard-wired to the retired Beads
774
+ // issues.jsonl, which is empty on a kernel-default repo — so the flagship
775
+ // view showed "Ready: none" despite a populated kernel (bug 40f35797).
776
+ snapshot = await readStatusSnapshot(effectiveProjectRoot, {
777
+ now: inputs.now,
778
+ staleAfterDays: inputs.staleAfterDays,
779
+ issueBackend: inputs.issueBackend,
780
+ env: process.env,
781
+ });
782
+ const discoveredIssue = discoverCurrentIssue(snapshot, context, effectiveProjectRoot);
783
+ if (discoveredIssue) {
784
+ inputs.issueId = discoveredIssue.id;
785
+ inputs.issue = discoveredIssue;
786
+ }
787
+ }
788
+
504
789
  const { workflowState, fallbackReason } = resolveWorkflowState(inputs);
505
790
 
791
+ if (isZeroArgStatus) {
792
+ if (workflowState) {
793
+ const result = buildAuthoritativeStatus(workflowState);
794
+ const output = inputs.json
795
+ ? JSON.stringify(buildPersonalStatusJson({ context, snapshot, workflowResult: result }), null, 2)
796
+ : formatZeroArgStatus({ context, snapshot, workflowResult: result, full: inputs.full });
797
+ return {
798
+ success: true,
799
+ context,
800
+ snapshot,
801
+ issueId: inputs.issueId,
802
+ output,
803
+ ...result,
804
+ };
805
+ }
806
+
807
+ const output = inputs.json
808
+ ? JSON.stringify(buildPersonalStatusJson({ context, snapshot }), null, 2)
809
+ : formatZeroArgStatus({ context, snapshot, full: inputs.full });
810
+ return {
811
+ success: true,
812
+ context,
813
+ snapshot,
814
+ issueId: inputs.issueId,
815
+ missingWorkflowState: true,
816
+ output,
817
+ ...(fallbackReason ? { fallbackReason } : {}),
818
+ };
819
+ }
820
+
506
821
  if (workflowState) {
507
822
  const result = buildAuthoritativeStatus(workflowState);
508
823
  return { success: true, output: formatStatus(result), ...result };
@@ -521,6 +836,8 @@ module.exports = {
521
836
  parseStatusInputs,
522
837
  readWorkflowStateFromBeads,
523
838
  detectStage,
839
+ detectRepoContext,
840
+ discoverCurrentIssue,
524
841
  analyzeBranch,
525
842
  analyzeFiles,
526
843
  analyzePR,
@@ -528,4 +845,5 @@ module.exports = {
528
845
  analyzeBeads,
529
846
  calculateConfidence,
530
847
  formatStatus,
848
+ extractDesignSlugs,
531
849
  };
@@ -1,73 +1,61 @@
1
1
  'use strict';
2
2
 
3
- const { execFileSync } = require('node:child_process');
4
-
5
- function isRecoverableBeadsSyncError(error) {
6
- const message = error?.message ?? String(error ?? '');
7
- return (
8
- message.includes('failed to open database') ||
9
- message.includes('database not found') ||
10
- message.includes('no beads configuration found') ||
11
- message.includes('remote \'origin\' not found') ||
12
- message.includes('remote "origin" not found')
13
- );
14
- }
3
+ const { createSyncBackend } = require('../sync-backend');
15
4
 
16
5
  /**
17
6
  * Forge Sync Command
18
- * Syncs Beads issue data by running dolt pull + push.
19
- * Uses execFileSync (not execSync) to prevent command injection (OWASP A03).
7
+ * Syncs Kernel issue state with the configured sync backend.
8
+ *
9
+ * The local kernel is single-machine authority, so the default `local-noop`
10
+ * backend is a graceful no-op that names the model instead of moving data.
11
+ * Selecting `git-jsonl`/`server` (a future swap) is a backend change with zero
12
+ * call-site churn — see docs/work/2026-06-26-sync-authority/plan.md.
20
13
  *
21
14
  * @module commands/sync
22
15
  */
23
16
  module.exports = {
24
17
  name: 'sync',
25
- description: 'Sync Beads issue data (dolt pull + push)',
18
+ description: 'Sync Kernel issue state with the configured sync backend (local-noop until a server/remote is configured)',
26
19
  usage: 'forge sync',
27
20
  flags: {},
28
21
 
29
22
  /**
30
- * Run beads dolt pull + push to sync issue data.
23
+ * Resolve a SyncBackend (default `local-noop`) and run its one-shot sync.
31
24
  * @param {string[]} _args - Positional arguments (unused)
32
25
  * @param {object} _flags - CLI flags (unused)
33
- * @param {string} _projectRoot - Project root path (unused)
26
+ * @param {string} projectRoot - Project root path (used for backend resolution)
34
27
  * @param {object} [opts] - Options for dependency injection
35
- * @param {Function} [opts._exec] - Override for execFileSync (testing)
28
+ * @param {object} [opts._backend] - Override for the resolved SyncBackend (testing)
29
+ * @param {object} [opts.deps] - Explicit backend selection (precedence over env/config)
30
+ * @param {object} [opts.env] - Environment override (testing)
36
31
  * @returns {Promise<{success: boolean, synced: boolean, message?: string, error?: string}>}
37
32
  */
38
- handler: async (_args, _flags, _projectRoot, opts = {}) => {
39
- const exec = opts._exec || execFileSync;
40
-
41
- // Step 1: Check if bd binary exists
33
+ handler: async (_args, _flags, projectRoot, opts = {}) => {
34
+ let backend;
42
35
  try {
43
- exec('bd', ['--version'], { stdio: 'pipe' });
44
- } catch { /* intentional: bd not installed, skip sync gracefully */ // NOSONAR S2486
45
- return {
46
- success: true,
47
- synced: false,
48
- message: 'Beads not installed — nothing to sync',
49
- };
36
+ backend = opts._backend || createSyncBackend({
37
+ projectRoot,
38
+ deps: opts.deps,
39
+ env: opts.env,
40
+ });
41
+ } catch (resolveErr) {
42
+ return { success: false, synced: false, error: resolveErr.message };
50
43
  }
51
44
 
52
- // Step 2: Run dolt pull + push
45
+ let result;
53
46
  try {
54
- exec('bd', ['dolt', 'pull'], { stdio: 'pipe' });
55
- exec('bd', ['dolt', 'push'], { stdio: 'pipe' });
47
+ result = await backend.sync({ ...opts, projectRoot });
56
48
  } catch (syncErr) {
57
- if (isRecoverableBeadsSyncError(syncErr)) {
58
- return {
59
- success: true,
60
- synced: false,
61
- message: 'Beads is installed but not initialized for sync in this worktree — skipping sync',
62
- };
63
- }
64
- return {
65
- success: false,
66
- synced: false,
67
- error: syncErr.message,
68
- };
49
+ console.error(`forge sync failed: ${syncErr.message}`);
50
+ return { success: false, synced: false, error: syncErr.message };
69
51
  }
70
-
71
- return { success: true, synced: true };
52
+ // Surface the outcome to the user — otherwise `forge sync` runs silently and
53
+ // a user can't tell whether it synced, no-op'd (local-noop), or failed.
54
+ if (result && result.message) {
55
+ console.log(result.message);
56
+ } else if (result && !result.success && result.error) {
57
+ console.error(`forge sync failed: ${result.error}`);
58
+ }
59
+ return result;
72
60
  },
73
61
  };
@@ -1,6 +1,7 @@
1
1
  const { existsSync } = require('node:fs');
2
2
  const { execFileSync } = require('node:child_process');
3
3
  const path = require('node:path');
4
+ const { getPackageRoot } = require('../package-root');
4
5
 
5
6
  // Resolve bash binary from fixed, known locations only (no PATH search).
6
7
  // SonarCloud S4036: PATH must not be searched for OS commands.
@@ -22,7 +23,9 @@ function _resolveBash() {
22
23
  }
23
24
 
24
25
  function handleTeam(args) {
25
- const scriptPath = path.join(__dirname, '..', '..', 'scripts', 'forge-team', 'index.sh');
26
+ // Route through the asset root so `forge team` finds the script in BOTH channels:
27
+ // the on-disk package (npm/npx) and a compiled binary (extracted embed dir).
28
+ const scriptPath = path.join(getPackageRoot(), 'scripts', 'forge-team', 'index.sh');
26
29
  const bashPath = _resolveBash();
27
30
 
28
31
  try {
@@ -34,4 +37,14 @@ function handleTeam(args) {
34
37
  }
35
38
  }
36
39
 
37
- module.exports = { handleTeam };
40
+ async function handler(args = []) {
41
+ handleTeam(args);
42
+ return { success: true };
43
+ }
44
+
45
+ module.exports = {
46
+ name: 'team',
47
+ description: 'Team coordination workflows and sync utilities',
48
+ handler,
49
+ handleTeam
50
+ };
@@ -27,6 +27,18 @@ const LOCKFILE_MAP = [
27
27
 
28
28
  const DEFAULT_TIMEOUT = 120000;
29
29
  const BEADS_CHECK_TIMEOUT = 3000;
30
+ const DIRECT_TEST_CANDIDATES = Object.freeze({
31
+ 'bin/forge.js': [
32
+ 'test/cli-flags.test.js',
33
+ 'test/forge-cli-registry.test.js',
34
+ 'test/setup-github-sync.test.js',
35
+ 'test/setup-runtime-flags.test.js',
36
+ ],
37
+ 'lib/lefthook-check.js': ['test/lefthook-check.test.js', 'test/runtime-health.test.js'],
38
+ 'lib/runtime-health.js': ['test/runtime-health.test.js'],
39
+ 'lib/upgrade-safety.js': ['test/commands/upgrade.test.js'],
40
+ 'scripts/test.js': ['test/scripts/test-runner.test.js'],
41
+ });
30
42
 
31
43
  /**
32
44
  * Detect the package manager by checking which lockfile exists.
@@ -115,7 +127,17 @@ function getChangedFiles(execFileSync, options = {}) {
115
127
  encoding: 'utf8',
116
128
  timeout: 5000,
117
129
  }).trim();
118
- } catch (_e) { // NOSONAR S2486
130
+ } catch (err) { // NOSONAR S2486
131
+ // A git-diff FAILURE is NOT the same as "no files changed": we could not
132
+ // determine the change set at all. Callers that must fail closed (preflight)
133
+ // pass { strict: true } so the error surfaces instead of masquerading as an
134
+ // empty list. The default path keeps the historical empty-list fallback that
135
+ // `forge test --affected` and the pre-push mapping rely on to run the full
136
+ // suite, so their semantics are unchanged.
137
+ if (options.strict) {
138
+ const reason = err && err.message ? err.message : String(err);
139
+ throw new Error(`git diff failed while computing changed files: ${reason}`);
140
+ }
119
141
  /* intentional: git diff failed, no affected files to report */
120
142
  return [];
121
143
  }
@@ -152,17 +174,36 @@ function getAffectedTestFiles(projectRoot, execFileSync, fs = defaultFs, options
152
174
  function getTestCandidatesForChangedFile(file) {
153
175
  if (!file) return [];
154
176
 
177
+ const directCandidates = DIRECT_TEST_CANDIDATES[file];
178
+ if (directCandidates) {
179
+ return directCandidates;
180
+ }
181
+
155
182
  if (file.startsWith('test/') && file.endsWith('.test.js')) {
156
183
  return [file];
157
184
  }
158
185
 
186
+ if (file === 'README.md'
187
+ || file === 'QUICKSTART.md'
188
+ || file === 'CHANGELOG.md'
189
+ || file === 'AGENTS.md'
190
+ || file === 'DEVELOPMENT.md'
191
+ || file === 'docs/INDEX.md'
192
+ || file === 'docs/PROJECT_DESIGN.md'
193
+ || file.startsWith('docs/forge/')
194
+ || file.startsWith('docs/guides/')
195
+ || file.startsWith('docs/reference/')
196
+ || file.startsWith('docs/work/')) {
197
+ return ['test/docs-consistency.test.js'];
198
+ }
199
+
159
200
  if (file.startsWith('lib/') && file.endsWith('.js')) {
160
201
  const relative = file.slice('lib/'.length);
161
202
  return [`test/${relative.replace(/\.js$/, '.test.js')}`];
162
203
  }
163
204
 
164
- if (file.startsWith('scripts/') && file.endsWith('.js')) {
165
- const relative = file.slice('scripts/'.length).replace(/\.js$/, '.test.js');
205
+ if (file.startsWith('scripts/') && (file.endsWith('.js') || file.endsWith('.sh'))) {
206
+ const relative = file.slice('scripts/'.length).replace(/\.(js|sh)$/, '.test.js');
166
207
  return [
167
208
  `test/scripts/${relative}`,
168
209
  `test/${relative}`,
@@ -177,10 +218,20 @@ function getTestCandidatesForChangedFile(file) {
177
218
  ];
178
219
  }
179
220
 
180
- if (file.startsWith('.claude/commands/') && file.endsWith('.md')) {
221
+ if (file.startsWith('.github/agentic-workflows/') || file === '.github/behavioral-test-scores.json') {
222
+ return [
223
+ 'test/scripts/behavioral-judge.test.js',
224
+ 'test/structural/agentic-workflow-sync.test.js',
225
+ ];
226
+ }
227
+
228
+ if (file.startsWith('.forge/')
229
+ || file.startsWith('.cursor/')
230
+ || file.startsWith('.codex/')) {
181
231
  return [
182
- 'test/command-sync-check.test.js',
183
- 'test/structural/command-sync.test.js',
232
+ 'test/agent-gaps.test.js',
233
+ 'test/scripts/check-agents.test.js',
234
+ 'test/structural/skills-sync-drift.test.js',
184
235
  ];
185
236
  }
186
237
 
@@ -192,7 +243,7 @@ module.exports = {
192
243
  getAffectedTestFiles,
193
244
  getTestCandidatesForChangedFile,
194
245
  name: 'test',
195
- description: 'Run tests with smart defaults (timeout, Beads skip, affected-only)',
246
+ description: 'Run tests with smart defaults (timeout, affected-only)',
196
247
  usage: 'forge test [--affected]',
197
248
  flags: {
198
249
  '--affected': 'Run only tests for changed files',
@@ -1,5 +1,5 @@
1
1
  'use strict';
2
2
 
3
- const { makeAliasCommand } = require('./_issue');
3
+ const { createIssueSubcommand } = require('./_issue');
4
4
 
5
- module.exports = makeAliasCommand('update');
5
+ module.exports = createIssueSubcommand('update');