forge-workflow 0.0.10 → 0.1.0-beta.3

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 (468) 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 +82 -5
  5. package/.forge/hooks/forge-native-hook.js +431 -0
  6. package/.forge/protected-paths.yaml +157 -0
  7. package/AGENTS.md +151 -61
  8. package/CHANGELOG.md +709 -0
  9. package/CLAUDE.md +9 -118
  10. package/QUICKSTART.md +175 -0
  11. package/README.md +275 -365
  12. package/bin/forge-cmd.js +120 -9
  13. package/bin/forge-preflight.js +26 -5
  14. package/bin/forge.js +532 -489
  15. package/docs/INDEX.md +93 -0
  16. package/docs/PROJECT_DESIGN.md +685 -0
  17. package/docs/architecture/index.md +66 -0
  18. package/docs/architecture/notes/README.md +35 -0
  19. package/docs/architecture/subsystems/README.md +46 -0
  20. package/docs/forge/TOOLCHAIN.md +670 -0
  21. package/docs/forge/VALIDATION.md +82 -0
  22. package/docs/{AGENT_INSTALL_PROMPT.md → guides/AGENT_INSTALL_PROMPT.md} +3 -3
  23. package/docs/guides/BEADS_GITHUB_SYNC.md +32 -0
  24. package/docs/{ENHANCED_ONBOARDING.md → guides/ENHANCED_ONBOARDING.md} +16 -12
  25. package/docs/guides/GREPTILE_SETUP.md +46 -0
  26. package/docs/guides/MANUAL_REVIEW_GUIDE.md +58 -0
  27. package/docs/guides/MIGRATION.md +56 -0
  28. package/docs/guides/SETUP.md +121 -0
  29. package/docs/guides/SUPPORT.md +190 -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 +214 -0
  35. package/docs/reference/DECISION_DRIFT_GUARDS.md +97 -0
  36. package/docs/{EXAMPLES.md → reference/EXAMPLES.md} +7 -5
  37. package/docs/reference/FORGE_KERNEL_STORAGE_MODEL.md +135 -0
  38. package/docs/reference/HERMES_INTEGRATION.md +118 -0
  39. package/docs/reference/INSIGHTS_RECAP.md +63 -0
  40. package/docs/reference/INSTALL.md +164 -0
  41. package/docs/reference/KERNEL_TAXONOMY_VALIDATION.md +161 -0
  42. package/docs/reference/PROTECTED_PATH_MANIFEST.md +25 -0
  43. package/docs/reference/RELEASE.md +68 -0
  44. package/docs/reference/RESEARCH_TEMPLATE.md +292 -0
  45. package/docs/{ROADMAP.md → reference/ROADMAP.md} +12 -9
  46. package/docs/reference/SKILLS.md +35 -0
  47. package/docs/reference/STATUS_BOARD.md +80 -0
  48. package/docs/reference/TEMPLATES.md +106 -0
  49. package/docs/{TOOLCHAIN.md → reference/TOOLCHAIN.md} +62 -47
  50. package/docs/reference/VALIDATION.md +82 -0
  51. package/docs/reference/agent-permissions.md +169 -0
  52. package/docs/reference/beads-to-kernel-migration-ux.md +61 -0
  53. package/docs/reference/control-plane-guarantees.md +125 -0
  54. package/docs/reference/dependency-chain.md +331 -0
  55. package/docs/reference/forge-kernel-issue-command-contract.md +161 -0
  56. package/docs/reference/forge-kernel-schema.md +72 -0
  57. package/docs/reference/kernel-conflict-evaluators.md +27 -0
  58. package/docs/reference/patch-md-format.md +77 -0
  59. package/docs/reference/protected-state-surfaces.md +59 -0
  60. package/docs/reference/shepherd.md +155 -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/activation/ensure-forge-home.js +135 -0
  67. package/lib/adapter-cli.js +307 -0
  68. package/lib/adapters/beads-issue-adapter.js +127 -0
  69. package/lib/adapters/beads-kernel-compat.js +1109 -0
  70. package/lib/adapters/greptile-review-adapter.js +141 -0
  71. package/lib/adapters/kernel-issue-adapter.js +101 -0
  72. package/lib/adapters/pr-state-adapter.js +484 -0
  73. package/lib/adoption-profiles.js +139 -0
  74. package/lib/agents/README.md +2 -6
  75. package/lib/agents/claude.plugin.json +3 -8
  76. package/lib/agents/codex.plugin.json +9 -1
  77. package/lib/agents/cursor.plugin.json +2 -6
  78. package/lib/agents/hermes.plugin.json +22 -0
  79. package/lib/agents-config.js +39 -1236
  80. package/lib/audit-evidence.js +282 -0
  81. package/lib/beads-detect.js +60 -0
  82. package/lib/beads-nudge.js +91 -0
  83. package/lib/beads-setup.js +121 -0
  84. package/lib/beads-sync-scaffold.js +25 -101
  85. package/lib/codex-skills.js +51 -1
  86. package/lib/commands/_aliases.js +248 -0
  87. package/lib/commands/_issue.js +780 -77
  88. package/lib/commands/_manifest.js +93 -0
  89. package/lib/commands/_registry.js +99 -34
  90. package/lib/commands/_resolve-command-opts.js +230 -0
  91. package/lib/commands/_serve-security.js +270 -0
  92. package/lib/commands/adapter.js +12 -0
  93. package/lib/commands/add.js +118 -0
  94. package/lib/commands/audit.js +70 -0
  95. package/lib/commands/blocked.js +5 -0
  96. package/lib/commands/board.js +64 -0
  97. package/lib/commands/claim.js +21 -2
  98. package/lib/commands/claims.js +7 -0
  99. package/lib/commands/clean.js +485 -75
  100. package/lib/commands/close.js +2 -2
  101. package/lib/commands/comment.js +5 -0
  102. package/lib/commands/control.js +148 -0
  103. package/lib/commands/create.js +2 -2
  104. package/lib/commands/dev.js +185 -7
  105. package/lib/commands/doc-gate.js +336 -0
  106. package/lib/commands/doctor.js +156 -0
  107. package/lib/commands/explain.js +15 -0
  108. package/lib/commands/export.js +237 -0
  109. package/lib/commands/gate.js +209 -0
  110. package/lib/commands/hooks.js +377 -0
  111. package/lib/commands/inbox.js +118 -0
  112. package/lib/commands/init.js +604 -0
  113. package/lib/commands/insights.js +79 -0
  114. package/lib/commands/issue.js +12 -1
  115. package/lib/commands/issues.js +17 -0
  116. package/lib/commands/lint.js +5 -0
  117. package/lib/commands/list.js +2 -2
  118. package/lib/commands/memory.js +81 -0
  119. package/lib/commands/merge.js +312 -0
  120. package/lib/commands/migrate.js +362 -0
  121. package/lib/commands/new.js +12 -0
  122. package/lib/commands/options.js +241 -0
  123. package/lib/commands/orient.js +13 -0
  124. package/lib/commands/orphans.js +5 -0
  125. package/lib/commands/patch.js +67 -0
  126. package/lib/commands/plan.js +481 -29
  127. package/lib/commands/pr.js +88 -0
  128. package/lib/commands/preflight.js +211 -0
  129. package/lib/commands/prime.js +13 -0
  130. package/lib/commands/push.js +135 -2
  131. package/lib/commands/ready.js +2 -2
  132. package/lib/commands/recall.js +171 -0
  133. package/lib/commands/recap.js +75 -0
  134. package/lib/commands/recommend.js +0 -1
  135. package/lib/commands/release.js +104 -0
  136. package/lib/commands/remember.js +140 -0
  137. package/lib/commands/role.js +99 -0
  138. package/lib/commands/serve.js +581 -0
  139. package/lib/commands/setup.js +900 -971
  140. package/lib/commands/shepherd.js +501 -0
  141. package/lib/commands/ship.js +59 -1
  142. package/lib/commands/show.js +2 -2
  143. package/lib/commands/stage.js +192 -0
  144. package/lib/commands/stale.js +5 -0
  145. package/lib/commands/status.js +158 -21
  146. package/lib/commands/sync.js +34 -46
  147. package/lib/commands/team.js +4 -1
  148. package/lib/commands/test.js +43 -27
  149. package/lib/commands/update.js +2 -2
  150. package/lib/commands/upgrade.js +47 -0
  151. package/lib/commands/validate.js +43 -18
  152. package/lib/commands/worktree.js +362 -99
  153. package/lib/config-writer.js +202 -0
  154. package/lib/control-plane.js +236 -0
  155. package/lib/core/runtime-graph.js +977 -0
  156. package/lib/dep-guard/keyword-ripple.js +2 -2
  157. package/lib/deprecated-sync-cleanup.js +362 -0
  158. package/lib/detect-agent.js +2 -28
  159. package/lib/detect-worktree.js +35 -9
  160. package/lib/doc-gate/declaration.js +177 -0
  161. package/lib/doc-gate/detect.js +289 -0
  162. package/lib/doc-gate/gate.js +375 -0
  163. package/lib/doc-gate/okf-config.js +128 -0
  164. package/lib/doc-gate/okf.js +429 -0
  165. package/lib/docs-command.js +1161 -6
  166. package/lib/forge-issues.js +382 -11
  167. package/lib/forge-lock.js +262 -0
  168. package/lib/gate-events.js +192 -0
  169. package/lib/global-flags.js +104 -0
  170. package/lib/greptile-match.js +7 -63
  171. package/lib/grounding/context-events.js +230 -0
  172. package/lib/grounding/read-first.js +112 -0
  173. package/lib/harness-capability-matrix.js +380 -0
  174. package/lib/hook-global-installer.js +347 -0
  175. package/lib/hook-renderer.js +541 -0
  176. package/lib/inbox.js +391 -0
  177. package/lib/insights.js +397 -0
  178. package/lib/issue-adapter.js +156 -0
  179. package/lib/issue-backend.js +145 -0
  180. package/lib/issue-render.js +220 -0
  181. package/lib/kernel/backing-issue.js +311 -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/owned-kernel.js +43 -0
  192. package/lib/kernel/planning-buckets-schema.js +109 -0
  193. package/lib/kernel/projection-jsonl-writer.js +450 -0
  194. package/lib/kernel/readiness-model.js +329 -0
  195. package/lib/kernel/schema.js +356 -0
  196. package/lib/kernel/sqlite-driver.js +2540 -0
  197. package/lib/kernel/taxonomy-validator.js +394 -0
  198. package/lib/lefthook-check.js +3 -2
  199. package/lib/lefthook-wiring.js +413 -0
  200. package/lib/mcp-config-renderer.js +288 -0
  201. package/lib/memory/graphiti-mcp.js +106 -0
  202. package/lib/memory/router.js +387 -0
  203. package/lib/memory/typed-api.js +102 -0
  204. package/lib/memory-digest.js +195 -0
  205. package/lib/merge-rules.js +395 -0
  206. package/lib/migrate-dry-run.js +466 -0
  207. package/lib/orientation.js +863 -0
  208. package/lib/package-manager-remediation.js +103 -0
  209. package/lib/package-root.js +381 -0
  210. package/lib/patch-intent.js +890 -0
  211. package/lib/plugin-catalog.js +3 -4
  212. package/lib/plugin-manager.js +0 -5
  213. package/lib/pr-bundle.js +186 -0
  214. package/lib/pr-monitor/auto-actions.js +175 -0
  215. package/lib/pr-monitor/differ.js +195 -0
  216. package/lib/pr-monitor/digest.js +206 -0
  217. package/lib/pr-monitor/events.js +0 -0
  218. package/lib/pr-monitor/gather.js +124 -0
  219. package/lib/pr-monitor/journal.js +299 -0
  220. package/lib/pr-monitor/monitor.js +146 -0
  221. package/lib/pr-monitor/render-sticky.js +192 -0
  222. package/lib/pr-monitor/upsert-sticky.js +169 -0
  223. package/lib/pr-monitor/watch-lifecycle.js +95 -0
  224. package/lib/pr-monitor/watch.js +247 -0
  225. package/lib/pr-pull.js +1314 -0
  226. package/lib/pr-shepherd.js +494 -0
  227. package/lib/pr-state-validator.js +59 -0
  228. package/lib/preflight/gates.js +237 -0
  229. package/lib/preflight/runner.js +116 -0
  230. package/lib/project-discovery.js +0 -53
  231. package/lib/project-memory.js +99 -497
  232. package/lib/protected-path-manifest.js +281 -0
  233. package/lib/protected-state-surfaces.js +387 -0
  234. package/lib/release-readiness.js +2105 -0
  235. package/lib/reset.js +59 -45
  236. package/lib/review-adapter.js +68 -0
  237. package/lib/rules-sync.js +260 -0
  238. package/lib/runtime-health.js +241 -20
  239. package/lib/safety-config-renderer.js +268 -0
  240. package/lib/setup-action-log.js +1 -7
  241. package/lib/setup.js +27 -65
  242. package/lib/shell-utils.js +76 -6
  243. package/lib/skills-sync.js +330 -0
  244. package/lib/smart-status/scoring.js +17 -3
  245. package/lib/status/beads-snapshot.js +45 -2
  246. package/lib/status/presenter.js +169 -18
  247. package/lib/status/snapshot.js +186 -0
  248. package/lib/sync-backend.js +202 -0
  249. package/lib/untrusted-content.js +52 -0
  250. package/lib/upgrade-safety.js +251 -0
  251. package/lib/workflow/enforce-stage.js +351 -45
  252. package/lib/workflow/stage-transition.js +115 -0
  253. package/lib/workflow/stages.js +30 -6
  254. package/lib/workflow/state-manager.js +11 -22
  255. package/lib/workflow/state.js +23 -1
  256. package/lib/workflow-profiles.js +17 -5
  257. package/package.json +37 -35
  258. package/rules/documentation.md +19 -0
  259. package/rules/kernel-tracking.md +26 -0
  260. package/rules/security.md +22 -0
  261. package/rules/tdd.md +20 -0
  262. package/rules/workflow.md +27 -0
  263. package/scripts/auto-backing-issue.js +47 -0
  264. package/scripts/beads-context.sh +81 -57
  265. package/scripts/beads-upgrade-smoke.sh +24 -3
  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.sh +22 -3
  272. package/scripts/file-index.sh +3 -0
  273. package/scripts/forge-team/lib/claim.sh +34 -18
  274. package/scripts/forge-team/lib/dashboard.sh +61 -86
  275. package/scripts/forge-team/lib/epic.sh +99 -263
  276. package/scripts/forge-team/lib/hooks.sh +26 -28
  277. package/scripts/forge-team/lib/identity.sh +4 -4
  278. package/scripts/forge-team/lib/sync-github.sh +49 -84
  279. package/scripts/forge-team/lib/verify.sh +93 -83
  280. package/scripts/forge-team/lib/workload.sh +41 -65
  281. package/scripts/forge-team/tests/claim.test.sh +25 -19
  282. package/scripts/forge-team/tests/dashboard.test.sh +31 -46
  283. package/scripts/forge-team/tests/epic.test.sh +52 -71
  284. package/scripts/forge-team/tests/hooks.test.sh +38 -50
  285. package/scripts/forge-team/tests/identity.test.sh +3 -3
  286. package/scripts/forge-team/tests/integration.test.sh +44 -66
  287. package/scripts/forge-team/tests/sync-github.test.sh +50 -83
  288. package/scripts/forge-team/tests/verify.test.sh +37 -46
  289. package/scripts/forge-team/tests/workflow-integration.test.sh +4 -4
  290. package/scripts/forge-team/tests/workload.test.sh +32 -66
  291. package/scripts/gen-command-manifest.js +153 -0
  292. package/scripts/gen-embedded-assets.mjs +129 -0
  293. package/scripts/install.ps1 +139 -0
  294. package/scripts/install.sh +268 -0
  295. package/scripts/lib/release-asset.mjs +84 -0
  296. package/scripts/parity-check.mjs +145 -0
  297. package/scripts/parity-check.test.mjs +58 -0
  298. package/scripts/pin-agentic-workflow-images.js +112 -0
  299. package/scripts/pr-auto-actions.js +93 -0
  300. package/scripts/pr-coordinator.sh +3 -0
  301. package/scripts/pr-verdict-label.js +50 -0
  302. package/scripts/preflight-sonar.eslint.config.mjs +44 -0
  303. package/scripts/preflight.sh +21 -94
  304. package/scripts/protected-state-check.js +104 -0
  305. package/scripts/smart-status.sh +60 -57
  306. package/scripts/spikes/config-race-bench.js +111 -0
  307. package/scripts/spikes/harness-capability-matrix.js +13 -0
  308. package/scripts/spikes/patch-anchor-stability-bench.js +125 -0
  309. package/scripts/spikes/protected-path-manifest.js +20 -0
  310. package/scripts/spikes/skill-auto-invoke-parity.js +292 -0
  311. package/scripts/sync-agent-skills.js +62 -0
  312. package/scripts/sync-utils.sh +3 -0
  313. package/scripts/test-ci-shard.js +13 -6
  314. package/scripts/test.js +95 -12
  315. package/skills/claim-safety/SKILL.md +102 -0
  316. package/skills/claim-safety/evals/evals.json +46 -0
  317. package/{.github/prompts/dev.prompt.md → skills/dev/SKILL.md} +44 -50
  318. package/skills/dev/evals/evals.json +50 -0
  319. package/skills/hermes-forge/SKILL.md +185 -0
  320. package/skills/hermes-forge/evals/evals.json +46 -0
  321. package/skills/issue-basics/SKILL.md +111 -0
  322. package/skills/issue-basics/evals/evals.json +46 -0
  323. package/skills/kernel/SKILL.md +166 -0
  324. package/skills/kernel/evals/evals.json +50 -0
  325. package/skills/memory/SKILL.md +102 -0
  326. package/skills/parallel-deep-research/SKILL.md +14 -11
  327. package/skills/parallel-deep-research/evals/evals.json +11 -27
  328. package/{.github/prompts/plan.prompt.md → skills/plan/SKILL.md} +132 -157
  329. package/skills/plan/evals/evals.json +42 -0
  330. package/skills/research/SKILL.md +195 -0
  331. package/skills/research/evals/evals.json +42 -0
  332. package/{.github/prompts/review.prompt.md → skills/review/SKILL.md} +98 -62
  333. package/skills/review/evals/evals.json +42 -0
  334. package/skills/rollback/SKILL.md +110 -0
  335. package/skills/rollback/evals/evals.json +46 -0
  336. package/skills/rollback/references/methods.md +204 -0
  337. package/{.cursor/commands/rollback.md → skills/rollback/references/workflow-integration.md} +10 -284
  338. package/skills/shepherd/SKILL.md +66 -0
  339. package/skills/shepherd/evals/evals.json +42 -0
  340. package/{.github/prompts/ship.prompt.md → skills/ship/SKILL.md} +81 -45
  341. package/skills/ship/evals/evals.json +42 -0
  342. package/skills/smith/SKILL.md +142 -0
  343. package/skills/smith/evals/evals.json +46 -0
  344. package/skills/smith/references/autonomy-and-gates.md +94 -0
  345. package/{.github/prompts/sonarcloud.prompt.md → skills/sonarcloud/SKILL.md} +14 -3
  346. package/skills/sonarcloud/evals/evals.json +46 -0
  347. package/skills/sonarcloud-analysis/SKILL.md +18 -13
  348. package/skills/sonarcloud-analysis/evals/evals.json +11 -15
  349. package/{.github/prompts/status.prompt.md → skills/status/SKILL.md} +20 -10
  350. package/skills/status/evals/evals.json +50 -0
  351. package/skills/triage-ready/SKILL.md +121 -0
  352. package/skills/triage-ready/evals/evals.json +42 -0
  353. package/{.github/prompts/validate.prompt.md → skills/validate/SKILL.md} +52 -29
  354. package/skills/validate/evals/evals.json +42 -0
  355. package/skills/verify/SKILL.md +299 -0
  356. package/skills/verify/evals/evals.json +50 -0
  357. package/.claude/commands/dev.md +0 -345
  358. package/.claude/commands/plan.md +0 -566
  359. package/.claude/commands/premerge.md +0 -186
  360. package/.claude/commands/research.md +0 -42
  361. package/.claude/commands/review.md +0 -451
  362. package/.claude/commands/rollback.md +0 -721
  363. package/.claude/commands/ship.md +0 -213
  364. package/.claude/commands/sonarcloud.md +0 -152
  365. package/.claude/commands/status.md +0 -90
  366. package/.claude/commands/validate.md +0 -288
  367. package/.claude/commands/verify.md +0 -269
  368. package/.claude/rules/workflow.md +0 -121
  369. package/.cline/workflows/dev.md +0 -342
  370. package/.cline/workflows/plan.md +0 -563
  371. package/.cline/workflows/premerge.md +0 -183
  372. package/.cline/workflows/research.md +0 -39
  373. package/.cline/workflows/review.md +0 -448
  374. package/.cline/workflows/rollback.md +0 -718
  375. package/.cline/workflows/ship.md +0 -210
  376. package/.cline/workflows/sonarcloud.md +0 -146
  377. package/.cline/workflows/status.md +0 -87
  378. package/.cline/workflows/validate.md +0 -285
  379. package/.cline/workflows/verify.md +0 -266
  380. package/.codex/config.toml +0 -11
  381. package/.codex/skills/dev/SKILL.md +0 -345
  382. package/.codex/skills/plan/SKILL.md +0 -566
  383. package/.codex/skills/premerge/SKILL.md +0 -186
  384. package/.codex/skills/research/SKILL.md +0 -42
  385. package/.codex/skills/review/SKILL.md +0 -451
  386. package/.codex/skills/rollback/SKILL.md +0 -721
  387. package/.codex/skills/ship/SKILL.md +0 -213
  388. package/.codex/skills/sonarcloud/SKILL.md +0 -149
  389. package/.codex/skills/status/SKILL.md +0 -90
  390. package/.codex/skills/validate/SKILL.md +0 -288
  391. package/.codex/skills/verify/SKILL.md +0 -269
  392. package/.cursor/commands/dev.md +0 -342
  393. package/.cursor/commands/plan.md +0 -563
  394. package/.cursor/commands/premerge.md +0 -183
  395. package/.cursor/commands/research.md +0 -39
  396. package/.cursor/commands/review.md +0 -448
  397. package/.cursor/commands/ship.md +0 -210
  398. package/.cursor/commands/sonarcloud.md +0 -146
  399. package/.cursor/commands/status.md +0 -87
  400. package/.cursor/commands/validate.md +0 -285
  401. package/.cursor/commands/verify.md +0 -266
  402. package/.cursorrules +0 -149
  403. package/.github/prompts/premerge.prompt.md +0 -188
  404. package/.github/prompts/research.prompt.md +0 -44
  405. package/.github/prompts/rollback.prompt.md +0 -723
  406. package/.github/prompts/verify.prompt.md +0 -271
  407. package/.github/workflows/beads-to-github.yml +0 -89
  408. package/.github/workflows/github-to-beads.yml +0 -100
  409. package/.kilocode/workflows/dev.md +0 -346
  410. package/.kilocode/workflows/plan.md +0 -567
  411. package/.kilocode/workflows/premerge.md +0 -187
  412. package/.kilocode/workflows/research.md +0 -43
  413. package/.kilocode/workflows/review.md +0 -452
  414. package/.kilocode/workflows/rollback.md +0 -722
  415. package/.kilocode/workflows/ship.md +0 -214
  416. package/.kilocode/workflows/sonarcloud.md +0 -150
  417. package/.kilocode/workflows/status.md +0 -91
  418. package/.kilocode/workflows/validate.md +0 -289
  419. package/.kilocode/workflows/verify.md +0 -270
  420. package/.opencode/commands/dev.md +0 -345
  421. package/.opencode/commands/plan.md +0 -566
  422. package/.opencode/commands/premerge.md +0 -186
  423. package/.opencode/commands/research.md +0 -42
  424. package/.opencode/commands/review.md +0 -451
  425. package/.opencode/commands/rollback.md +0 -721
  426. package/.opencode/commands/ship.md +0 -213
  427. package/.opencode/commands/sonarcloud.md +0 -149
  428. package/.opencode/commands/status.md +0 -90
  429. package/.opencode/commands/validate.md +0 -288
  430. package/.opencode/commands/verify.md +0 -269
  431. package/.roo/commands/dev.md +0 -346
  432. package/.roo/commands/plan.md +0 -567
  433. package/.roo/commands/premerge.md +0 -187
  434. package/.roo/commands/research.md +0 -43
  435. package/.roo/commands/review.md +0 -452
  436. package/.roo/commands/rollback.md +0 -722
  437. package/.roo/commands/ship.md +0 -214
  438. package/.roo/commands/sonarcloud.md +0 -150
  439. package/.roo/commands/status.md +0 -91
  440. package/.roo/commands/validate.md +0 -289
  441. package/.roo/commands/verify.md +0 -270
  442. package/docs/BEADS_GITHUB_SYNC.md +0 -281
  443. package/docs/GREPTILE_SETUP.md +0 -400
  444. package/docs/MANUAL_REVIEW_GUIDE.md +0 -106
  445. package/docs/SETUP.md +0 -663
  446. package/docs/VALIDATION.md +0 -363
  447. package/lib/agents/cline.plugin.json +0 -29
  448. package/lib/agents/copilot.plugin.json +0 -24
  449. package/lib/agents/kilocode.plugin.json +0 -22
  450. package/lib/agents/opencode.plugin.json +0 -23
  451. package/lib/agents/roo.plugin.json +0 -30
  452. package/lib/beads-bootstrap.js +0 -225
  453. package/lib/beads-health-check.js +0 -188
  454. package/lib/commands/commands-reset.js +0 -147
  455. package/opencode.json +0 -67
  456. package/scripts/beads-context.test.js +0 -584
  457. package/scripts/github-beads-sync/comment.mjs +0 -64
  458. package/scripts/github-beads-sync/config.mjs +0 -148
  459. package/scripts/github-beads-sync/github-api.mjs +0 -131
  460. package/scripts/github-beads-sync/index.mjs +0 -356
  461. package/scripts/github-beads-sync/label-mapper.mjs +0 -54
  462. package/scripts/github-beads-sync/mapping.mjs +0 -132
  463. package/scripts/github-beads-sync/reverse-sync-cli.mjs +0 -31
  464. package/scripts/github-beads-sync/reverse-sync.mjs +0 -162
  465. package/scripts/github-beads-sync/run-bd.mjs +0 -161
  466. package/scripts/github-beads-sync/sanitize.mjs +0 -121
  467. package/scripts/github-beads-sync.config.json +0 -26
  468. package/scripts/sync-commands.js +0 -600
