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
@@ -3,15 +3,19 @@
3
3
  const { execFileSync, spawnSync } = require('node:child_process');
4
4
  const fs = require('node:fs');
5
5
  const path = require('node:path');
6
- const { bootstrapBeads } = require('../beads-bootstrap');
6
+ const { detectDefaultBranch } = require('../beads-sync-scaffold');
7
7
 
8
8
  /**
9
9
  * Forge Worktree Command
10
- * Manage isolated worktrees with Beads integration.
10
+ * Manage isolated worktrees.
11
11
  * Uses execFileSync (not execSync) to prevent command injection (OWASP A03).
12
12
  *
13
+ * The Kernel issue store lives in the git common dir, which every worktree of a
14
+ * repo shares, so no per-worktree issue-store bootstrap is needed — a new
15
+ * worktree already sees the same kernel.
16
+ *
13
17
  * Subcommands:
14
- * create <slug> - Create worktree at .worktrees/<slug> with branch + beads + install
18
+ * create <slug> - Create worktree at .worktrees/<slug> with branch + install
15
19
  * remove <slug> - Remove worktree via git worktree remove
16
20
  *
17
21
  * @module commands/worktree
@@ -24,7 +28,10 @@ const { bootstrapBeads } = require('../beads-bootstrap');
24
28
  * @returns {string|null} Package manager command or null
25
29
  */
