forge-workflow 0.0.10 → 0.1.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (454) hide show
  1. package/.claude/rules/{greptile-review-process.md → review-process.md} +56 -41
  2. package/.claude/scripts/{greptile-resolve.sh → review-resolve.sh} +13 -3
  3. package/.cursor/rules/permissions-guidance.mdc +2 -2
  4. package/.forge/hooks/check-tdd.js +3 -0
  5. package/.forge/hooks/forge-native-hook.js +245 -0
  6. package/.forge/protected-paths.yaml +157 -0
  7. package/AGENTS.md +150 -61
  8. package/CHANGELOG.md +681 -0
  9. package/CLAUDE.md +9 -118
  10. package/QUICKSTART.md +171 -0
  11. package/README.md +271 -363
  12. package/bin/forge-cmd.js +120 -9
  13. package/bin/forge-preflight.js +26 -5
  14. package/bin/forge.js +461 -489
  15. package/docs/INDEX.md +93 -0
  16. package/docs/PROJECT_DESIGN.md +685 -0
  17. package/docs/architecture/index.md +66 -0
  18. package/docs/architecture/notes/README.md +35 -0
  19. package/docs/architecture/subsystems/README.md +46 -0
  20. package/docs/forge/TOOLCHAIN.md +670 -0
  21. package/docs/forge/VALIDATION.md +82 -0
  22. package/docs/{AGENT_INSTALL_PROMPT.md → guides/AGENT_INSTALL_PROMPT.md} +3 -3
  23. package/docs/guides/BEADS_GITHUB_SYNC.md +32 -0
  24. package/docs/{ENHANCED_ONBOARDING.md → guides/ENHANCED_ONBOARDING.md} +16 -12
  25. package/docs/guides/GREPTILE_SETUP.md +46 -0
  26. package/docs/guides/MANUAL_REVIEW_GUIDE.md +58 -0
  27. package/docs/guides/MIGRATION.md +56 -0
  28. package/docs/guides/SETUP.md +118 -0
  29. package/docs/guides/SUPPORT.md +185 -0
  30. package/docs/guides/WORKFLOW_TEMPLATES.md +74 -0
  31. package/docs/guides/memory-backends.md +183 -0
  32. package/docs/reference/ADAPTERS.md +128 -0
  33. package/docs/reference/AGENT_SKILL_PARITY.md +175 -0
  34. package/docs/reference/COMMANDS.md +205 -0
  35. package/docs/reference/DECISION_DRIFT_GUARDS.md +97 -0
  36. package/docs/{EXAMPLES.md → reference/EXAMPLES.md} +7 -5
  37. package/docs/reference/FORGE_KERNEL_STORAGE_MODEL.md +135 -0
  38. package/docs/reference/HERMES_INTEGRATION.md +118 -0
  39. package/docs/reference/INSIGHTS_RECAP.md +63 -0
  40. package/docs/reference/INSTALL.md +164 -0
  41. package/docs/reference/KERNEL_TAXONOMY_VALIDATION.md +161 -0
  42. package/docs/reference/PROTECTED_PATH_MANIFEST.md +25 -0
  43. package/docs/reference/RELEASE.md +68 -0
  44. package/docs/reference/RESEARCH_TEMPLATE.md +292 -0
  45. package/docs/{ROADMAP.md → reference/ROADMAP.md} +12 -9
  46. package/docs/reference/SKILLS.md +35 -0
  47. package/docs/reference/STATUS_BOARD.md +80 -0
  48. package/docs/reference/TEMPLATES.md +106 -0
  49. package/docs/{TOOLCHAIN.md → reference/TOOLCHAIN.md} +62 -47
  50. package/docs/reference/VALIDATION.md +82 -0
  51. package/docs/reference/agent-permissions.md +169 -0
  52. package/docs/reference/beads-to-kernel-migration-ux.md +61 -0
  53. package/docs/reference/control-plane-guarantees.md +125 -0
  54. package/docs/reference/dependency-chain.md +331 -0
  55. package/docs/reference/forge-kernel-issue-command-contract.md +161 -0
  56. package/docs/reference/forge-kernel-schema.md +72 -0
  57. package/docs/reference/kernel-conflict-evaluators.md +27 -0
  58. package/docs/reference/patch-md-format.md +77 -0
  59. package/docs/reference/protected-state-surfaces.md +59 -0
  60. package/docs/reference/shepherd.md +115 -0
  61. package/docs/reference/superpowers-analysis.md +320 -0
  62. package/docs/reference/superpowers-integration-options.md +404 -0
  63. package/docs/reference/test-environment.md +519 -0
  64. package/docs/reference/upgrade-safety.md +59 -0
  65. package/lefthook.yml +18 -0
  66. package/lib/adapter-cli.js +307 -0
  67. package/lib/adapters/beads-issue-adapter.js +127 -0
  68. package/lib/adapters/beads-kernel-compat.js +1042 -0
  69. package/lib/adapters/greptile-review-adapter.js +141 -0
  70. package/lib/adapters/kernel-issue-adapter.js +101 -0
  71. package/lib/adapters/pr-state-adapter.js +484 -0
  72. package/lib/adoption-profiles.js +126 -0
  73. package/lib/agents/README.md +2 -6
  74. package/lib/agents/claude.plugin.json +3 -8
  75. package/lib/agents/codex.plugin.json +9 -1
  76. package/lib/agents/cursor.plugin.json +2 -6
  77. package/lib/agents/hermes.plugin.json +22 -0
  78. package/lib/agents-config.js +39 -1236
  79. package/lib/audit-evidence.js +282 -0
  80. package/lib/beads-setup.js +121 -0
  81. package/lib/beads-sync-scaffold.js +25 -101
  82. package/lib/codex-skills.js +51 -1
  83. package/lib/commands/_issue.js +741 -77
  84. package/lib/commands/_manifest.js +91 -0
  85. package/lib/commands/_registry.js +85 -34
  86. package/lib/commands/_resolve-command-opts.js +261 -0
  87. package/lib/commands/_serve-security.js +270 -0
  88. package/lib/commands/adapter.js +12 -0
  89. package/lib/commands/add.js +118 -0
  90. package/lib/commands/audit.js +70 -0
  91. package/lib/commands/blocked.js +5 -0
  92. package/lib/commands/board.js +64 -0
  93. package/lib/commands/claim.js +21 -2
  94. package/lib/commands/claims.js +7 -0
  95. package/lib/commands/clean.js +485 -75
  96. package/lib/commands/close.js +2 -2
  97. package/lib/commands/comment.js +5 -0
  98. package/lib/commands/control.js +148 -0
  99. package/lib/commands/create.js +2 -2
  100. package/lib/commands/dev.js +185 -7
  101. package/lib/commands/doc-gate.js +336 -0
  102. package/lib/commands/doctor.js +156 -0
  103. package/lib/commands/explain.js +15 -0
  104. package/lib/commands/export.js +237 -0
  105. package/lib/commands/gate.js +192 -0
  106. package/lib/commands/hooks.js +242 -0
  107. package/lib/commands/inbox.js +118 -0
  108. package/lib/commands/init.js +598 -0
  109. package/lib/commands/insights.js +79 -0
  110. package/lib/commands/issue.js +12 -1
  111. package/lib/commands/issues.js +17 -0
  112. package/lib/commands/lint.js +5 -0
  113. package/lib/commands/list.js +2 -2
  114. package/lib/commands/merge.js +312 -0
  115. package/lib/commands/migrate.js +523 -0
  116. package/lib/commands/new.js +12 -0
  117. package/lib/commands/options.js +241 -0
  118. package/lib/commands/orient.js +13 -0
  119. package/lib/commands/orphans.js +5 -0
  120. package/lib/commands/patch.js +67 -0
  121. package/lib/commands/plan.js +436 -24
  122. package/lib/commands/preflight.js +211 -0
  123. package/lib/commands/prime.js +13 -0
  124. package/lib/commands/push.js +69 -2
  125. package/lib/commands/ready.js +2 -2
  126. package/lib/commands/recall.js +116 -0
  127. package/lib/commands/recap.js +61 -0
  128. package/lib/commands/recommend.js +0 -1
  129. package/lib/commands/release.js +91 -0
  130. package/lib/commands/remember.js +74 -0
  131. package/lib/commands/role.js +99 -0
  132. package/lib/commands/serve.js +581 -0
  133. package/lib/commands/setup.js +838 -972
  134. package/lib/commands/shepherd.js +436 -0
  135. package/lib/commands/ship.js +23 -1
  136. package/lib/commands/show.js +2 -2
  137. package/lib/commands/stage.js +192 -0
  138. package/lib/commands/stale.js +5 -0
  139. package/lib/commands/status.js +158 -21
  140. package/lib/commands/sync.js +34 -46
  141. package/lib/commands/team.js +4 -1
  142. package/lib/commands/test.js +43 -27
  143. package/lib/commands/update.js +2 -2
  144. package/lib/commands/upgrade.js +47 -0
  145. package/lib/commands/validate.js +43 -18
  146. package/lib/commands/worktree.js +307 -100
  147. package/lib/config-writer.js +202 -0
  148. package/lib/control-plane.js +236 -0
  149. package/lib/core/runtime-graph.js +946 -0
  150. package/lib/dep-guard/keyword-ripple.js +2 -2
  151. package/lib/deprecated-sync-cleanup.js +362 -0
  152. package/lib/detect-agent.js +2 -28
  153. package/lib/detect-worktree.js +35 -9
  154. package/lib/doc-gate/declaration.js +177 -0
  155. package/lib/doc-gate/detect.js +289 -0
  156. package/lib/doc-gate/gate.js +375 -0
  157. package/lib/doc-gate/okf-config.js +128 -0
  158. package/lib/doc-gate/okf.js +429 -0
  159. package/lib/docs-command.js +1161 -6
  160. package/lib/forge-issues.js +382 -11
  161. package/lib/forge-lock.js +262 -0
  162. package/lib/gate-events.js +193 -0
  163. package/lib/global-flags.js +74 -0
  164. package/lib/greptile-match.js +7 -63
  165. package/lib/harness-capability-matrix.js +380 -0
  166. package/lib/hook-global-installer.js +347 -0
  167. package/lib/hook-renderer.js +451 -0
  168. package/lib/inbox.js +391 -0
  169. package/lib/insights.js +397 -0
  170. package/lib/issue-adapter.js +156 -0
  171. package/lib/issue-backend.js +145 -0
  172. package/lib/issue-render.js +220 -0
  173. package/lib/kernel/backing-issue.js +305 -0
  174. package/lib/kernel/broker.js +1218 -0
  175. package/lib/kernel/cli-broker-factory.js +130 -0
  176. package/lib/kernel/conflict-signal.js +82 -0
  177. package/lib/kernel/evaluators.js +195 -0
  178. package/lib/kernel/fs-class.js +495 -0
  179. package/lib/kernel/issue-command-contract.js +559 -0
  180. package/lib/kernel/issue-id-resolver.js +186 -0
  181. package/lib/kernel/lease-enforcer.js +158 -0
  182. package/lib/kernel/migrations.js +333 -0
  183. package/lib/kernel/planning-buckets-schema.js +109 -0
  184. package/lib/kernel/projection-jsonl-writer.js +450 -0
  185. package/lib/kernel/readiness-model.js +329 -0
  186. package/lib/kernel/schema.js +356 -0
  187. package/lib/kernel/sqlite-driver.js +2504 -0
  188. package/lib/kernel/taxonomy-validator.js +394 -0
  189. package/lib/lefthook-check.js +3 -2
  190. package/lib/lefthook-wiring.js +413 -0
  191. package/lib/mcp-config-renderer.js +288 -0
  192. package/lib/memory/graphiti-mcp.js +106 -0
  193. package/lib/memory/router.js +387 -0
  194. package/lib/memory/typed-api.js +102 -0
  195. package/lib/memory-digest.js +195 -0
  196. package/lib/merge-rules.js +395 -0
  197. package/lib/migrate-dry-run.js +466 -0
  198. package/lib/orientation.js +863 -0
  199. package/lib/package-manager-remediation.js +103 -0
  200. package/lib/package-root.js +381 -0
  201. package/lib/patch-intent.js +890 -0
  202. package/lib/plugin-catalog.js +3 -4
  203. package/lib/plugin-manager.js +0 -5
  204. package/lib/pr-bundle.js +186 -0
  205. package/lib/pr-monitor/differ.js +195 -0
  206. package/lib/pr-monitor/events.js +0 -0
  207. package/lib/pr-monitor/gather.js +124 -0
  208. package/lib/pr-monitor/journal.js +299 -0
  209. package/lib/pr-monitor/monitor.js +146 -0
  210. package/lib/pr-monitor/render-sticky.js +157 -0
  211. package/lib/pr-monitor/watch-lifecycle.js +95 -0
  212. package/lib/pr-monitor/watch.js +247 -0
  213. package/lib/pr-pull.js +1273 -0
  214. package/lib/pr-shepherd.js +494 -0
  215. package/lib/pr-state-validator.js +59 -0
  216. package/lib/preflight/gates.js +237 -0
  217. package/lib/preflight/runner.js +116 -0
  218. package/lib/project-discovery.js +0 -53
  219. package/lib/project-memory.js +99 -497
  220. package/lib/protected-path-manifest.js +281 -0
  221. package/lib/protected-state-surfaces.js +387 -0
  222. package/lib/release-readiness.js +2089 -0
  223. package/lib/reset.js +59 -45
  224. package/lib/review-adapter.js +68 -0
  225. package/lib/rules-sync.js +260 -0
  226. package/lib/runtime-health.js +241 -20
  227. package/lib/safety-config-renderer.js +268 -0
  228. package/lib/setup-action-log.js +1 -7
  229. package/lib/setup.js +27 -65
  230. package/lib/shell-utils.js +76 -6
  231. package/lib/skills-sync.js +330 -0
  232. package/lib/smart-status/scoring.js +17 -3
  233. package/lib/status/beads-snapshot.js +45 -2
  234. package/lib/status/presenter.js +169 -18
  235. package/lib/status/snapshot.js +186 -0
  236. package/lib/sync-backend.js +202 -0
  237. package/lib/untrusted-content.js +52 -0
  238. package/lib/upgrade-safety.js +199 -0
  239. package/lib/workflow/enforce-stage.js +296 -47
  240. package/lib/workflow/stage-transition.js +115 -0
  241. package/lib/workflow/stages.js +30 -6
  242. package/lib/workflow/state-manager.js +11 -22
  243. package/lib/workflow/state.js +23 -1
  244. package/lib/workflow-profiles.js +17 -5
  245. package/package.json +37 -35
  246. package/rules/documentation.md +19 -0
  247. package/rules/kernel-tracking.md +26 -0
  248. package/rules/security.md +22 -0
  249. package/rules/tdd.md +20 -0
  250. package/rules/workflow.md +27 -0
  251. package/scripts/auto-backing-issue.js +47 -0
  252. package/scripts/beads-context.sh +81 -57
  253. package/scripts/beads-upgrade-smoke.sh +24 -3
  254. package/scripts/bootstrap-windows-tools.sh +78 -0
  255. package/scripts/branch-protection.js +2 -3
  256. package/scripts/check-agents.js +34 -137
  257. package/scripts/commitlint.js +3 -1
  258. package/scripts/conflict-detect.sh +3 -0
  259. package/scripts/dep-guard.sh +22 -3
  260. package/scripts/file-index.sh +3 -0
  261. package/scripts/forge-team/lib/claim.sh +34 -18
  262. package/scripts/forge-team/lib/dashboard.sh +61 -86
  263. package/scripts/forge-team/lib/epic.sh +99 -263
  264. package/scripts/forge-team/lib/hooks.sh +26 -28
  265. package/scripts/forge-team/lib/identity.sh +4 -4
  266. package/scripts/forge-team/lib/sync-github.sh +49 -84
  267. package/scripts/forge-team/lib/verify.sh +93 -83
  268. package/scripts/forge-team/lib/workload.sh +41 -65
  269. package/scripts/forge-team/tests/claim.test.sh +25 -19
  270. package/scripts/forge-team/tests/dashboard.test.sh +31 -46
  271. package/scripts/forge-team/tests/epic.test.sh +52 -71
  272. package/scripts/forge-team/tests/hooks.test.sh +38 -50
  273. package/scripts/forge-team/tests/identity.test.sh +3 -3
  274. package/scripts/forge-team/tests/integration.test.sh +44 -66
  275. package/scripts/forge-team/tests/sync-github.test.sh +50 -83
  276. package/scripts/forge-team/tests/verify.test.sh +37 -46
  277. package/scripts/forge-team/tests/workflow-integration.test.sh +4 -4
  278. package/scripts/forge-team/tests/workload.test.sh +32 -66
  279. package/scripts/gen-command-manifest.js +153 -0
  280. package/scripts/gen-embedded-assets.mjs +129 -0
  281. package/scripts/install.ps1 +139 -0
  282. package/scripts/install.sh +268 -0
  283. package/scripts/lib/release-asset.mjs +84 -0
  284. package/scripts/parity-check.mjs +145 -0
  285. package/scripts/parity-check.test.mjs +58 -0
  286. package/scripts/pin-agentic-workflow-images.js +112 -0
  287. package/scripts/pr-coordinator.sh +3 -0
  288. package/scripts/preflight-sonar.eslint.config.mjs +44 -0
  289. package/scripts/preflight.sh +21 -94
  290. package/scripts/protected-state-check.js +104 -0
  291. package/scripts/smart-status.sh +60 -57
  292. package/scripts/spikes/config-race-bench.js +111 -0
  293. package/scripts/spikes/harness-capability-matrix.js +13 -0
  294. package/scripts/spikes/patch-anchor-stability-bench.js +125 -0
  295. package/scripts/spikes/protected-path-manifest.js +20 -0
  296. package/scripts/spikes/skill-auto-invoke-parity.js +292 -0
  297. package/scripts/sync-agent-skills.js +62 -0
  298. package/scripts/sync-utils.sh +3 -0
  299. package/scripts/test-ci-shard.js +13 -6
  300. package/scripts/test.js +95 -12
  301. package/skills/claim-safety/SKILL.md +102 -0
  302. package/skills/claim-safety/evals/evals.json +46 -0
  303. package/{.github/prompts/dev.prompt.md → skills/dev/SKILL.md} +44 -50
  304. package/skills/dev/evals/evals.json +50 -0
  305. package/skills/hermes-forge/SKILL.md +185 -0
  306. package/skills/hermes-forge/evals/evals.json +46 -0
  307. package/skills/issue-basics/SKILL.md +111 -0
  308. package/skills/issue-basics/evals/evals.json +46 -0
  309. package/skills/kernel/SKILL.md +166 -0
  310. package/skills/kernel/evals/evals.json +50 -0
  311. package/skills/memory/SKILL.md +102 -0
  312. package/skills/parallel-deep-research/SKILL.md +14 -11
  313. package/skills/parallel-deep-research/evals/evals.json +11 -27
  314. package/{.github/prompts/plan.prompt.md → skills/plan/SKILL.md} +132 -157
  315. package/skills/plan/evals/evals.json +42 -0
  316. package/skills/research/SKILL.md +195 -0
  317. package/skills/research/evals/evals.json +42 -0
  318. package/{.github/prompts/review.prompt.md → skills/review/SKILL.md} +98 -62
  319. package/skills/review/evals/evals.json +42 -0
  320. package/skills/rollback/SKILL.md +110 -0
  321. package/skills/rollback/evals/evals.json +46 -0
  322. package/skills/rollback/references/methods.md +204 -0
  323. package/{.cursor/commands/rollback.md → skills/rollback/references/workflow-integration.md} +10 -284
  324. package/skills/shepherd/SKILL.md +66 -0
  325. package/skills/shepherd/evals/evals.json +42 -0
  326. package/{.github/prompts/ship.prompt.md → skills/ship/SKILL.md} +81 -45
  327. package/skills/ship/evals/evals.json +42 -0
  328. package/skills/smith/SKILL.md +142 -0
  329. package/skills/smith/evals/evals.json +46 -0
  330. package/skills/smith/references/autonomy-and-gates.md +94 -0
  331. package/{.github/prompts/sonarcloud.prompt.md → skills/sonarcloud/SKILL.md} +14 -3
  332. package/skills/sonarcloud/evals/evals.json +46 -0
  333. package/skills/sonarcloud-analysis/SKILL.md +18 -13
  334. package/skills/sonarcloud-analysis/evals/evals.json +11 -15
  335. package/{.github/prompts/status.prompt.md → skills/status/SKILL.md} +20 -10
  336. package/skills/status/evals/evals.json +50 -0
  337. package/skills/triage-ready/SKILL.md +121 -0
  338. package/skills/triage-ready/evals/evals.json +42 -0
  339. package/{.github/prompts/validate.prompt.md → skills/validate/SKILL.md} +52 -29
  340. package/skills/validate/evals/evals.json +42 -0
  341. package/skills/verify/SKILL.md +299 -0
  342. package/skills/verify/evals/evals.json +50 -0
  343. package/.claude/commands/dev.md +0 -345
  344. package/.claude/commands/plan.md +0 -566
  345. package/.claude/commands/premerge.md +0 -186
  346. package/.claude/commands/research.md +0 -42
  347. package/.claude/commands/review.md +0 -451
  348. package/.claude/commands/rollback.md +0 -721
  349. package/.claude/commands/ship.md +0 -213
  350. package/.claude/commands/sonarcloud.md +0 -152
  351. package/.claude/commands/status.md +0 -90
  352. package/.claude/commands/validate.md +0 -288
  353. package/.claude/commands/verify.md +0 -269
  354. package/.claude/rules/workflow.md +0 -121
  355. package/.cline/workflows/dev.md +0 -342
  356. package/.cline/workflows/plan.md +0 -563
  357. package/.cline/workflows/premerge.md +0 -183
  358. package/.cline/workflows/research.md +0 -39
  359. package/.cline/workflows/review.md +0 -448
  360. package/.cline/workflows/rollback.md +0 -718
  361. package/.cline/workflows/ship.md +0 -210
  362. package/.cline/workflows/sonarcloud.md +0 -146
  363. package/.cline/workflows/status.md +0 -87
  364. package/.cline/workflows/validate.md +0 -285
  365. package/.cline/workflows/verify.md +0 -266
  366. package/.codex/config.toml +0 -11
  367. package/.codex/skills/dev/SKILL.md +0 -345
  368. package/.codex/skills/plan/SKILL.md +0 -566
  369. package/.codex/skills/premerge/SKILL.md +0 -186
  370. package/.codex/skills/research/SKILL.md +0 -42
  371. package/.codex/skills/review/SKILL.md +0 -451
  372. package/.codex/skills/rollback/SKILL.md +0 -721
  373. package/.codex/skills/ship/SKILL.md +0 -213
  374. package/.codex/skills/sonarcloud/SKILL.md +0 -149
  375. package/.codex/skills/status/SKILL.md +0 -90
  376. package/.codex/skills/validate/SKILL.md +0 -288
  377. package/.codex/skills/verify/SKILL.md +0 -269
  378. package/.cursor/commands/dev.md +0 -342
  379. package/.cursor/commands/plan.md +0 -563
  380. package/.cursor/commands/premerge.md +0 -183
  381. package/.cursor/commands/research.md +0 -39
  382. package/.cursor/commands/review.md +0 -448
  383. package/.cursor/commands/ship.md +0 -210
  384. package/.cursor/commands/sonarcloud.md +0 -146
  385. package/.cursor/commands/status.md +0 -87
  386. package/.cursor/commands/validate.md +0 -285
  387. package/.cursor/commands/verify.md +0 -266
  388. package/.cursorrules +0 -149
  389. package/.github/prompts/premerge.prompt.md +0 -188
  390. package/.github/prompts/research.prompt.md +0 -44
  391. package/.github/prompts/rollback.prompt.md +0 -723
  392. package/.github/prompts/verify.prompt.md +0 -271
  393. package/.github/workflows/beads-to-github.yml +0 -89
  394. package/.github/workflows/github-to-beads.yml +0 -100
  395. package/.kilocode/workflows/dev.md +0 -346
  396. package/.kilocode/workflows/plan.md +0 -567
  397. package/.kilocode/workflows/premerge.md +0 -187
  398. package/.kilocode/workflows/research.md +0 -43
  399. package/.kilocode/workflows/review.md +0 -452
  400. package/.kilocode/workflows/rollback.md +0 -722
  401. package/.kilocode/workflows/ship.md +0 -214
  402. package/.kilocode/workflows/sonarcloud.md +0 -150
  403. package/.kilocode/workflows/status.md +0 -91
  404. package/.kilocode/workflows/validate.md +0 -289
  405. package/.kilocode/workflows/verify.md +0 -270
  406. package/.opencode/commands/dev.md +0 -345
  407. package/.opencode/commands/plan.md +0 -566
  408. package/.opencode/commands/premerge.md +0 -186
  409. package/.opencode/commands/research.md +0 -42
  410. package/.opencode/commands/review.md +0 -451
  411. package/.opencode/commands/rollback.md +0 -721
  412. package/.opencode/commands/ship.md +0 -213
  413. package/.opencode/commands/sonarcloud.md +0 -149
  414. package/.opencode/commands/status.md +0 -90
  415. package/.opencode/commands/validate.md +0 -288
  416. package/.opencode/commands/verify.md +0 -269
  417. package/.roo/commands/dev.md +0 -346
  418. package/.roo/commands/plan.md +0 -567
  419. package/.roo/commands/premerge.md +0 -187
  420. package/.roo/commands/research.md +0 -43
  421. package/.roo/commands/review.md +0 -452
  422. package/.roo/commands/rollback.md +0 -722
  423. package/.roo/commands/ship.md +0 -214
  424. package/.roo/commands/sonarcloud.md +0 -150
  425. package/.roo/commands/status.md +0 -91
  426. package/.roo/commands/validate.md +0 -289
  427. package/.roo/commands/verify.md +0 -270
  428. package/docs/BEADS_GITHUB_SYNC.md +0 -281
  429. package/docs/GREPTILE_SETUP.md +0 -400
  430. package/docs/MANUAL_REVIEW_GUIDE.md +0 -106
  431. package/docs/SETUP.md +0 -663
  432. package/docs/VALIDATION.md +0 -363
  433. package/lib/agents/cline.plugin.json +0 -29
  434. package/lib/agents/copilot.plugin.json +0 -24
  435. package/lib/agents/kilocode.plugin.json +0 -22
  436. package/lib/agents/opencode.plugin.json +0 -23
  437. package/lib/agents/roo.plugin.json +0 -30
  438. package/lib/beads-bootstrap.js +0 -225
  439. package/lib/beads-health-check.js +0 -188
  440. package/lib/commands/commands-reset.js +0 -147
  441. package/opencode.json +0 -67
  442. package/scripts/beads-context.test.js +0 -584
  443. package/scripts/github-beads-sync/comment.mjs +0 -64
  444. package/scripts/github-beads-sync/config.mjs +0 -148
  445. package/scripts/github-beads-sync/github-api.mjs +0 -131
  446. package/scripts/github-beads-sync/index.mjs +0 -356
  447. package/scripts/github-beads-sync/label-mapper.mjs +0 -54
  448. package/scripts/github-beads-sync/mapping.mjs +0 -132
  449. package/scripts/github-beads-sync/reverse-sync-cli.mjs +0 -31
  450. package/scripts/github-beads-sync/reverse-sync.mjs +0 -162
  451. package/scripts/github-beads-sync/run-bd.mjs +0 -161
  452. package/scripts/github-beads-sync/sanitize.mjs +0 -121
  453. package/scripts/github-beads-sync.config.json +0 -26
  454. package/scripts/sync-commands.js +0 -600