@@ -0,0 +1,192 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Forge Stage Command (f61601ab)
5
+ *
6
+ * Records the REAL workflow phase of an issue into the kernel_stage_runs table so
7
+ * the phase is queryable — instead of being guessed from status+claim (a
8
+ * claimed-open issue with a merged PR would otherwise still show "dev"). Mirrors
9
+ * the worktree-linkage registry: direct kernel writes, idempotent per
10
+ * (issue_id, stage).
11
+ *
12
+ * forge stage <issue-id> <stage> --start Open a stage (status=active)
13
+ * forge stage <issue-id> <stage> --complete Close a stage (status=done)
14
+ * forge stage <issue-id> --current Print the current stage
15
+ * forge stage <issue-id> --list Print the full stage history
16
+ *
17
+ * `<stage>` is one of the canonical workflow stages (plan|dev|validate|ship|review|verify).
18
+ * Read the current stage back on `forge show <id>` (data.current_stage).
19
+ *
20
+ * @module commands/stage
21
+ */
22
+
23
+ const { STAGE_IDS, normalizeStageId } = require('../workflow/stages');
24
+ const { resolveIssueId } = require('../kernel/issue-id-resolver');
25
+
26
+ const STAGE_FLAGS = ['--start', '--complete', '--list', '--current', '--json'];
27
+ const VALUE_FLAGS = ['--substage'];
28
+ const USAGE = 'Usage: forge stage <issue-id> <stage> --start|--complete (reads: --current | --list)';
29
+
30
+ /**
31
+ * Parse the stage command's own flags out of the raw args (the global flag parser
32
+ * only recognizes an allowlist, so — like `worktree` — this command extracts its
33
+ * own). Returns { positional, opts } or { error }.
34
+ */
35
+ function parseStageArgs(args = []) {
36
+ const positional = [];
37
+ const opts = { start: false, complete: false, list: false, current: false, json: false, substage: null };
38
+
39
+ for (let i = 0; i < args.length; i += 1) {
40
+ const arg = args[i];
41
+ if (typeof arg !== 'string') continue;
42
+
43
+ const valueFlag = VALUE_FLAGS.find(flag => arg === flag || arg.startsWith(`${flag}=`));
44
+ if (valueFlag) {
45
+ let value;
46
+ if (arg.startsWith(`${valueFlag}=`)) {
47
+ value = arg.slice(`${valueFlag}=`.length);
48
+ } else {
49
+ value = args[i + 1];
50
+ i += 1;
51
+ }
52
+ if (!value || value.startsWith('--')) {
53
+ return { error: `Missing value for ${valueFlag}. ${USAGE}` };
54
+ }
55
+ opts.substage = value;
56
+ continue;
57
+ }
58
+
59
+ if (STAGE_FLAGS.includes(arg)) {
60
+ opts[arg.slice(2)] = true;
61
+ continue;
62
+ }
63
+
64
+ if (arg.startsWith('--')) {
65
+ return { error: `Unknown flag: ${arg}. ${USAGE}` };
66
+ }
67
+
68
+ positional.push(arg);
69
+ }
70
+
71
+ return { positional, opts };
72
+ }
73
+
74
+ async function resolveDriver(projectRoot, opts) {
75
+ if (opts._kernelDriver) return opts._kernelDriver;
76
+ const { buildMigratedKernelIssueDeps } = require('../kernel/cli-broker-factory');
77
+ return (await buildMigratedKernelIssueDeps({ projectRoot })).kernelDriver;
78
+ }
79
+
80
+ function render(opts, data, humanLines) {
81
+ if (opts.json || process.env.FORGE_JSON === '1') {
82
+ return { success: true, output: JSON.stringify(data, null, 2) };
83
+ }
84
+ return { success: true, output: humanLines.join('\n'), ...data };
85
+ }
86
+
87
+ module.exports = {
88
+ name: 'stage',
89
+ description: 'Record or read an issue\'s real workflow stage (stage_runs)',
90
+ usage: USAGE,
91
+ flags: {
92
+ '--start': 'Open a stage run (status=active)',
93
+ '--complete': 'Complete a stage run (status=done)',
94
+ '--substage': 'Optional substage label to record',
95
+ '--current': 'Print the current stage (latest active, else latest completed)',
96
+ '--list': 'Print the full stage-run history for the issue',
97
+ '--json': 'Emit machine-readable JSON',
98
+ },
99
+
100
+ /**
101
+ * @param {string[]} args - Positional + flag args after `stage`
102
+ * @param {object} _flags - Global parsed flags (unused; this command parses its own)
103
+ * @param {string} projectRoot - Project root path
104
+ * @param {object} [opts] - DI options (may inject `_kernelDriver`)
105
+ * @returns {Promise<object>}
106
+ */
107
+ handler: async (args, _flags, projectRoot, opts = {}) => {
108
+ const parsed = parseStageArgs(args);
109
+ if (parsed.error) {
110
+ return { success: false, error: parsed.error };
111
+ }
112
+ const { positional, opts: stageOpts } = parsed;
113
+
114
+ const issueRef = positional[0];
115
+ if (!issueRef) {
116
+ return { success: false, error: `Missing issue id. ${USAGE}` };
117
+ }
118
+
119
+ let driver;
120
+ try {
121
+ driver = await resolveDriver(projectRoot, opts);
122
+ } catch (error) {
123
+ return { success: false, error: `Kernel unavailable: ${error.message}` };
124
+ }
125
+
126
+ // Resolve prefixes / display handles to a full issue id (same resolver the
127
+ // issue commands use), so `forge stage 1a2b3c4d dev --start` works.
128
+ const resolution = await resolveIssueId(
129
+ issueRef,
130
+ (needle, limit) => driver.findIssueIdsByPrefix(needle, limit, {}, {}),
131
+ );
132
+ if (resolution.error) {
133
+ return { success: false, error: resolution.error };
134
+ }
135
+ const issueId = resolution.id;
136
+
137
+ // Read paths -----------------------------------------------------------
138
+ if (stageOpts.current) {
139
+ const current = driver.getCurrentStage({ issue_id: issueId }, {});
140
+ const data = {
141
+ issue_id: issueId,
142
+ current_stage: current ? current.stage : null,
143
+ current_stage_status: current ? current.status : null,
144
+ };
145
+ const human = current
146
+ ? `${issueId}: ${current.stage} (${current.status})`
147
+ : `${issueId}: no stage recorded`;
148
+ return render(stageOpts, data, [human]);
149
+ }
150
+
151
+ if (stageOpts.list) {
152
+ const runs = driver.listStageRuns({ issue_id: issueId }, {});
153
+ const data = { issue_id: issueId, stage_runs: runs };
154
+ const human = runs.length === 0
155
+ ? [`${issueId}: no stage runs`]
156
+ : runs.map(run => ` ${run.stage.padEnd(9)} ${run.status.padEnd(7)} started ${run.started_at}${run.completed_at ? ` → completed ${run.completed_at}` : ''}`);
157
+ return render(stageOpts, data, [`${issueId} stage history:`, ...human]);
158
+ }
159
+
160
+ // Write paths ----------------------------------------------------------
161
+ const stage = normalizeStageId(positional[1]);
162
+ if (!stage) {
163
+ return {
164
+ success: false,
165
+ error: `Invalid or missing stage "${positional[1] ?? ''}". Expected one of: ${STAGE_IDS.join(', ')}. ${USAGE}`,
166
+ };
167
+ }
168
+
169
+ if (stageOpts.start && stageOpts.complete) {
170
+ return { success: false, error: `Pass only one of --start or --complete. ${USAGE}` };
171
+ }
172
+ const action = stageOpts.complete ? 'complete' : 'start';
173
+
174
+ let row;
175
+ try {
176
+ row = driver.recordStageRun(
177
+ { issue_id: issueId, stage, action, substage: stageOpts.substage || null },
178
+ {},
179
+ );
180
+ } catch (error) {
181
+ // A FOREIGN KEY failure means the issue id does not exist in the kernel.
182
+ if (/FOREIGN KEY/i.test(String(error.message))) {
183
+ return { success: false, error: `Issue ${issueId} not found in the kernel.` };
184
+ }
185
+ return { success: false, error: `Failed to record stage: ${error.message}` };
186
+ }
187
+
188
+ const data = { issue_id: issueId, action, stage_run: row };
189
+ const verb = action === 'complete' ? 'completed' : 'started';
190
+ return render(stageOpts, data, [`${issueId}: ${verb} stage ${stage} (${row.status})`]);
191
+ },
192
+ };
@@ -0,0 +1,5 @@
1
+ 'use strict';
2
+
3
+ const { createIssueSubcommand } = require('./_issue');
4
+
5
+ module.exports = createIssueSubcommand('stale');
@@ -9,8 +9,12 @@ const {
9
9
  } = require('../workflow/state');