26
30
  function detectPackageManager(projectRoot, fsApi) {
31
+ // Bun ships two lockfile formats: the legacy binary `bun.lockb` and the current
32
+ // text `bun.lock`. Recognize both so a Bun repo isn't mis-detected as npm.
27
33
  if (fsApi.existsSync(path.join(projectRoot, 'bun.lockb'))) return 'bun';
34
+ if (fsApi.existsSync(path.join(projectRoot, 'bun.lock'))) return 'bun';
28
35
  if (fsApi.existsSync(path.join(projectRoot, 'pnpm-lock.yaml'))) return 'pnpm';
29
36
  if (fsApi.existsSync(path.join(projectRoot, 'yarn.lock'))) return 'yarn';
30
37
  if (fsApi.existsSync(path.join(projectRoot, 'package-lock.json'))) return 'npm';
@@ -32,51 +39,6 @@ function detectPackageManager(projectRoot, fsApi) {
32
39
  return null;
33
40
  }
34
41
 
35
- /**
36
- * Stop a Dolt server running in a worktree to release file locks.
37
- * Tries graceful `bd dolt stop` first, then falls back to PID-based kill
38
- * from lock files. Never kills ALL Dolt processes - only the specific PID.
39
- *
40
- * @param {string} worktreePath - Absolute path to the worktree
41
- * @param {object} [opts] - Options for dependency injection
42
- * @param {Function} [opts._exec] - Override for execFileSync (testing)
43
- * @param {object} [opts._fs] - Override for fs module (testing)
44
- * @param {string} [opts._platform] - Override for process.platform (testing)
45
- * @returns {{ stopped: boolean, method: string, pid?: number }}
46
- */
47
- function stopDolt(worktreePath, opts = {}) {
48
- const exec = opts._exec || execFileSync;
49
- const _fs = opts._fs || fs;
50
- const platform = opts._platform || process.platform;
51
-
52
- // 1. Try graceful: bd dolt stop
53
- try {
54
- exec('bd', ['dolt', 'stop'], { cwd: worktreePath, timeout: 5000, stdio: 'pipe' });
55
- return { stopped: true, method: 'bd-dolt-stop' };
56
- } catch (_e) { /* intentional: bd not available, try PID-based kill */ } // NOSONAR S2486
57
-
58
- // 2. Try PID-based kill from lock file
59
- const lockPaths = [
60
- path.join(worktreePath, '.beads', 'dolt-server.lock'),
61
- path.join(worktreePath, '.beads', 'daemon.lock'),
62
- ];
63
- for (const lockPath of lockPaths) {
64
- try {
65
- const pid = Number.parseInt(_fs.readFileSync(lockPath, 'utf-8').trim(), 10);
66
- if (!Number.isNaN(pid)) {
67
- if (platform === 'win32') {
68
- exec('taskkill', ['/F', '/PID', String(pid)], { stdio: 'pipe' });
69
- } else {
70
- process.kill(pid, 'SIGTERM');
71
- }
72
- return { stopped: true, method: 'pid-kill', pid };
73
- }
74
- } catch (_e) { /* intentional: try next lock file */ } // NOSONAR S2486
75
- }
76
-
77
- return { stopped: false, method: 'none' };
78
- }
79
-
80
42
  /**
81
43
  * Check if a git branch already exists.
82
44
  * @param {string} branchName - Branch name to check
@@ -92,42 +54,224 @@ function branchExists(branchName, runFile) {
92
54
  }
93
55
  }
94
56
 
95
- function setupBeadsWithBootstrap(worktreePath, projectRoot, platform, fsApi, exec, options = {}) {
96
- if (!fsApi.existsSync(path.resolve(projectRoot, '.beads'))) {
97
- return 'Beads not installed - skipping .beads setup';
98
- }
99
-
100
- const bootstrapResult = bootstrapBeads(worktreePath, {
101
- _exec: exec,
102
- _fs: fsApi,
103
- _platform: platform,
104
- mainProjectRoot: projectRoot,
105
- _safeBeadsInit: options._safeBeadsInit,
106
- });
107
-
57
+ /**
58
+ * True when a git ref resolves to a commit (branch, tag, remote-tracking ref, or SHA).
59
+ * @param {string} ref - The ref to check
60
+ * @param {string} projectRoot - Repo root to run git in
61
+ * @param {Function} runFile - execFileSync function (for DI)
62
+ * @returns {boolean}
63
+ */
64
+ function refExists(ref, projectRoot, runFile) {
108
65
  try {
109
- exec('bd', ['--version'], { cwd: worktreePath, timeout: 5000, stdio: 'pipe' });
110
- } catch (_bdErr) { /* intentional: bd may not be on PATH */ // NOSONAR S2486
111
- if (bootstrapResult.success === false && bootstrapResult.warning) {
112
- return bootstrapResult.warning;
113
- }
114
- return 'Beads verification failed - .beads linked but bd may not work';
66
+ runFile('git', ['-C', projectRoot, 'rev-parse', '--verify', '--quiet', `${ref}^{commit}`], { stdio: ['pipe', 'pipe', 'pipe'] });
67
+ return true;
68
+ } catch (_err) { /* intentional: ref does not resolve */ // NOSONAR S2486
69
+ return false;
115
70
  }
71
+ }
72
+
73
+ /**
74
+ * Resolve the base ref a NEW worktree branch should fork from. Defaults to the
75
+ * repo's DEFAULT branch (NOT the checkout's current HEAD) so a worktree created
76
+ * off a WIP branch never silently inherits unrelated commits. Prefers the remote
77
+ * default (`origin/<default>`) when present, else the local default branch, and
78
+ * falls back to the detected name so git surfaces a clear error if neither exists.
79
+ * @param {string} projectRoot - Repo root
80
+ * @param {Function} runFile - execFileSync function (for DI)
81
+ * @returns {string} The base ref to pass to `git worktree add ... <base>`
82
+ */
83
+ function resolveDefaultBase(projectRoot, runFile) {
84
+ const def = detectDefaultBranch(projectRoot, { _exec: runFile });
85
+ const originRef = `origin/${def}`;
86
+ if (refExists(originRef, projectRoot, runFile)) return originRef;
87
+ if (refExists(def, projectRoot, runFile)) return def;
88
+ return def;
89
+ }
116
90
 
117
- return bootstrapResult.warning;
91
+ /**
92
+ * True when a link failure is a privilege/support problem (Windows without the
93
+ * symlink privilege, restricted FS) rather than a real error. In that case we
94
+ * degrade to a package install instead of hard-failing.
95
+ * @param {Error} error
96
+ * @returns {boolean}
97
+ */
98
+ function isLinkPermissionError(error) {
99
+ return ['EPERM', 'EACCES', 'ENOSYS', 'UV_EPERM'].includes(error && error.code);
118
100
  }
119
101
 
120
102
  /**
121
- * Run package install in the new worktree if a package manager is detected.
103
+ * Run package install in the new worktree if a package manager is detected, and
104
+ * SURFACE failures (spawn error or non-zero exit) instead of swallowing them.
122
105
  * @param {string} worktreePath - Absolute path to the new worktree
123
106
  * @param {string} projectRoot - Absolute path to the project root
124
107
  * @param {Function} spawnFn - spawnSync-compatible function
125
108
  * @param {object} fsApi - fs module (for DI)
109
+ * @returns {{ linked: boolean, installed: boolean }}
110
+ * @throws {Error} when the install cannot be spawned or exits non-zero.
126
111
  */
127
112
  function runInstall(worktreePath, projectRoot, spawnFn, fsApi) {
128
113
  const pkgManager = detectPackageManager(projectRoot, fsApi);
129
- if (pkgManager) {
130
- spawnFn(pkgManager, ['install'], { cwd: worktreePath, stdio: 'pipe' });
114
+ if (!pkgManager) return { linked: false, installed: false };
115
+
116
+ // shell:true on Windows: npm/pnpm/yarn are .cmd shims that cannot be spawned
117
+ // directly (ENOENT / EINVAL). pkgManager comes from lockfile detection (fixed
118
+ // set) and args are hardcoded, so no user input reaches the shell.
119
+ const result = spawnFn(pkgManager, ['install'], {
120
+ cwd: worktreePath,
121
+ stdio: 'pipe',
122
+ shell: process.platform === 'win32',
123
+ });
124
+ if (result && result.error) {
125
+ throw new Error(`Dependency install failed: could not run '${pkgManager} install' in ${worktreePath}: ${result.error.message}`);
126
+ }
127
+ if (result && typeof result.status === 'number' && result.status !== 0) {
128
+ throw new Error(`Dependency install failed: '${pkgManager} install' exited with code ${result.status} in ${worktreePath}`);
129
+ }
130
+ return { linked: false, installed: true };
131
+ }
132
+
133
+ /**
134
+ * Populate the new worktree's node_modules. Fast path: link to the main repo's
135
+ * shared install (a junction on Windows, a directory symlink on POSIX) so a fresh
136
+ * worktree is immediately usable without a full reinstall — this repo's own
137
+ * worktrees follow that pattern. Fallback: run the detected package manager's
138
+ * install when there is no shared install to link. Both paths surface failures.
139
+ * @param {string} worktreePath - Absolute path to the new worktree
140
+ * @param {string} projectRoot - Absolute path to the project root
141
+ * @param {object} deps
142
+ * @param {Function} deps.spawnFn - spawnSync-compatible function
143
+ * @param {object} deps.fsApi - fs module (for DI)
144
+ * @param {string} deps.platform - process.platform value
145
+ * @returns {{ linked: boolean, installed: boolean }}
146
+ * @throws {Error} when linking fails for a non-privilege reason or install fails.
147
+ */
148
+ function setupWorktreeDeps(worktreePath, projectRoot, { spawnFn, fsApi, platform }) {
149
+ const srcModules = path.join(projectRoot, 'node_modules');
150
+ const destModules = path.join(worktreePath, 'node_modules');
151
+
152
+ // Already populated (e.g. git checkout carried it) — nothing to do.
153
+ if (fsApi.existsSync(destModules)) return { linked: false, installed: false };
154
+
155
+ // Fast path: link to the shared install when the main repo has one.
156
+ if (fsApi.existsSync(srcModules)) {
157
+ const linkType = platform === 'win32' ? 'junction' : 'dir';
158
+ try {
159
+ fsApi.symlinkSync(srcModules, destModules, linkType);
160
+ return { linked: true, installed: false };
161
+ } catch (error) {
162
+ // Degrade to a real install only when the link was refused for lack of
163
+ // privilege/support; any other failure is surfaced to the caller.
164
+ if (!isLinkPermissionError(error)) throw error;
165
+ }
166
+ }
167
+
168
+ return runInstall(worktreePath, projectRoot, spawnFn, fsApi);
169
+ }
170
+
171
+ /**
172
+ * Best-effort: record the issue → worktree → work-folder linkage in the Kernel so
173
+ * `forge worktree list` and orientation READ it instead of guessing the work-folder by
174
+ * a filesystem heuristic. Writes a `kernel_worktrees` row keyed by the absolute worktree
175
+ * path (idempotent upsert) and, when an issue + work-folder are given, drops a
176
+ * machine-readable `.forge-issue` marker so a folder resolves to its issue deterministically.
177
+ *
178
+ * Never throws: a repo without a reachable Kernel (or without the 007 linkage columns)
179
+ * must still get a usable worktree — orientation falls back to the folder heuristic.
180
+ *
181
+ * @param {object} params
182
+ * @param {string} params.projectRoot
183
+ * @param {string} params.worktreePath - Absolute worktree path (the row key).
184
+ * @param {string} params.branch
185
+ * @param {string|null} params.issueId
186
+ * @param {string|null} params.workFolder - Repo-relative work-folder path.
187
+ * @param {object} params.opts - DI options (may inject `_kernelDriver`, `_exec`, `_fs`).
188
+ * @returns {Promise<{registered: boolean, issueId: string|null, workFolder: string|null, reason?: string}>}
189
+ */
190
+ async function registerWorktreeLinkage({ projectRoot, worktreePath, branch, issueId, workFolder, opts }) {
191
+ const runFile = opts._exec || execFileSync;
192
+ const fsApi = opts._fs || fs;
193
+ try {
194
+ // Drop the folder → issue marker first — deterministic even if the DB write fails.
195
+ if (workFolder && issueId) {
196
+ const folderAbs = path.resolve(projectRoot, workFolder);
197
+ fsApi.mkdirSync(folderAbs, { recursive: true });
198
+ fsApi.writeFileSync(path.join(folderAbs, '.forge-issue'), `${issueId}\n`, 'utf8');
199
+ }
200
+
201
+ let driver = opts._kernelDriver;
202
+ if (!driver) {
203
+ // No reachable git repo → no shared Kernel; skip quietly (orientation still works
204
+ // off the folder heuristic). Avoids spawning `git` in a non-repo directory.
205
+ if (!fsApi.existsSync(path.join(projectRoot, '.git'))) {
206
+ return { registered: false, issueId: issueId || null, workFolder: workFolder || null, reason: 'no git repository' };
207
+ }
208
+ // Lazy require: keep the worktree command light and avoid paying kernel setup
209
+ // when linkage is not needed. Migrated deps guarantee the 007 columns exist.
210
+ const { buildMigratedKernelIssueDeps } = require('../kernel/cli-broker-factory');
211
+ driver = (await buildMigratedKernelIssueDeps({ projectRoot })).kernelDriver;
212
+ }
213
+
214
+ const { resolveGitCommonDir } = require('../kernel/broker');
215
+ const gitCommonDir = resolveGitCommonDir(projectRoot, { execFileSync: runFile });
216
+
217
+ driver.registerWorktree({
218
+ git_common_dir: gitCommonDir,
219
+ path: worktreePath,
220
+ branch,
221
+ actor: process.env.FORGE_ACTOR || null,
222
+ issue_id: issueId || null,
223
+ work_folder: workFolder || null,
224
+ registered_at: new Date().toISOString(),
225
+ state: 'active',
226
+ });
227
+
228
+ return { registered: true, issueId: issueId || null, workFolder: workFolder || null };
229
+ } catch (error) {
230
+ // Non-fatal: keep the worktree usable. Only warn when the caller explicitly asked
231
+ // for linkage (--issue/--work-folder); a plain `worktree create` stays quiet.
232
+ if (issueId || workFolder) {
233
+ process.stderr.write(`forge worktree: kernel linkage not recorded (${error.message})\n`);
234
+ }
235
+ return { registered: false, issueId: issueId || null, workFolder: workFolder || null, reason: error.message };
236
+ }
237
+ }
238
+
239
+ /**
240
+ * Auto-file rail: guarantee the new worktree's branch has a backing Kernel issue,
241
+ * WITHOUT anyone remembering `forge issue create` (kernel issue a4b8f56f). Best-effort
242
+ * and NON-BLOCKING — the worktree must stay usable even if tracking fails, so any error
243
+ * degrades to a stderr warning and null. Skipped when `--issue` already links a real
244
+ * issue. Idempotent via ensureBackingIssue (deduped by branch), so re-creating a
245
+ * worktree or later `forge push`/pre-push firing never mints a duplicate.
246
+ *
247
+ * @param {object} params
248
+ * @param {string} params.projectRoot
249
+ * @param {string} params.worktreePath
250
+ * @param {string} params.branch
251
+ * @param {string|null} params.issueId - explicit --issue (skip auto-file when set).
252
+ * @param {object} params.opts - DI (may inject `_ensureBackingIssue`, `_kernelDriver`, `_kernelBroker`, `_fs`).
253
+ * @returns {Promise<object|null>} the backing-issue descriptor, or null when skipped/unavailable.
254
+ */
255
+ async function autoFileBackingIssue({ projectRoot, worktreePath, branch, issueId, opts }) {
256
+ if (issueId) return null; // explicit --issue already links a real issue.
257
+ const fsApi = opts._fs || fs;
258
+ try {
259
+ // Lazy require INSIDE the try: a module-load failure (missing file / broken
260
+ // sub-dependency) must degrade to null, never crash `forge worktree create`.
261
+ const ensureFn = opts._ensureBackingIssue || require('../kernel/backing-issue').ensureBackingIssue;
262
+ let driver = opts._kernelDriver;
263
+ let broker = opts._kernelBroker;
264
+ if (!driver || !broker) {
265
+ if (!fsApi.existsSync(path.join(projectRoot, '.git'))) return null; // no repo → no kernel
266
+ const { buildMigratedKernelIssueDeps } = require('../kernel/cli-broker-factory');
267
+ const deps = await buildMigratedKernelIssueDeps({ projectRoot });
268
+ driver = driver || deps.kernelDriver;
269
+ broker = broker || deps.kernelBroker;
270
+ }
271
+ return await ensureFn({ branch, projectRoot, worktreePath, driver, broker });
272
+ } catch (error) {
273
+ process.stderr.write(`forge worktree: backing issue not auto-filed (${error.message})\n`);
274
+ return null;
131
275
  }
132
276
  }
133
277
 
@@ -143,9 +287,10 @@ async function handleCreate(slug, flags, projectRoot, opts) {
143
287
  const runFile = opts._exec || execFileSync;
144
288
  const runSpawn = opts._spawn || spawnSync;
145
289
  const fsApi = opts._fs || fs;
146
- const platform = opts._platform || process.platform;
147
290
 
148
291
  const branchName = flags['--branch'] || flags.branch || `feat/${slug}`;
292
+ const issueId = flags['--issue'] || null;
293
+ const workFolder = flags['--work-folder'] || null;
149
294
  const worktreesDir = path.resolve(projectRoot, '.worktrees');
150
295
 
151
296
  // Guard: Detect bare repo state - worktrees created from bare repos produce broken branches
@@ -168,42 +313,103 @@ async function handleCreate(slug, flags, projectRoot, opts) {
168
313
 
169
314
  const worktreePath = path.resolve(worktreesDir, slug);
170
315
 
316
+ // Validate an explicit --base up front so a bad ref errors BEFORE anything is
317
+ // created (no branch, no worktree dir). Base only applies when creating a NEW
318
+ // branch; an existing branch is checked out as-is.
319
+ const explicitBase = flags['--base'] || null;
320
+ if (explicitBase && !refExists(explicitBase, projectRoot, runFile)) {
321
+ return { success: false, error: `Invalid --base: ref '${explicitBase}' not found. Verify it exists (git rev-parse --verify ${explicitBase}).` };
322
+ }
323
+
171
324
  // Step 0: Check if worktree already exists
172
325
  if (fsApi.existsSync(worktreePath)) {
326
+ // A pre-existing worktree may be checked out on a different branch than the
327
+ // requested branchName — read the actual HEAD so the kernel row is accurate.
328
+ let existingBranch = branchName;
329
+ try {
330
+ const head = runFile('git', ['-C', worktreePath, 'rev-parse', '--abbrev-ref', 'HEAD'], { encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();
331
+ // A detached worktree reports the literal "HEAD" — don't persist that; keep branchName.
332
+ if (head && head !== 'HEAD') existingBranch = head;
333
+ } catch (_error) { /* not a resolvable worktree HEAD — fall back to branchName */ }
334
+ const linkage = await registerWorktreeLinkage({ projectRoot, worktreePath, branch: existingBranch, issueId, workFolder, opts });
335
+ const backing = await autoFileBackingIssue({ projectRoot, worktreePath, branch: existingBranch, issueId, opts });
173
336
  return {
174
337
  success: true,
175
338
  reused: true,
176
339
  message: `Worktree already exists at ${worktreePath}`,
177
340
  worktreePath,
341
+ linkage,
342
+ backing,
178
343
  };
179
344
  }
180
345
 
181
346
  // Step 1: Ensure .worktrees/ dir exists
182
347
  fsApi.mkdirSync(worktreesDir, { recursive: true });
183
348
 
184
- // Step 2: Create git worktree
349
+ // Step 2: Create git worktree. For a NEW branch, fork from an explicit --base or
350
+ // the repo's DEFAULT branch — NOT the checkout's current HEAD — so the worktree
351
+ // never silently inherits unrelated WIP commits (B2). An existing branch is
352
+ // checked out as-is (no base applies).
185
353
  const hasBranch = branchExists(branchName, runFile);
354
+ let base = null;
186
355
  if (hasBranch) {
187
356
  runFile('git', ['worktree', 'add', worktreePath, branchName], { stdio: 'pipe' });
188
357
  } else {
189
- runFile('git', ['worktree', 'add', worktreePath, '-b', branchName], { stdio: 'pipe' });
358
+ base = explicitBase || resolveDefaultBase(projectRoot, runFile);
359
+ runFile('git', ['worktree', 'add', worktreePath, '-b', branchName, base], { stdio: 'pipe' });
190
360
  }
191
361
 
192
- // Step 3: Beads integration
193
- const beadsWarning = setupBeadsWithBootstrap(worktreePath, projectRoot, platform, fsApi, runFile, opts);
362
+ // Step 3: Populate node_modules (link to the shared install, else install).
363
+ // No per-worktree issue-store bootstrap is needed — the Kernel DB lives in the
364
+ // shared git common dir, so the new worktree already sees the same kernel.
365
+ let deps;
366
+ try {
367
+ deps = setupWorktreeDeps(worktreePath, projectRoot, {
368
+ spawnFn: runSpawn,
369
+ fsApi,
370
+ platform: opts._platform || process.platform,
371
+ });
372
+ } catch (error) {
373
+ // Surface the failure instead of leaving a worktree with no usable deps.
374
+ return { success: false, error: error.message, worktreePath, branch: branchName };
375
+ }
194
376
 
195
- // Step 4: Run package install
196
- runInstall(worktreePath, projectRoot, runSpawn, fsApi);
377
+ const linkage = await registerWorktreeLinkage({ projectRoot, worktreePath, branch: branchName, issueId, workFolder, opts });
378
+ const backing = await autoFileBackingIssue({ projectRoot, worktreePath, branch: branchName, issueId, opts });
197
379
 
198
- const result = {
380
+ // Report the base so the fork point is never silent. `base` is null when an
381
+ // existing branch was checked out (no fork happened).
382
+ const baseNote = base ? `based on ${base}` : `existing branch ${branchName}`;
383
+ return {
199
384
  success: true,
200
385
  worktreePath,
201
386
  branch: branchName,
387
+ base,
388
+ depsLinked: deps.linked,
389
+ depsInstalled: deps.installed,
390
+ linkage,
391
+ backing,
392
+ output: `Created worktree ${worktreePath} on ${branchName} (${baseNote}).`,
202
393
  };
203
- if (beadsWarning) {
204
- result.beadsWarning = beadsWarning;
394
+ }
395
+
396
+ /**
397
+ * Handle the "list" subcommand — read the worktree registry from the Kernel.
398
+ * @param {string} projectRoot - Project root path
399
+ * @param {object} opts - DI options (may inject `_kernelDriver`)
400
+ * @returns {Promise<object>} Result object with a `worktrees` array
401
+ */
402
+ async function handleList(projectRoot, opts) {
403
+ try {
404
+ let driver = opts._kernelDriver;
405
+ if (!driver) {
406
+ const { buildMigratedKernelIssueDeps } = require('../kernel/cli-broker-factory');
407
+ driver = (await buildMigratedKernelIssueDeps({ projectRoot })).kernelDriver;
408
+ }
409
+ return { success: true, worktrees: driver.listWorktrees({}) };
410
+ } catch (error) {
411
+ return { success: false, error: error.message, worktrees: [] };
205
412
  }
206
- return result;
207
413
  }
208
414
 
209
415
  /**
@@ -220,29 +426,66 @@ async function handleRemove(slug, projectRoot, opts) {
220
426
  }
221
427
 
222
428
  const runFile = opts._exec || execFileSync;
223
- const fsModule = opts._fs || fs;
224
429
  const worktreePath = path.resolve(projectRoot, '.worktrees', slug);
225
430
 
226
- // Stop Dolt server to release file locks
227
- const stopResult = stopDolt(worktreePath, { _exec: runFile, _fs: fsModule, _platform: opts._platform });
228
- if (stopResult.stopped) {
229
- // Brief wait for file locks to release (Windows needs this)
230
- await new Promise(r => setTimeout(r, 500));
231
- }
232
-
431
+ // No issue-store server to stop: the Kernel SQLite DB lives in the shared git
432
+ // common dir, so removing a worktree directory never touches it.
233
433
  runFile('git', ['worktree', 'remove', worktreePath], { stdio: 'pipe' });
234
434
 
235
435
  return { success: true, removed: worktreePath };
236
436
  }
237
437
 
438
+ // Long flags that take a value, in both `--flag value` and `--flag=value` forms.
439
+ const WORKTREE_VALUE_FLAGS = ['--branch', '--issue', '--work-folder', '--base'];
440
+ const WORKTREE_USAGE_HINT = 'Usage: forge worktree create <slug> [--branch <name>] [--base <ref>] [--issue <id>] [--work-folder <path>]';
441
+
442
+ function parseWorktreeArgs(args, flags) {
443
+ const positional = [];
444
+ const parsedFlags = { ...flags };
445
+
446
+ for (let i = 0; i < args.length; i++) {
447
+ const arg = args[i];
448
+
449
+ const spaceFlag = WORKTREE_VALUE_FLAGS.find(flag => arg === flag);
450
+ if (spaceFlag) {
451
+ const value = args[i + 1];
452
+ if (!value || value.startsWith('--')) {
453
+ return { error: `Missing value for ${spaceFlag}. ${WORKTREE_USAGE_HINT}` };
454
+ }
455
+ parsedFlags[spaceFlag] = value;
456
+ i++;
457
+ continue;
458
+ }
459
+
460
+ const eqFlag = WORKTREE_VALUE_FLAGS.find(flag => arg.startsWith(`${flag}=`));
461
+ if (eqFlag) {
462
+ const value = arg.slice(`${eqFlag}=`.length);
463
+ if (!value) {
464
+ return { error: `Missing value for ${eqFlag}. ${WORKTREE_USAGE_HINT}` };
465
+ }
466
+ parsedFlags[eqFlag] = value;
467
+ continue;
468
+ }
469
+
470
+ positional.push(arg);
471
+ }
472
+
473
+ // Back-compat alias consumed by handleCreate (flags['--branch'] || flags.branch).
474
+ if (parsedFlags['--branch']) parsedFlags.branch = parsedFlags['--branch'];
475
+
476
+ return { args: positional, flags: parsedFlags };
477
+ }
478
+
238
479
  module.exports = {
239
480
  name: 'worktree',
240
- description: 'Manage isolated worktrees with Beads integration',
241
- usage: 'forge worktree <create|remove> <slug>',
481
+ description: 'Manage isolated worktrees',
482
+ usage: 'forge worktree <create|remove|list> <slug>',
242
483
  flags: {
243
484
  '--branch': 'Custom branch name (default: feat/<slug>)',
485
+ '--base': 'Base ref a new branch forks from (default: the repo default branch, e.g. origin/main)',
486
+ '--issue': 'Kernel issue id to link this worktree to (records issue → worktree)',
487
+ '--work-folder': 'Repo-relative work-folder this issue owns (records worktree → work-folder + drops a .forge-issue marker)',
244
488
  },
245
- stopDolt,
246
489
 
247
490
  /**
248
491
  * Main handler for the worktree command.
@@ -257,27 +500,47 @@ module.exports = {
257
500
  * @returns {Promise<object>}
258
501
  */
259
502
  handler: async (args, flags, projectRoot, opts = {}) => {
260
- const subcommand = args[0];
261
- const slug = args[1];
503
+ const parsed = parseWorktreeArgs(args, flags);
504
+ if (parsed.error) {
505
+ return { success: false, error: parsed.error };
506
+ }
507
+
508
+ const subcommand = parsed.args[0];
262
509
 
263
- if (!subcommand || !['create', 'remove'].includes(subcommand)) {
510
+ if (!subcommand || !['create', 'remove', 'list'].includes(subcommand)) {
264
511
  return {
265
512
  success: false,
266
- error: 'Missing or invalid subcommand. Usage: forge worktree <create|remove> <slug>',
513
+ error: 'Missing or invalid subcommand. Usage: forge worktree <create|remove|list> <slug>',
267
514
  };
268
515
  }
269
516
 
517
+ // `list` reads the Kernel registry and takes no slug.
518
+ if (subcommand === 'list') {
519
+ return handleList(projectRoot, opts);
520
+ }
521
+
522
+ const slug = parsed.args[1];
270
523
  if (!slug) {
271
524
  return {
272
525
  success: false,
273
- error: 'Missing slug. Usage: forge worktree <create|remove> <slug>',
526
+ error: 'Missing slug. Usage: forge worktree <create|remove|list> <slug>',
274
527
  };
275
528
  }
276
529
 
277
530
  if (subcommand === 'create') {
278
- return handleCreate(slug, flags, projectRoot, opts);
531
+ return handleCreate(slug, parsed.flags, projectRoot, opts);
279
532
  }
280
533
 
281
534
  return handleRemove(slug, projectRoot, opts);
282
535
  },
536
+
537
+ // Exposed for unit tests; not part of the CLI surface.
538
+ _internal: {
539
+ detectPackageManager,
540
+ setupWorktreeDeps,
541
+ runInstall,
542
+ autoFileBackingIssue,
543
+ refExists,
544
+ resolveDefaultBase,
545
+ },
283
546
  };