@@ -1,17 +1,17 @@
1
1
  #!/usr/bin/env bash
2
- # scripts/forge-team/lib/sync-github.sh — Bidirectional sync between Beads and GitHub
2
+ # scripts/forge-team/lib/sync-github.sh — Bidirectional sync between Forge issues and GitHub
3
3
  #
4
4
  # Functions:
5
- # sync_issue_create <beads-id> — Create GitHub issue from Beads
6
- # sync_issue_claim <beads-id> — Assign current dev on GitHub
7
- # sync_issue_status <beads-id> <status> — Update status label on GitHub
8
- # sync_issue_close <beads-id> — Close GitHub issue
9
- # sync_issue_deps <beads-id-a> <beads-id-b> — Add "Blocked by" comment
10
- # _get_github_issue_number <beads-id> — Extract canonical GitHub issue from bd show
5
+ # sync_issue_create <issue-id> — Create GitHub issue from a Forge issue
6
+ # sync_issue_claim <issue-id> — Assign current dev on GitHub
7
+ # sync_issue_status <issue-id> <status> — Update status label on GitHub
8
+ # sync_issue_close <issue-id> — Close GitHub issue
9
+ # sync_issue_deps <issue-id-a> <issue-id-b> — Add "Blocked by" comment
10
+ # _get_github_issue_number <issue-id> — Extract canonical GitHub issue from issue show
11
11
  #