10
10
  const { STAGE_IDS } = require('../workflow/stages');
11
11
  const { detectWorktree } = require('../detect-worktree');
12
- const { readBeadsSnapshot } = require('../status/beads-snapshot');
13
- const { formatZeroArgStatus } = require('../status/presenter');
12
+ const { readStatusSnapshot } = require('../status/snapshot');
13
+ const {
14
+ buildPersonalStatusJson,
15
+ formatRunNextLines,
16
+ formatZeroArgStatus,
17
+ } = require('../status/presenter');
14
18
  const {
15
19
  loadState,
16
20
  extractWorkflowStateFromComments,
@@ -18,6 +22,12 @@ const {
18
22
  } = require('../workflow/state-manager');
19
23
  const { secureExecFileSync } = require('../shell-utils.js');
20
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.)
21
31
  const WORKFLOW_STAGES = {
22
32
  1: { name: 'Fresh Start', nextCommand: 'research' },
23
33
  2: { name: 'Research', nextCommand: 'research' },
@@ -30,6 +40,10 @@ const WORKFLOW_STAGES = {
30
40
  9: { name: 'Verification', nextCommand: 'verify' },
31
41
  };
32
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).
33
47
  const AUTHORITATIVE_STAGE_NAMES = {
34
48
  plan: 'Planning',
35
49
  dev: 'Development',
@@ -329,10 +343,15 @@ function detectStage(context) {
329
343
 
330
344
  function parseStatusInputs(args = [], flags = {}) {
331
345
  const statusArgs = Array.isArray(args) ? args : [];
346
+ const firstDefined = (...values) => values.find(value => value !== undefined && value !== null);
332
347
  const getNextValue = (flagName) => {
333
348
  const index = statusArgs.indexOf(flagName);
334
349
  if (index !== -1 && index + 1 < statusArgs.length) {
335
- return statusArgs[index + 1];
350
+ const candidate = statusArgs[index + 1];
351
+ if (typeof candidate === 'string' && candidate.startsWith('--')) {
352
+ return null;
353
+ }
354
+ return candidate;
336
355
  }
337
356
  return null;
338
357
  };
@@ -343,10 +362,23 @@ function parseStatusInputs(args = [], flags = {}) {
343
362
  };
344
363
 
345
364
  return {
346
- issueId: flags.issueId || flags['--issue-id'] || getInlineValue('--issue-id') || getNextValue('--issue-id'),
347
- workflowState: flags.workflowState || flags['--workflow-state'] || getInlineValue('--workflow-state') || getNextValue('--workflow-state'),
348
- bdComments: flags.bdComments || flags['--bd-comments'] || getInlineValue('--bd-comments') || getNextValue('--bd-comments'),
349
- 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
+ ),
350
382
  };
351
383
  }
352
384
 
@@ -408,17 +440,104 @@ function readIssueDetails(issueId, projectRoot = process.cwd()) {
408
440
  }
409
441
  }