12
12
  # Env overrides (for testing):
13
- # GH_CMD — Path to gh binary (default: gh)
14
- # BD_CMD — Path to bd binary (default: bd)
13
+ # GH_CMD — Path to gh binary (default: gh)
14
+ # FORGE_CMD — Path to forge binary (default: forge)
15
15
  #
16
16
  # All GitHub-sourced strings are sanitized via sanitize_for_agent() before
17
17
  # use in shell commands (OWASP A03).
@@ -87,45 +87,6 @@ _extract_issue_number_from_url() {
87
87
  printf '%s' "$suffix"
88
88
  }
89
89
 
90
- _extract_issue_title_from_show() {
91
- local input="${1:-}"
92
-
93
- printf '%s' "$input" | node -e '
94
- const fs = require("node:fs");
95
- const input = fs.readFileSync(0, "utf8");
96
- const lines = input.split(/\r?\n/);
97
- const titledLine = lines.find((line) => /^Title:\s*/.test(line));
98
-
99
- if (titledLine) {
100
- process.stdout.write(titledLine.replace(/^Title:\s*/, "").trim());
101
- process.exit(0);
102
- }
103
-
104
- const summaryLine = lines.find((line) => line.trim().length > 0);
105
- if (!summaryLine) {
106
- process.exit(0);
107
- }
108
-
109
- const separators = [" - ", " \u00B7 ", " \u2022 "];
110
- for (const separator of separators) {
111
- const separatorIndex = summaryLine.indexOf(separator);
112
- if (separatorIndex === -1) {
113
- continue;
114
- }
115
-
116
- const candidate = summaryLine
117
- .slice(separatorIndex + separator.length)
118
- .replace(/\s+\[[^\]]*\]\s*$/u, "")
119
- .trim();
120
-
121
- if (candidate.length > 0) {
122
- process.stdout.write(candidate);
123
- break;
124
- }
125
- }
126
- '
127
- }
128
-
129
90
  _sync_mapping_file() {
130
91
  local root="${TEAM_MAP_ROOT:-.}"
131
92
  printf '%s' "${SYNC_MAPPING_FILE:-$root/.github/beads-mapping.json}"
@@ -196,41 +157,30 @@ NODE
196
157
  }