410
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
+
411
530
  function extractDesignSlugs(design) {
412
531
  if (typeof design !== 'string' || design.trim() === '') {
413
532
  return [];
414
533
  }
415
534
 
416
535
  const slugs = new Set();
417
- const pattern = /docs[\\/]+plans[\\/]+\d{4}-\d{2}-\d{2}-([a-z0-9-]+?)-(?:design|tasks|decisions)\.md/g;
418
- let match = pattern.exec(design);
419
- while (match) {
420
- slugs.add(match[1]);
421
- match = pattern.exec(design);
536
+ for (const candidate of extractPathCandidates(design)) {
537
+ const slug = extractSlugFromDesignPath(candidate);
538
+ if (slug) {
539
+ slugs.add(slug);
540
+ }
422
541
  }
423
542
 
424
543
  return [...slugs];
@@ -450,7 +569,8 @@ function discoverCurrentIssue(snapshot, context, projectRoot = process.cwd()) {
450
569
  return hydratedIssues.get(issue.id);
451
570
  }
452
571
 
453
- const details = issue.design && issue.comments ? issue : readIssueDetails(issue.id, projectRoot);
572
+ const hasSufficientDetails = Boolean(issue.comments) && Boolean(issue.design);
573
+ const details = hasSufficientDetails ? issue : readIssueDetails(issue.id, projectRoot);
454
574
  const hydratedIssue = details ? { ...issue, ...details } : issue;
455
575
  hydratedIssues.set(issue.id, hydratedIssue);
456
576
  return hydratedIssue;
@@ -578,11 +698,8 @@ function collectCompletedChecks(result) {
578
698
  function formatAuthoritativeStatus(result) {
579
699
  const completedStages = result.workflowState.completedStages.map(stageId => stageId);
580
700
  const allowedCommands = result.nextStages.map(stageId => '/' + stageId).join(', ');
581
- const nextLines = [`Run now: /${result.runCommand}`];
701
+ const nextLines = formatRunNextLines(result);
582
702
 
583
- if (result.nextCommand) {
584
- nextLines.push(`Next after this: /${result.nextCommand}`);
585
- }
586
703
  if (allowedCommands) {
587
704
  nextLines.push(`Allowed after this: ${allowedCommands}`);
588
705
  }
@@ -633,7 +750,12 @@ function formatStatus(result) {
633
750
 
634
751
  module.exports = {
635
752
  name: 'status',
636
- 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
+ },
637
759
  handler: async (args, flags, projectRoot) => {
638
760
  const inputs = parseStatusInputs(args, flags);
639
761
  const isZeroArgStatus = !inputs.issueId && !inputs.workflowState && !inputs.bdComments;
@@ -647,7 +769,16 @@ module.exports = {
647
769
 
648
770
  if (isZeroArgStatus) {
649
771
  context = detectRepoContext(effectiveProjectRoot);
650
- snapshot = readBeadsSnapshot(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
+ });
651
782
  const discoveredIssue = discoverCurrentIssue(snapshot, context, effectiveProjectRoot);
652
783
  if (discoveredIssue) {
653
784
  inputs.issueId = discoveredIssue.id;
@@ -660,23 +791,29 @@ module.exports = {
660
791
  if (isZeroArgStatus) {
661
792
  if (workflowState) {
662
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 });
663
797
  return {
664
798
  success: true,
665
799
  context,
666
800
  snapshot,
667
801
  issueId: inputs.issueId,
668
- output: formatZeroArgStatus({ context, snapshot, workflowResult: result }),
802
+ output,
669
803
  ...result,
670
804
  };
671
805
  }
672
806
 
807
+ const output = inputs.json
808
+ ? JSON.stringify(buildPersonalStatusJson({ context, snapshot }), null, 2)
809
+ : formatZeroArgStatus({ context, snapshot, full: inputs.full });
673
810
  return {
674
811
  success: true,
675
812
  context,
676
813
  snapshot,
677
814
  issueId: inputs.issueId,
678
815
  missingWorkflowState: true,
679
- output: formatZeroArgStatus({ context, snapshot }),
816
+ output,
680
817
  ...(fallbackReason ? { fallbackReason } : {}),
681
818
  };
682
819
  }
@@ -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 {