197
158
 
198
159
  # ── _get_github_issue_number ─────────────────────────────────────────────
199
- # Extracts the canonical GitHub issue number from `bd show <id> --json`.
160
+ # Extracts the canonical GitHub issue number from the issue's github_issue:<n>
161
+ # label (via `issue show <id> --json`), falling back to the local mapping file.
200
162
  # Returns the number or empty string (exit 1 if not found).
201
163
  _get_github_issue_number() {
202
164
  local beads_id="${1:-}"
203
165
  if [[ -z "$beads_id" ]]; then
204
- _sync_error "beads-id required"
166
+ _sync_error "issue id required"
205
167
  return 1
206
168
  fi
207
169
 
208
- local bd_cmd="${BD_CMD:-bd}"
170
+ local forge_cmd="${FORGE_CMD:-forge}"
209
171
  local show_output
210
- show_output="$("$bd_cmd" show "$beads_id" --json 2>/dev/null)" || {
211
- _sync_error "Failed to get beads info for $beads_id"
172
+ show_output="$("$forge_cmd" issue show "$beads_id" --json 2>/dev/null)" || {
173
+ _sync_error "Failed to get issue info for $beads_id"
212
174
  return 1
213
175
  }
214
176
 
215
177
  local issue_num
216
- issue_num="$(printf '%s' "$show_output" | node -e '
217
- const fs = require("node:fs");
218
- const input = fs.readFileSync(0, "utf8");
219
- try {
220
- const data = JSON.parse(input);
221
- const issue = Array.isArray(data) ? data[0] : data;
222
- const value = issue?.github?.number
223
- ?? issue?.githubNumber
224
- ?? issue?.issueNumber
225
- ?? issue?.github_issue
226
- ?? null;
227
- if (value != null && value !== "") {
228
- process.stdout.write(String(value));
229
- }
230
- } catch (_err) {
231
- process.exitCode = 0;
232
- }
233
- ')"
178
+ issue_num="$(printf '%s' "$show_output" | jq -r '
179
+ (.data.issue // .data // {})
180
+ | (.labels // [])
181
+ | map(select(startswith("github_issue:")))
182
+ | (.[0] // "")
183
+ | ltrimstr("github_issue:")' 2>/dev/null)"
234
184
 
235
185
  if [[ -z "$issue_num" ]]; then
236
186
  issue_num="$(_get_issue_number_from_mapping "$beads_id" 2>/dev/null || true)"
@@ -246,16 +196,16 @@ _get_github_issue_number() {
246
196
  }
247
197
 
248
198
  # ── _get_issue_title ─────────────────────────────────────────────────────
249
- # Extracts issue title from `bd show <id>` output.
250
- # Looks for "Title: <text>" line or falls back to the summary line.
199
+ # Extracts the issue title from `issue show <id> --json`. Falls back to the
200
+ # issue id when the title is empty.
251
201
  _get_issue_title() {
252
202
  local beads_id="${1:-}"
253
- local bd_cmd="${BD_CMD:-bd}"
203
+ local forge_cmd="${FORGE_CMD:-forge}"
254
204
  local show_output
255
- show_output="$("$bd_cmd" show "$beads_id" 2>/dev/null)" || return 1
205
+ show_output="$("$forge_cmd" issue show "$beads_id" --json 2>/dev/null)" || return 1
256
206
 
257
207
  local title
258
- title="$(_extract_issue_title_from_show "$show_output")"
208
+ title="$(printf '%s' "$show_output" | jq -r '(.data.issue // .data // {}) | (.title // "")' 2>/dev/null)"
259
209
 
260
210
  if [[ -z "$title" ]]; then
261
211
  title="$beads_id"
@@ -266,10 +216,10 @@ _get_issue_title() {
266
216
 
267
217
  # ── Public API ───────────────────────────────────────────────────────────
268
218
 
269
- # sync_issue_create <beads-id>
270
- # Creates GitHub issue from Beads issue data.
271
- # Persists both the canonical mapping file and the legacy github_issue state
272
- # until downstream hook and verify flows stop reading bd show output.
219
+ # sync_issue_create <issue-id>
220
+ # Creates a GitHub issue from the Forge issue data.
221
+ # Persists both the canonical mapping file and the github_issue:<n> label so
222
+ # that downstream hook and verify flows can resolve the GitHub association.
273
223
  # Returns 0 on success, 1 on failure.
274
224
  sync_issue_create() {
275
225
  local beads_id="${1:-}"
@@ -279,9 +229,24 @@ sync_issue_create() {
279
229
  fi
280
230
 
281
231
  local gh_cmd="${GH_CMD:-gh}"
282
- local bd_cmd="${BD_CMD:-bd}"
232
+ local forge_cmd="${FORGE_CMD:-forge}"
233
+
234
+ # Retry-safety / idempotency: the mapping file is persisted *before* the
235
+ # github_issue:<n> label is stored, so a previous run that created the GitHub
236
+ # issue and mapping but failed on the label leaves a recoverable mapping entry.
237
+ # On retry, repair the missing label from the mapping instead of calling
238
+ # `gh issue create` again and creating a duplicate GitHub issue.
239
+ local existing_num
240
+ existing_num="$(_get_issue_number_from_mapping "$beads_id" 2>/dev/null || true)"
241
+ if [[ -n "$existing_num" ]]; then
242
+ if ! "$forge_cmd" issue update "$beads_id" --label "github_issue:$existing_num" >/dev/null 2>&1; then
243
+ _sync_error "Failed to store github_issue:$existing_num for $beads_id"
244
+ return 1
245
+ fi
246
+ return 0
247
+ fi
283
248
 
284
- # Get title from bd show
249
+ # Get title from the issue backend
285
250
  local raw_title
286
251
  raw_title="$(_get_issue_title "$beads_id")" || {
287
252
  _sync_error "Failed to get title for $beads_id"
@@ -313,8 +278,8 @@ sync_issue_create() {
313
278
  return 1
314
279
  fi
315
280
 
316
- if ! "$bd_cmd" set-state "$beads_id" "github_issue=$issue_num" >/dev/null 2>&1; then
317
- _sync_error "Failed to store github_issue=$issue_num for $beads_id"
281
+ if ! "$forge_cmd" issue update "$beads_id" --label "github_issue:$issue_num" >/dev/null 2>&1; then
282
+ _sync_error "Failed to store github_issue:$issue_num for $beads_id"
318
283
  return 1
319
284
  fi
320
285
 
@@ -7,15 +7,15 @@
7
7
  # Checks:
8
8
  # 1. gh auth status (GitHub CLI authenticated)
9
9
  # 2. Identity mapped in team-map.jsonl
10
- # 3. Orphan Beads issues (Beads without GitHub counterpart)
11
- # 4. Orphan GitHub issues (GitHub without Beads counterpart)
12
- # 5. Assignee consistency between Beads and GitHub
10
+ # 3. Orphan tracked issues (no GitHub counterpart)
11
+ # 4. Orphan GitHub issues (no tracked counterpart)
12
+ # 5. Assignee consistency between tracked issues and GitHub
13
13
  #
14
14
  # Env overrides (for testing):
15
15
  # GH_CMD — Path to gh binary (default: gh)
16
- # BD_CMD — Path to bd binary (default: bd)
17
- # TEAM_MAP_ROOT — Root dir for .beads/ and .github/ (default: .)
18
- # VERIFY_MAPPING_FILE — Path to beads-mapping.json (default: $TEAM_MAP_ROOT/.github/beads-mapping.json)
16
+ # FORGE_CMD — Path to forge binary (default: forge)
17
+ # TEAM_MAP_ROOT — Root dir for .forge/ and .github/ (default: .)
18
+ # VERIFY_MAPPING_FILE — Path to the GitHub issue mapping (default: $TEAM_MAP_ROOT/.github/beads-mapping.json)
19
19
  #
20
20
  # This file does NOT set errexit/pipefail — callers manage their own shell options.
21
21
 
@@ -66,16 +66,19 @@ _mapping_file() {
66
66
  printf '%s' "${VERIFY_MAPPING_FILE:-$root/.github/beads-mapping.json}"
67
67
  }
68
68
 
69
- # ── _extract_beads_ids ───────────────────────────────────────────────────
70
- # Parse bd list output to extract beads IDs.
71
- # Input format: "◐ forge-aaa · Feature A" or "○ forge-bbb · Feature B"
72
- # Output: one ID per line
73
- _extract_beads_ids() {
74
- local input="${1:-}"
75
- if [[ -z "$input" ]]; then
76
- return 0
77
- fi
78
- printf '%s\n' "$input" | grep -oP '(forge-[a-zA-Z0-9]+(\.[a-zA-Z0-9]+)*)' || true
69
+ # ── _verify_active_issues_json ─────────────────────────────────────────────
70
+ # Merged JSON array of open + in_progress issues. The Kernel list filter does
71
+ # not accept comma-joined statuses, so query each status separately and
72
+ # concatenate the issue arrays.
73
+ _verify_active_issues_json() {
74
+ local forge_cmd="${FORGE_CMD:-forge}"
75
+ local open_json inprog_json
76
+ open_json="$("$forge_cmd" issue list --status=open --json 2>/dev/null)" || open_json=""
77
+ inprog_json="$("$forge_cmd" issue list --status=in_progress --json 2>/dev/null)" || inprog_json=""
78
+ jq -n \
79
+ --argjson a "${open_json:-null}" \
80
+ --argjson b "${inprog_json:-null}" \
81
+ '((($a.data.issues) // []) + (($b.data.issues) // []))' 2>/dev/null || echo "[]"
79
82
  }
80
83
 
81
84
  # ── Public API ───────────────────────────────────────────────────────────
@@ -84,7 +87,6 @@ _extract_beads_ids() {
84
87
  # Exit 0 if all clean, exit 1 if any issues found.
85
88
  cmd_verify() {
86
89
  local gh_cmd="${GH_CMD:-gh}"
87
- local bd_cmd="${BD_CMD:-bd}"
88
90
  local issues_found=0
89
91
 
90
92
  echo "forge team verify"
@@ -130,47 +132,75 @@ cmd_verify() {
130
132
  issues_found=$((issues_found + 1))
131
133
  fi
132
134
 
133
- # ── Check 3: Orphan Beads issues (Beads without GitHub) ─────────────
134
- local bd_output
135
- bd_output="$("$bd_cmd" list --status=open,in_progress 2>/dev/null)" || bd_output=""
136
-
137
- local beads_ids
138
- beads_ids="$(_extract_beads_ids "$bd_output")"
139
-
140
- local beads_total=0
141
- local beads_synced=0
142
- local beads_orphans=()
143
-
144
- if [[ -n "$beads_ids" ]]; then
145
- while IFS= read -r bid; do
146
- [[ -z "$bid" ]] && continue
147
- beads_total=$((beads_total + 1))
148
-
149
- # Check if this beads issue has github_issue state
150
- local show_output
151
- show_output="$("$bd_cmd" show "$bid" 2>/dev/null)" || show_output=""
152
- if printf '%s' "$show_output" | grep -qP 'github_issue:\d+'; then
153
- beads_synced=$((beads_synced + 1))
154
- else
155
- beads_orphans+=("$bid")
156
- fi
157
- done <<< "$beads_ids"
158
- fi
135
+ # ── Association set: labels mapping (shared by Check 3 and Check 4)
136
+ # A GitHub↔Forge association can live in either the issue's `github_issue:<n>`
137
+ # label OR the local mapping file. Build a single set from BOTH so Check 3
138
+ # (Forge→GitHub) and Check 4 (GitHub→Forge) agree: a label-only or
139
+ # mapping-only association must never be misreported as an orphan.
140
+ local mapping_file
141
+ mapping_file="$(_mapping_file)"
142
+ local mapping_content="{}"
143
+ [[ -f "$mapping_file" ]] && mapping_content="$(cat "$mapping_file" 2>/dev/null || echo '{}')"
144
+
145
+ local issues_json
146
+ issues_json="$(_verify_active_issues_json)"
147
+
148
+ local -A assoc_gh_num=() # associated GitHub numbers (mapping keys ∪ label numbers)
149
+ local -A assoc_beads=() # associated Forge ids (mapping values ∪ labelled ids)
150
+
151
+ # From the mapping file: keys are GitHub numbers, values are Forge ids. The
152
+ # value is a string today; tolerate a legacy/forward object form ({beads_id}).
153
+ local _m_num _m_bid
154
+ while IFS=$'\t' read -r _m_num _m_bid; do
155
+ [[ -n "$_m_num" ]] && assoc_gh_num["$_m_num"]=1
156
+ [[ -n "$_m_bid" && "$_m_bid" != "null" ]] && assoc_beads["$_m_bid"]=1
157
+ done <<< "$(printf '%s' "$mapping_content" | jq -r '
158
+ to_entries[]
159
+ | [ (.key | tostring),
160
+ (if (.value | type) == "object" then (.value.beads_id // "") else (.value | tostring) end) ]
161
+ | @tsv' 2>/dev/null | tr -d '\r')"
162
+
163
+ # From active issues: a github_issue:<n> label links the issue id to number n.
164
+ local _l_id _l_num
165
+ while IFS=$'\t' read -r _l_id _l_num; do
166
+ [[ -z "$_l_id" ]] && continue
167
+ assoc_beads["$_l_id"]=1
168
+ [[ -n "$_l_num" ]] && assoc_gh_num["$_l_num"]=1
169
+ done <<< "$(printf '%s' "$issues_json" | jq -r '
170
+ .[]
171
+ | . as $x
172
+ | ((($x.labels // []) | map(select(startswith("github_issue:"))) | .[0] // "") | ltrimstr("github_issue:")) as $num
173
+ | select($num != "")
174
+ | [ $x.id, $num ] | @tsv' 2>/dev/null | tr -d '\r')"
175
+
176
+ # ── Check 3: Orphan tracked issues (no GitHub counterpart) ──────────
177
+ local issue_total issue_synced=0
178
+ issue_total="$(printf '%s' "$issues_json" | jq 'length' 2>/dev/null || echo 0)"
179
+
180
+ local issue_orphans=()
181
+ local _aid
182
+ while IFS= read -r _aid; do
183
+ [[ -z "$_aid" ]] && continue
184
+ if [[ -n "${assoc_beads[$_aid]:-}" ]]; then
185
+ issue_synced=$((issue_synced + 1))
186
+ else
187
+ issue_orphans+=("$_aid")
188
+ fi
189
+ done <<< "$(printf '%s' "$issues_json" | jq -r '.[].id' 2>/dev/null | tr -d '\r')"
159
190
 
160
- if [[ ${#beads_orphans[@]} -eq 0 ]]; then
161
- echo "✓ Beads→GitHub: ${beads_synced}/${beads_total} issues synced"
191
+ if [[ ${#issue_orphans[@]} -eq 0 ]]; then
192
+ echo "✓ Forge→GitHub: ${issue_synced}/${issue_total} issues synced"
162
193
  else
163
- echo "✗ Beads→GitHub: ${#beads_orphans[@]} orphan issues found"
164
- for orphan in "${beads_orphans[@]}"; do
194
+ echo "✗ Forge→GitHub: ${#issue_orphans[@]} orphan issues found"
195
+ for orphan in "${issue_orphans[@]}"; do
165
196
  _verify_prompt "${orphan} has no GitHub issue. Run: forge team sync-issue ${orphan}"
166
197
  done
167
- issues_found=$((issues_found + ${#beads_orphans[@]}))
198
+ issues_found=$((issues_found + ${#issue_orphans[@]}))
168
199
  fi
169
200
 
170
- # ── Check 4: Orphan GitHub issues (GitHub without Beads) ────────────
171
- local mapping_file
172
- mapping_file="$(_mapping_file)"
173
-
201
+ # ── Check 4: Orphan GitHub issues (no tracked counterpart) ──────────
202
+ # Uses the same association set: a GitHub number is mapped when it appears in
203
+ # the mapping file OR as a github_issue:<n> label on an active issue.
174
204
  local gh_issues_json
175
205
  gh_issues_json="$("$gh_cmd" issue list --state open --json number,title --limit 100 2>/dev/null)" || gh_issues_json="[]"
176
206
 
@@ -178,43 +208,23 @@ cmd_verify() {
178
208
  gh_issue_count="$(printf '%s' "$gh_issues_json" | jq 'length')"
179
209
 
180
210
  local gh_orphans=()
181
-
182
- if [[ "$gh_issue_count" -gt 0 ]] && [[ -f "$mapping_file" ]]; then
183
- local mapping_content
184
- mapping_content="$(cat "$mapping_file")"
185
-
186
- local i=0
187
- while [[ $i -lt $gh_issue_count ]]; do
188
- local issue_num
189
- issue_num="$(printf '%s' "$gh_issues_json" | jq -r ".[$i].number")"
190
-
191
- # Check if this GitHub issue number is a key in the mapping
192
- local mapped
193
- mapped="$(printf '%s' "$mapping_content" | jq -r --arg n "$issue_num" '.[$n] // empty')"
194
-
195
- if [[ -z "$mapped" ]]; then
196
- gh_orphans+=("$issue_num")
197
- fi
198
- i=$((i + 1))
199
- done
200
- elif [[ "$gh_issue_count" -gt 0 ]] && [[ ! -f "$mapping_file" ]]; then
201
- # No mapping file — all GitHub issues are orphans
202
- local i=0
203
- while [[ $i -lt $gh_issue_count ]]; do
204
- local issue_num
205
- issue_num="$(printf '%s' "$gh_issues_json" | jq -r ".[$i].number")"
211
+ local i=0
212
+ while [[ $i -lt $gh_issue_count ]]; do
213
+ local issue_num
214
+ issue_num="$(printf '%s' "$gh_issues_json" | jq -r ".[$i].number")"
215
+ if [[ -z "${assoc_gh_num[$issue_num]:-}" ]]; then
206
216
  gh_orphans+=("$issue_num")
207
- i=$((i + 1))
208
- done
209
- fi
217
+ fi
218
+ i=$((i + 1))
219
+ done
210
220
 
211
221
  if [[ ${#gh_orphans[@]} -eq 0 ]]; then
212
222
  local mapped_count=$((gh_issue_count))
213
- echo "✓ GitHub→Beads: ${mapped_count}/${mapped_count} issues mapped"
223
+ echo "✓ GitHub→Forge: ${mapped_count}/${mapped_count} issues mapped"
214
224
  else
215
- echo "✗ GitHub→Beads: ${#gh_orphans[@]} orphan issues found"
225
+ echo "✗ GitHub→Forge: ${#gh_orphans[@]} orphan issues found"
216
226
  for orphan_num in "${gh_orphans[@]}"; do
217
- _verify_prompt "GitHub #${orphan_num} has no Beads issue. Run: forge team import #${orphan_num}"
227
+ _verify_prompt "GitHub #${orphan_num} has no tracked issue. Run: forge team import #${orphan_num}"
218
228
  done
219
229
  issues_found=$((issues_found + ${#gh_orphans[@]}))
220
230
  fi
@@ -7,7 +7,7 @@
7
7
  # cmd_workload — Main entry point for `forge team workload`
8
8
  #
9
9
  # Env overrides (for testing):
10
- # BD_CMD — Path to bd binary
10
+ # FORGE_CMD — Path to forge binary
11
11
  # GH_CMD — Path to gh binary
12
12
  # WORKLOAD_NOW — Override "now" timestamp for testing (ISO 8601)
13
13
  #
@@ -88,70 +88,46 @@ _json_escape() {
88
88
  }
89
89
 
90
90
  # _collect_issues — Gather all open/in_progress issues with details
91
- # Outputs lines: id|title|status|owner|updated|depends_on
91
+ # Outputs tab-delimited (jq @tsv) lines: id<TAB>title<TAB>status<TAB>owner<TAB>updated<TAB>depends_on
92
+ # @tsv escapes any tab/newline/CR inside fields, so user-controlled titles that
93
+ # contain '|', tabs, or newlines never shift the downstream columns.
94
+ #
95
+ # The Kernel list filter does not accept comma-joined statuses, so query each
96
+ # active status separately and concatenate the JSON issue arrays. Every field
97
+ # the workload view needs (status, assignee, updated_at, dependencies) is
98
+ # present in `issue list --json`, so no per-issue lookup is required.
92
99
  _collect_issues() {
93
- local bd_cmd="${BD_CMD:-bd}"
94
- local list_output
95
- local combined_exit=0
96
-
97
- # beads 0.62 rejects comma-separated status filters, so fall back to
98
- # separate queries when the combined form fails.
99
- list_output="$("$bd_cmd" list --status=open,in_progress 2>/dev/null)"
100
- combined_exit=$?
101
- if [[ "$combined_exit" -ne 0 ]]; then
102
- list_output="$(
103
- {
104
- "$bd_cmd" list --status=open 2>/dev/null || true
105
- "$bd_cmd" list --status=in_progress 2>/dev/null || true
106
- } | awk 'NF && !seen[$0]++'
107
- )"
108
- fi
109
-
110
- # Filter empty lines
111
- if [[ -z "$list_output" ]] || [[ -z "$(printf '%s' "$list_output" | tr -d '[:space:]')" ]]; then
112
- return 0
113
- fi
114
-
115
- while IFS= read -r line; do
116
- # Skip empty lines
117
- [[ -z "$line" ]] && continue
118
-
119
- # Extract issue id: first field like "forge-xxx" after optional icon
120
- local issue_id
121
- issue_id="$(echo "$line" | grep -oE 'forge-[a-zA-Z0-9]+(\.[a-zA-Z0-9]+)*' | head -1)"
122
- [[ -z "$issue_id" ]] && continue
123
-
124
- # Get details via bd show
125
- local show_output
126
- show_output="$("$bd_cmd" show "$issue_id" 2>/dev/null)" || continue
127
-
128
- # Parse owner
129
- local owner
130
- owner="$(echo "$show_output" | grep -E '^Owner:' | sed 's/^Owner:[[:space:]]*//')"
131
- [[ -z "$owner" ]] && owner="unassigned"
132
-
133
- # Parse status from the header line [● Px · STATUS]
134
- local status
135
- status="$(echo "$show_output" | head -1 | grep -oE '(IN_PROGRESS|OPEN|BLOCKED|CLOSED)' | head -1)"
136
- [[ -z "$status" ]] && status="OPEN"
137
-
138
- # Parse title — text after "forge-xxx · " on first line
139
- local title
140
- title="$(echo "$show_output" | head -1 | sed -E 's/^[^·]*·[[:space:]]*//' | sed -E 's/[[:space:]]*\[.*//')"
141
-
142
- # Parse updated timestamp
143
- local updated
144
- updated="$(echo "$show_output" | grep -E '^Updated:' | sed 's/^Updated:[[:space:]]*//')"
145
- [[ -z "$updated" ]] && updated=""
146
-
147
- # Parse dependencies (DEPENDS ON section)
148
- local depends_on=""
149
- if echo "$show_output" | grep -q "DEPENDS ON"; then
150
- depends_on="$(echo "$show_output" | grep -E '^\s*→' | sed -E 's/^\s*→\s*//' | sed -E 's/:.*//' | tr '\n' ',' | sed 's/,$//')"
151
- fi
152
-
153
- echo "${issue_id}|${title}|${status}|${owner}|${updated}|${depends_on}"
154
- done <<< "$list_output"
100
+ local forge_cmd="${FORGE_CMD:-forge}"
101
+ local open_json inprog_json issues_json
102
+
103
+ open_json="$("$forge_cmd" issue list --status=open --json 2>/dev/null)" || open_json=""
104
+ inprog_json="$("$forge_cmd" issue list --status=in_progress --json 2>/dev/null)" || inprog_json=""
105
+
106
+ issues_json="$(jq -n \
107
+ --argjson a "${open_json:-null}" \
108
+ --argjson b "${inprog_json:-null}" \
109
+ '((($a.data.issues) // []) + (($b.data.issues) // []))' 2>/dev/null)" || issues_json="[]"
110
+
111
+ # A Kernel issue's `.dependencies` lists the issues it BLOCKS (outgoing), so
112
+ # depends_on (the issues that block THIS one) is a reverse scan: issues within
113
+ # the active set whose `.dependencies` include this issue.
114
+ printf '%s' "$issues_json" | jq -r '
115
+ . as $all
116
+ | $all[]
117
+ | . as $x
118
+ | [ $x.id,
119
+ ($x.title // ""),
120
+ (($x.status // "open") | ascii_upcase),
121
+ ($x.assignee // $x.claimed_by // "unassigned"),
122
+ (($x.updated_at // "") | sub("\\.[0-9]+Z$"; "Z")),
123
+ ( [ $all[]
124
+ | select( (.dependencies // [])
125
+ | map(if type == "object" then (.id // .to // "") else . end)
126
+ | any(. == $x.id) )
127
+ | .id ]
128
+ | join(",") )
129
+ ]
130
+ | @tsv' | tr -d '\r'
155
131
  }
156
132
 
157
133
  # ── Public API ───────────────────────────────────────────────────────────
@@ -213,7 +189,7 @@ cmd_workload() {
213
189
  local work_dir
214
190
  work_dir="$(mktemp -d)"
215
191
 
216
- while IFS='|' read -r issue_id title status owner updated depends_on; do
192
+ while IFS=$'\t' read -r issue_id title status owner updated depends_on; do
217
193
  [[ -z "$issue_id" ]] && continue
218
194
 
219
195
  # Apply developer filter
@@ -7,7 +7,7 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
7
7
  TEST_TMP="$(mktemp -d)"
8
8
  trap 'rm -rf "$TEST_TMP"' EXIT
9
9
 
10
- # Override TEAM_MAP_ROOT so tests don't touch real .beads/
10
+ # Override TEAM_MAP_ROOT so tests don't touch real .forge/
11
11
  export TEAM_MAP_ROOT="$TEST_TMP"
12
12
 
13
13
  # ── Create mock gh ───────────────────────────────────────────────────────
@@ -52,26 +52,32 @@ exit 1
52
52
  MOCK
53
53
  chmod +x "$mock_dir/gh-fail"
54
54
 
55
- # ── Create mock bd ───────────────────────────────────────────────────────
56
- cat > "$mock_dir/bd" << 'MOCK'
55
+ # ── Create mock forge ─────────────────────────────────────────────────────
56
+ # `issue show --json` carries the github_issue:<n> association as a label.
57
+ cat > "$mock_dir/forge" << 'MOCK'
57
58
  #!/usr/bin/env bash
58
- case "$1" in
59
- show)
60
- case "$2" in
61
- forge-assigned) echo "forge-assigned Assigned Issue [github_issue:42]" ;;
62
- forge-unassigned) echo "forge-unassigned Unassigned Issue [github_issue:43]" ;;
63
- forge-mine) echo "forge-mine My Issue [github_issue:44]" ;;
64
- forge-nogithub) echo "forge-nogithub No GitHub Issue" ;;
65
- *) echo "unknown issue" ;;
66
- esac ;;
67
- update) echo "Updated" ;;
68
- comments) echo "Comment added" ;;
59
+ sub="$1 $2"
60
+ id="$3"
61
+ case "$sub" in
62
+ "issue show")
63
+ case "$id" in
64
+ forge-assigned) labels='["github_issue:42"]' ;;
65
+ forge-unassigned) labels='["github_issue:43"]' ;;
66
+ forge-mine) labels='["github_issue:44"]' ;;
67
+ forge-nogithub) labels='[]' ;;
68
+ *) printf 'unexpected issue id: %s\n' "$id" >&2; exit 64 ;;
69
+ esac
70
+ printf '{"data":{"issue":{"id":"%s","labels":%s}}}\n' "$id" "$labels"
71
+ ;;
72
+ "issue claim") printf '{"data":{"id":"%s"}}\n' "$id" ;;
73
+ "issue comment") printf '{"data":{"id":"%s"}}\n' "$id" ;;
74
+ *) printf 'unexpected forge command: %s\n' "$*" >&2; exit 64 ;;
69
75
  esac
70
76
  MOCK
71
- chmod +x "$mock_dir/bd"
77
+ chmod +x "$mock_dir/forge"
72
78
 
73
79
  export GH_CMD="$mock_dir/gh"
74
- export BD_CMD="$mock_dir/bd"
80
+ export FORGE_CMD="$mock_dir/forge"
75
81
 
76
82
  # Source the library under test
77
83
  source "$SCRIPT_DIR/lib/claim.sh"
@@ -109,7 +115,7 @@ assert_exit() {
109
115
  # ── Test 1: Pre-claim on unassigned issue → returns 0 ───────────────────
110
116
  echo "── Test 1: Pre-claim on unassigned issue ──"
111
117
  unset _GITHUB_USER_CACHE
112
- mkdir -p "$TEST_TMP/.beads"
118
+ mkdir -p "$TEST_TMP/.forge"
113
119
  rc=0
114
120
  pre_claim_check "forge-unassigned" 2>/dev/null || rc=$?
115
121
  assert_exit "unassigned issue returns 0" 0 "$rc"
@@ -136,7 +142,7 @@ assert_exit "assigned to self returns 0" 0 "$rc"
136
142
  echo ""
137
143
  echo "── Test 4: --force overrides pre-claim check ──"
138
144
  unset _GITHUB_USER_CACHE
139
- mkdir -p "$TEST_TMP/.beads"
145
+ mkdir -p "$TEST_TMP/.forge"
140
146
  rc=0
141
147
  output="$(forge_team_claim "forge-assigned" "--force" 2>&1)" || rc=$?
142
148
  assert_exit "force claim succeeds" 0 "$rc"
@@ -146,7 +152,7 @@ assert_contains "confirmation output" "claimed" "$output"
146
152
  echo ""
147
153
  echo "── Test 5: Claim with lock succeeds ──"
148
154
  unset _GITHUB_USER_CACHE
149
- mkdir -p "$TEST_TMP/.beads"
155
+ mkdir -p "$TEST_TMP/.forge"
150
156
  # Use the unassigned issue so pre-claim passes
151
157
  rc=0
152
158
  output="$(forge_team_claim "forge-unassigned" 2>&1)" || rc=$?