forge-workflow 0.0.9 → 0.1.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (479) hide show
  1. package/.claude/rules/{greptile-review-process.md → review-process.md} +56 -41
  2. package/.claude/scripts/{greptile-resolve.sh → review-resolve.sh} +13 -3
  3. package/.cursor/rules/permissions-guidance.mdc +2 -2
  4. package/.forge/hooks/check-tdd.js +3 -0
  5. package/.forge/hooks/forge-native-hook.js +245 -0
  6. package/.forge/protected-paths.yaml +157 -0
  7. package/AGENTS.md +151 -61
  8. package/CHANGELOG.md +681 -0
  9. package/CLAUDE.md +9 -106
  10. package/QUICKSTART.md +171 -0
  11. package/README.md +271 -363
  12. package/bin/forge-cmd.js +120 -9
  13. package/bin/forge-preflight.js +26 -5
  14. package/bin/forge.js +466 -489
  15. package/docs/INDEX.md +93 -0
  16. package/docs/PROJECT_DESIGN.md +685 -0
  17. package/docs/architecture/index.md +66 -0
  18. package/docs/architecture/notes/README.md +35 -0
  19. package/docs/architecture/subsystems/README.md +46 -0
  20. package/docs/{TOOLCHAIN.md → forge/TOOLCHAIN.md} +56 -47
  21. package/docs/forge/VALIDATION.md +82 -0
  22. package/docs/{AGENT_INSTALL_PROMPT.md → guides/AGENT_INSTALL_PROMPT.md} +3 -3
  23. package/docs/guides/BEADS_GITHUB_SYNC.md +32 -0
  24. package/docs/{ENHANCED_ONBOARDING.md → guides/ENHANCED_ONBOARDING.md} +16 -12
  25. package/docs/guides/GREPTILE_SETUP.md +46 -0
  26. package/docs/guides/MANUAL_REVIEW_GUIDE.md +58 -0
  27. package/docs/guides/MIGRATION.md +56 -0
  28. package/docs/guides/SETUP.md +118 -0
  29. package/docs/guides/SUPPORT.md +185 -0
  30. package/docs/guides/WORKFLOW_TEMPLATES.md +74 -0
  31. package/docs/guides/memory-backends.md +183 -0
  32. package/docs/reference/ADAPTERS.md +128 -0
  33. package/docs/reference/AGENT_SKILL_PARITY.md +175 -0
  34. package/docs/reference/COMMANDS.md +205 -0
  35. package/docs/reference/DECISION_DRIFT_GUARDS.md +97 -0
  36. package/docs/{EXAMPLES.md → reference/EXAMPLES.md} +7 -5
  37. package/docs/reference/FORGE_KERNEL_STORAGE_MODEL.md +135 -0
  38. package/docs/reference/HERMES_INTEGRATION.md +118 -0
  39. package/docs/reference/INSIGHTS_RECAP.md +63 -0
  40. package/docs/reference/INSTALL.md +164 -0
  41. package/docs/reference/KERNEL_TAXONOMY_VALIDATION.md +161 -0
  42. package/docs/reference/PROTECTED_PATH_MANIFEST.md +25 -0
  43. package/docs/reference/RELEASE.md +68 -0
  44. package/docs/reference/RESEARCH_TEMPLATE.md +292 -0
  45. package/docs/{ROADMAP.md → reference/ROADMAP.md} +12 -9
  46. package/docs/reference/SKILLS.md +35 -0
  47. package/docs/reference/STATUS_BOARD.md +80 -0
  48. package/docs/reference/TEMPLATES.md +106 -0
  49. package/docs/reference/TOOLCHAIN.md +658 -0
  50. package/docs/reference/VALIDATION.md +82 -0
  51. package/docs/reference/agent-permissions.md +169 -0
  52. package/docs/reference/beads-to-kernel-migration-ux.md +61 -0
  53. package/docs/reference/control-plane-guarantees.md +125 -0
  54. package/docs/reference/dependency-chain.md +331 -0
  55. package/docs/reference/forge-kernel-issue-command-contract.md +161 -0
  56. package/docs/reference/forge-kernel-schema.md +72 -0
  57. package/docs/reference/kernel-conflict-evaluators.md +27 -0
  58. package/docs/reference/patch-md-format.md +77 -0
  59. package/docs/reference/protected-state-surfaces.md +59 -0
  60. package/docs/reference/shepherd.md +115 -0
  61. package/docs/reference/superpowers-analysis.md +320 -0
  62. package/docs/reference/superpowers-integration-options.md +404 -0
  63. package/docs/reference/test-environment.md +519 -0
  64. package/docs/reference/upgrade-safety.md +59 -0
  65. package/lefthook.yml +18 -0
  66. package/lib/adapter-cli.js +307 -0
  67. package/lib/adapters/beads-issue-adapter.js +127 -0
  68. package/lib/adapters/beads-kernel-compat.js +1042 -0
  69. package/lib/adapters/greptile-review-adapter.js +141 -0
  70. package/lib/adapters/kernel-issue-adapter.js +101 -0
  71. package/lib/adapters/pr-state-adapter.js +484 -0
  72. package/lib/adoption-profiles.js +126 -0
  73. package/lib/agents/README.md +2 -6
  74. package/lib/agents/claude.plugin.json +3 -8
  75. package/lib/agents/codex.plugin.json +9 -1
  76. package/lib/agents/cursor.plugin.json +2 -6
  77. package/lib/agents/hermes.plugin.json +22 -0
  78. package/lib/agents-config.js +39 -1236
  79. package/lib/audit-evidence.js +282 -0
  80. package/lib/beads-setup.js +225 -28
  81. package/lib/beads-sync-scaffold.js +36 -107
  82. package/lib/codex-skills.js +51 -1
  83. package/lib/commands/_issue.js +744 -70
  84. package/lib/commands/_manifest.js +91 -0
  85. package/lib/commands/_registry.js +85 -34
  86. package/lib/commands/_resolve-command-opts.js +261 -0
  87. package/lib/commands/_serve-security.js +270 -0
  88. package/lib/commands/adapter.js +12 -0
  89. package/lib/commands/add.js +118 -0
  90. package/lib/commands/audit.js +70 -0
  91. package/lib/commands/blocked.js +5 -0
  92. package/lib/commands/board.js +64 -0
  93. package/lib/commands/claim.js +21 -2
  94. package/lib/commands/claims.js +7 -0
  95. package/lib/commands/clean.js +485 -75
  96. package/lib/commands/close.js +2 -2
  97. package/lib/commands/comment.js +5 -0
  98. package/lib/commands/control.js +148 -0
  99. package/lib/commands/create.js +2 -2
  100. package/lib/commands/dev.js +185 -7
  101. package/lib/commands/doc-gate.js +336 -0
  102. package/lib/commands/doctor.js +156 -0
  103. package/lib/commands/explain.js +15 -0
  104. package/lib/commands/export.js +237 -0
  105. package/lib/commands/gate.js +192 -0
  106. package/lib/commands/hooks.js +242 -0
  107. package/lib/commands/inbox.js +118 -0
  108. package/lib/commands/init.js +598 -0
  109. package/lib/commands/insights.js +79 -0
  110. package/lib/commands/issue.js +12 -1
  111. package/lib/commands/issues.js +66 -0
  112. package/lib/commands/lint.js +5 -0
  113. package/lib/commands/list.js +2 -2
  114. package/lib/commands/merge.js +312 -0
  115. package/lib/commands/migrate.js +523 -0
  116. package/lib/commands/new.js +12 -0
  117. package/lib/commands/options.js +241 -0
  118. package/lib/commands/orient.js +13 -0
  119. package/lib/commands/orphans.js +5 -0
  120. package/lib/commands/patch.js +67 -0
  121. package/lib/commands/plan.js +436 -24
  122. package/lib/commands/preflight.js +211 -0
  123. package/lib/commands/prime.js +13 -0
  124. package/lib/commands/push.js +69 -2
  125. package/lib/commands/ready.js +2 -2
  126. package/lib/commands/recall.js +116 -0
  127. package/lib/commands/recap.js +61 -0
  128. package/lib/commands/recommend.js +22 -2
  129. package/lib/commands/release.js +91 -0
  130. package/lib/commands/remember.js +74 -0
  131. package/lib/commands/role.js +99 -0
  132. package/lib/commands/serve.js +581 -0
  133. package/lib/commands/setup.js +851 -979
  134. package/lib/commands/shepherd.js +436 -0
  135. package/lib/commands/ship.js +23 -1
  136. package/lib/commands/show.js +2 -2
  137. package/lib/commands/stage.js +192 -0
  138. package/lib/commands/stale.js +5 -0
  139. package/lib/commands/status.js +329 -11
  140. package/lib/commands/sync.js +34 -46
  141. package/lib/commands/team.js +15 -2
  142. package/lib/commands/test.js +58 -7
  143. package/lib/commands/update.js +2 -2
  144. package/lib/commands/upgrade.js +47 -0
  145. package/lib/commands/validate.js +56 -25
  146. package/lib/commands/worktree.js +308 -128
  147. package/lib/config-writer.js +202 -0
  148. package/lib/control-plane.js +236 -0
  149. package/lib/core/runtime-graph.js +946 -0
  150. package/lib/dep-guard/keyword-ripple.js +184 -0
  151. package/lib/deprecated-sync-cleanup.js +362 -0
  152. package/lib/detect-agent.js +2 -28
  153. package/lib/detect-worktree.js +42 -17
  154. package/lib/doc-gate/declaration.js +177 -0
  155. package/lib/doc-gate/detect.js +289 -0
  156. package/lib/doc-gate/gate.js +375 -0
  157. package/lib/doc-gate/okf-config.js +128 -0
  158. package/lib/doc-gate/okf.js +429 -0
  159. package/lib/docs-command.js +1161 -6
  160. package/lib/forge-issues.js +697 -0
  161. package/lib/forge-lock.js +262 -0
  162. package/lib/gate-events.js +193 -0
  163. package/lib/global-flags.js +74 -0
  164. package/lib/greptile-match.js +7 -63
  165. package/lib/harness-capability-matrix.js +380 -0
  166. package/lib/hook-global-installer.js +347 -0
  167. package/lib/hook-renderer.js +451 -0
  168. package/lib/inbox.js +391 -0
  169. package/lib/insights.js +397 -0
  170. package/lib/issue-adapter.js +156 -0
  171. package/lib/issue-backend.js +145 -0
  172. package/lib/issue-render.js +220 -0
  173. package/lib/issue-sync/authority.js +100 -0
  174. package/lib/issue-sync/github-pull.js +184 -0
  175. package/lib/issue-sync/import-primitives.js +98 -0
  176. package/lib/issue-sync/legacy-link-bridge.js +436 -0
  177. package/lib/issue-sync/link-store.js +292 -0
  178. package/lib/issue-sync/project-github.js +123 -0
  179. package/lib/issue-sync/reconcile.js +195 -0
  180. package/lib/issue-sync/schema.js +126 -0
  181. package/lib/kernel/backing-issue.js +305 -0
  182. package/lib/kernel/broker.js +1218 -0
  183. package/lib/kernel/cli-broker-factory.js +130 -0
  184. package/lib/kernel/conflict-signal.js +82 -0
  185. package/lib/kernel/evaluators.js +195 -0
  186. package/lib/kernel/fs-class.js +495 -0
  187. package/lib/kernel/issue-command-contract.js +559 -0
  188. package/lib/kernel/issue-id-resolver.js +186 -0
  189. package/lib/kernel/lease-enforcer.js +158 -0
  190. package/lib/kernel/migrations.js +333 -0
  191. package/lib/kernel/planning-buckets-schema.js +109 -0
  192. package/lib/kernel/projection-jsonl-writer.js +450 -0
  193. package/lib/kernel/readiness-model.js +329 -0
  194. package/lib/kernel/schema.js +356 -0
  195. package/lib/kernel/sqlite-driver.js +2504 -0
  196. package/lib/kernel/taxonomy-validator.js +394 -0
  197. package/lib/lefthook-check.js +8 -4
  198. package/lib/lefthook-wiring.js +413 -0
  199. package/lib/mcp-config-renderer.js +288 -0
  200. package/lib/memory/graphiti-mcp.js +106 -0
  201. package/lib/memory/router.js +387 -0
  202. package/lib/memory/typed-api.js +102 -0
  203. package/lib/memory-digest.js +195 -0
  204. package/lib/merge-rules.js +395 -0
  205. package/lib/migrate-dry-run.js +466 -0
  206. package/lib/orientation.js +863 -0
  207. package/lib/package-manager-remediation.js +103 -0
  208. package/lib/package-root.js +381 -0
  209. package/lib/patch-intent.js +890 -0
  210. package/lib/plugin-catalog.js +3 -4
  211. package/lib/plugin-manager.js +0 -5
  212. package/lib/pr-bundle.js +186 -0
  213. package/lib/pr-monitor/differ.js +195 -0
  214. package/lib/pr-monitor/events.js +0 -0
  215. package/lib/pr-monitor/gather.js +124 -0
  216. package/lib/pr-monitor/journal.js +299 -0
  217. package/lib/pr-monitor/monitor.js +146 -0
  218. package/lib/pr-monitor/render-sticky.js +157 -0
  219. package/lib/pr-monitor/watch-lifecycle.js +95 -0
  220. package/lib/pr-monitor/watch.js +247 -0
  221. package/lib/pr-pull.js +1273 -0
  222. package/lib/pr-shepherd.js +494 -0
  223. package/lib/pr-state-validator.js +59 -0
  224. package/lib/preflight/gates.js +237 -0
  225. package/lib/preflight/runner.js +116 -0
  226. package/lib/project-discovery.js +0 -53
  227. package/lib/project-memory.js +166 -0
  228. package/lib/protected-path-manifest.js +281 -0
  229. package/lib/protected-state-surfaces.js +387 -0
  230. package/lib/release-readiness.js +2089 -0
  231. package/lib/reset.js +59 -45
  232. package/lib/review-adapter.js +68 -0
  233. package/lib/rules-sync.js +260 -0
  234. package/lib/runtime-health.js +332 -23
  235. package/lib/safety-config-renderer.js +268 -0
  236. package/lib/setup-action-log.js +1 -7
  237. package/lib/setup.js +27 -65
  238. package/lib/shell-utils.js +76 -6
  239. package/lib/skills-sync.js +330 -0
  240. package/lib/smart-status/conflicts.js +205 -0
  241. package/lib/smart-status/scoring.js +191 -0
  242. package/lib/status/beads-snapshot.js +145 -0
  243. package/lib/status/presenter.js +216 -0
  244. package/lib/status/snapshot.js +186 -0
  245. package/lib/sync-backend.js +202 -0
  246. package/lib/untrusted-content.js +52 -0
  247. package/lib/upgrade-safety.js +199 -0
  248. package/lib/workflow/enforce-stage.js +298 -47
  249. package/lib/workflow/stage-transition.js +115 -0
  250. package/lib/workflow/stages.js +30 -6
  251. package/lib/workflow/state-manager.js +159 -14
  252. package/lib/workflow/state.js +23 -1
  253. package/lib/workflow-profiles.js +17 -5
  254. package/package.json +46 -36
  255. package/rules/documentation.md +19 -0
  256. package/rules/kernel-tracking.md +26 -0
  257. package/rules/security.md +22 -0
  258. package/rules/tdd.md +20 -0
  259. package/rules/workflow.md +27 -0
  260. package/scripts/auto-backing-issue.js +47 -0
  261. package/scripts/beads-context.sh +165 -22
  262. package/scripts/beads-migrate-to-dolt.sh +7 -0
  263. package/scripts/beads-upgrade-smoke.sh +284 -0
  264. package/scripts/behavioral-judge.sh +115 -11
  265. package/scripts/benchmark.js +349 -63
  266. package/scripts/bootstrap-windows-tools.sh +78 -0
  267. package/scripts/branch-protection.js +2 -3
  268. package/scripts/check-agents.js +34 -137
  269. package/scripts/commitlint.js +3 -1
  270. package/scripts/conflict-detect.sh +3 -0
  271. package/scripts/dep-guard-analyze.js +52 -17
  272. package/scripts/dep-guard-keyword-ripple.js +29 -0
  273. package/scripts/dep-guard-render-review.js +86 -0
  274. package/scripts/dep-guard.sh +64 -232
  275. package/scripts/file-index.sh +3 -0
  276. package/scripts/forge-team/lib/claim.sh +34 -18
  277. package/scripts/forge-team/lib/dashboard.sh +61 -86
  278. package/scripts/forge-team/lib/epic.sh +99 -263
  279. package/scripts/forge-team/lib/hooks.sh +26 -28
  280. package/scripts/forge-team/lib/identity.sh +4 -4
  281. package/scripts/forge-team/lib/sync-github.sh +144 -47
  282. package/scripts/forge-team/lib/verify.sh +93 -83
  283. package/scripts/forge-team/lib/workload.sh +41 -65
  284. package/scripts/forge-team/tests/claim.test.sh +25 -19
  285. package/scripts/forge-team/tests/dashboard.test.sh +31 -46
  286. package/scripts/forge-team/tests/epic.test.sh +52 -71
  287. package/scripts/forge-team/tests/hooks.test.sh +38 -50
  288. package/scripts/forge-team/tests/identity.test.sh +3 -3
  289. package/scripts/forge-team/tests/integration.test.sh +44 -66
  290. package/scripts/forge-team/tests/sync-github.test.sh +183 -79
  291. package/scripts/forge-team/tests/verify.test.sh +37 -46
  292. package/scripts/forge-team/tests/workflow-integration.test.sh +4 -4
  293. package/scripts/forge-team/tests/workload.test.sh +32 -66
  294. package/scripts/gen-command-manifest.js +153 -0
  295. package/scripts/gen-embedded-assets.mjs +129 -0
  296. package/scripts/install.ps1 +139 -0
  297. package/scripts/install.sh +268 -0
  298. package/scripts/lib/beads-migrate-to-dolt.mjs +503 -0
  299. package/scripts/lib/release-asset.mjs +84 -0
  300. package/scripts/parity-check.mjs +145 -0
  301. package/scripts/parity-check.test.mjs +58 -0
  302. package/scripts/pin-agentic-workflow-images.js +112 -0
  303. package/scripts/pr-coordinator.sh +3 -0
  304. package/scripts/preflight-sonar.eslint.config.mjs +44 -0
  305. package/scripts/preflight.sh +108 -0
  306. package/scripts/protected-state-check.js +104 -0
  307. package/scripts/smart-status-score.js +31 -0
  308. package/scripts/smart-status-sessions.js +51 -0
  309. package/scripts/smart-status.sh +117 -369
  310. package/scripts/spikes/config-race-bench.js +111 -0
  311. package/scripts/spikes/harness-capability-matrix.js +13 -0
  312. package/scripts/spikes/patch-anchor-stability-bench.js +125 -0
  313. package/scripts/spikes/protected-path-manifest.js +20 -0
  314. package/scripts/spikes/skill-auto-invoke-parity.js +292 -0
  315. package/scripts/sync-agent-skills.js +62 -0
  316. package/scripts/sync-agentic-workflow.js +48 -0
  317. package/scripts/sync-utils.sh +3 -0
  318. package/scripts/test-ci-shard.js +251 -0
  319. package/scripts/test-dashboard.js +188 -52
  320. package/scripts/test-full-suite.js +186 -0
  321. package/scripts/test-profile.js +278 -0
  322. package/scripts/test.js +302 -28
  323. package/scripts/validate.js +143 -0
  324. package/scripts/validate.sh +18 -1
  325. package/skills/claim-safety/SKILL.md +102 -0
  326. package/skills/claim-safety/evals/evals.json +46 -0
  327. package/{.github/prompts/dev.prompt.md → skills/dev/SKILL.md} +46 -52
  328. package/skills/dev/evals/evals.json +50 -0
  329. package/skills/hermes-forge/SKILL.md +185 -0
  330. package/skills/hermes-forge/evals/evals.json +46 -0
  331. package/skills/issue-basics/SKILL.md +111 -0
  332. package/skills/issue-basics/evals/evals.json +46 -0
  333. package/skills/kernel/SKILL.md +166 -0
  334. package/skills/kernel/evals/evals.json +50 -0
  335. package/skills/memory/SKILL.md +102 -0
  336. package/skills/parallel-deep-research/SKILL.md +14 -11
  337. package/skills/parallel-deep-research/evals/evals.json +11 -27
  338. package/{.github/prompts/plan.prompt.md → skills/plan/SKILL.md} +134 -159
  339. package/skills/plan/evals/evals.json +42 -0
  340. package/skills/research/SKILL.md +195 -0
  341. package/skills/research/evals/evals.json +42 -0
  342. package/{.github/prompts/review.prompt.md → skills/review/SKILL.md} +98 -62
  343. package/skills/review/evals/evals.json +42 -0
  344. package/skills/rollback/SKILL.md +110 -0
  345. package/skills/rollback/evals/evals.json +46 -0
  346. package/skills/rollback/references/methods.md +204 -0
  347. package/{.cursor/commands/rollback.md → skills/rollback/references/workflow-integration.md} +10 -284
  348. package/skills/shepherd/SKILL.md +66 -0
  349. package/skills/shepherd/evals/evals.json +42 -0
  350. package/skills/ship/SKILL.md +251 -0
  351. package/skills/ship/evals/evals.json +42 -0
  352. package/skills/smith/SKILL.md +142 -0
  353. package/skills/smith/evals/evals.json +46 -0
  354. package/skills/smith/references/autonomy-and-gates.md +94 -0
  355. package/{.github/prompts/sonarcloud.prompt.md → skills/sonarcloud/SKILL.md} +14 -3
  356. package/skills/sonarcloud/evals/evals.json +46 -0
  357. package/skills/sonarcloud-analysis/SKILL.md +18 -13
  358. package/skills/sonarcloud-analysis/evals/evals.json +11 -15
  359. package/skills/status/SKILL.md +102 -0
  360. package/skills/status/evals/evals.json +50 -0
  361. package/skills/triage-ready/SKILL.md +121 -0
  362. package/skills/triage-ready/evals/evals.json +42 -0
  363. package/{.github/prompts/validate.prompt.md → skills/validate/SKILL.md} +52 -29
  364. package/skills/validate/evals/evals.json +42 -0
  365. package/skills/verify/SKILL.md +299 -0
  366. package/skills/verify/evals/evals.json +50 -0
  367. package/.claude/commands/dev.md +0 -345
  368. package/.claude/commands/plan.md +0 -566
  369. package/.claude/commands/premerge.md +0 -186
  370. package/.claude/commands/research.md +0 -42
  371. package/.claude/commands/review.md +0 -451
  372. package/.claude/commands/rollback.md +0 -721
  373. package/.claude/commands/ship.md +0 -213
  374. package/.claude/commands/sonarcloud.md +0 -152
  375. package/.claude/commands/status.md +0 -90
  376. package/.claude/commands/validate.md +0 -288
  377. package/.claude/commands/verify.md +0 -269
  378. package/.claude/rules/workflow.md +0 -121
  379. package/.cline/workflows/dev.md +0 -342
  380. package/.cline/workflows/plan.md +0 -563
  381. package/.cline/workflows/premerge.md +0 -183
  382. package/.cline/workflows/research.md +0 -39
  383. package/.cline/workflows/review.md +0 -448
  384. package/.cline/workflows/rollback.md +0 -718
  385. package/.cline/workflows/ship.md +0 -210
  386. package/.cline/workflows/sonarcloud.md +0 -146
  387. package/.cline/workflows/status.md +0 -87
  388. package/.cline/workflows/validate.md +0 -285
  389. package/.cline/workflows/verify.md +0 -266
  390. package/.codex/config.toml +0 -11
  391. package/.codex/skills/dev/SKILL.md +0 -345
  392. package/.codex/skills/plan/SKILL.md +0 -566
  393. package/.codex/skills/premerge/SKILL.md +0 -186
  394. package/.codex/skills/research/SKILL.md +0 -42
  395. package/.codex/skills/review/SKILL.md +0 -451
  396. package/.codex/skills/rollback/SKILL.md +0 -721
  397. package/.codex/skills/ship/SKILL.md +0 -213
  398. package/.codex/skills/sonarcloud/SKILL.md +0 -149
  399. package/.codex/skills/status/SKILL.md +0 -90
  400. package/.codex/skills/validate/SKILL.md +0 -288
  401. package/.codex/skills/verify/SKILL.md +0 -269
  402. package/.cursor/commands/dev.md +0 -342
  403. package/.cursor/commands/plan.md +0 -563
  404. package/.cursor/commands/premerge.md +0 -183
  405. package/.cursor/commands/research.md +0 -39
  406. package/.cursor/commands/review.md +0 -448
  407. package/.cursor/commands/ship.md +0 -210
  408. package/.cursor/commands/sonarcloud.md +0 -146
  409. package/.cursor/commands/status.md +0 -87
  410. package/.cursor/commands/validate.md +0 -285
  411. package/.cursor/commands/verify.md +0 -266
  412. package/.cursorrules +0 -149
  413. package/.github/prompts/premerge.prompt.md +0 -188
  414. package/.github/prompts/research.prompt.md +0 -44
  415. package/.github/prompts/rollback.prompt.md +0 -723
  416. package/.github/prompts/ship.prompt.md +0 -215
  417. package/.github/prompts/status.prompt.md +0 -92
  418. package/.github/prompts/verify.prompt.md +0 -271
  419. package/.github/workflows/beads-to-github.yml +0 -56
  420. package/.github/workflows/github-to-beads.yml +0 -97
  421. package/.kilocode/workflows/dev.md +0 -346
  422. package/.kilocode/workflows/plan.md +0 -567
  423. package/.kilocode/workflows/premerge.md +0 -187
  424. package/.kilocode/workflows/research.md +0 -43
  425. package/.kilocode/workflows/review.md +0 -452
  426. package/.kilocode/workflows/rollback.md +0 -722
  427. package/.kilocode/workflows/ship.md +0 -214
  428. package/.kilocode/workflows/sonarcloud.md +0 -150
  429. package/.kilocode/workflows/status.md +0 -91
  430. package/.kilocode/workflows/validate.md +0 -289
  431. package/.kilocode/workflows/verify.md +0 -270
  432. package/.opencode/commands/dev.md +0 -345
  433. package/.opencode/commands/plan.md +0 -566
  434. package/.opencode/commands/premerge.md +0 -186
  435. package/.opencode/commands/research.md +0 -42
  436. package/.opencode/commands/review.md +0 -451
  437. package/.opencode/commands/rollback.md +0 -721
  438. package/.opencode/commands/ship.md +0 -213
  439. package/.opencode/commands/sonarcloud.md +0 -149
  440. package/.opencode/commands/status.md +0 -90
  441. package/.opencode/commands/validate.md +0 -288
  442. package/.opencode/commands/verify.md +0 -269
  443. package/.roo/commands/dev.md +0 -346
  444. package/.roo/commands/plan.md +0 -567
  445. package/.roo/commands/premerge.md +0 -187
  446. package/.roo/commands/research.md +0 -43
  447. package/.roo/commands/review.md +0 -452
  448. package/.roo/commands/rollback.md +0 -722
  449. package/.roo/commands/ship.md +0 -214
  450. package/.roo/commands/sonarcloud.md +0 -150
  451. package/.roo/commands/status.md +0 -91
  452. package/.roo/commands/validate.md +0 -289
  453. package/.roo/commands/verify.md +0 -270
  454. package/docs/BEADS_GITHUB_SYNC.md +0 -255
  455. package/docs/GREPTILE_SETUP.md +0 -400
  456. package/docs/MANUAL_REVIEW_GUIDE.md +0 -106
  457. package/docs/SETUP.md +0 -663
  458. package/docs/VALIDATION.md +0 -363
  459. package/lib/agents/cline.plugin.json +0 -29
  460. package/lib/agents/copilot.plugin.json +0 -24
  461. package/lib/agents/kilocode.plugin.json +0 -22
  462. package/lib/agents/opencode.plugin.json +0 -23
  463. package/lib/agents/roo.plugin.json +0 -30
  464. package/lib/beads-health-check.js +0 -143
  465. package/lib/commands/commands-reset.js +0 -147
  466. package/opencode.json +0 -67
  467. package/scripts/beads-context.test.js +0 -567
  468. package/scripts/github-beads-sync/comment.mjs +0 -64
  469. package/scripts/github-beads-sync/config.mjs +0 -148
  470. package/scripts/github-beads-sync/github-api.mjs +0 -131
  471. package/scripts/github-beads-sync/index.mjs +0 -332
  472. package/scripts/github-beads-sync/label-mapper.mjs +0 -54
  473. package/scripts/github-beads-sync/mapping.mjs +0 -78
  474. package/scripts/github-beads-sync/reverse-sync-cli.mjs +0 -31
  475. package/scripts/github-beads-sync/reverse-sync.mjs +0 -138
  476. package/scripts/github-beads-sync/run-bd.mjs +0 -161
  477. package/scripts/github-beads-sync/sanitize.mjs +0 -121
  478. package/scripts/github-beads-sync.config.json +0 -26
  479. package/scripts/sync-commands.js +0 -600
@@ -27,7 +27,6 @@ const STAGES = Object.freeze({
27
27
  VALIDATE: 'validate',
28
28
  SHIP: 'ship',
29
29
  REVIEW: 'review',
30
- PREMERGE: 'premerge',
31
30
  });
32
31
 
33
32
  const BUDGET_MODES = Object.freeze({
@@ -326,12 +325,12 @@ const CATALOG = Object.freeze({
326
325
  install: { method: 'config', cmd: 'Self-hosted (qodo.ai)' },
327
326
  },
328
327
 
329
- // ── Merge ──
328
+ // ── Release automation (ship-time) ──
330
329
  changesets: {
331
330
  name: 'Changesets',
332
331
  type: 'cli',
333
332
  tier: 'free',
334
- stage: 'premerge',
333
+ stage: 'ship',
335
334
  description: 'Versioning and changelog management',
336
335
  detectWhen: ['file:package.json'],
337
336
  install: { method: 'npm', cmd: 'bun add -D @changesets/cli', dev: true },
@@ -340,7 +339,7 @@ const CATALOG = Object.freeze({
340
339
  name: 'Release Please',
341
340
  type: 'config',
342
341
  tier: 'free',
343
- stage: 'premerge',
342
+ stage: 'ship',
344
343
  description: 'Automated release PRs by Google',
345
344
  detectWhen: [],
346
345
  install: { method: 'config', cmd: 'GitHub Action (google-github-actions/release-please-action)' },
@@ -30,12 +30,7 @@ const DEFAULT_SUPPORT_STATUS = 'supported';
30
30
  const SURFACE_BY_AGENT_ID = Object.freeze({
31
31
  claude: 'cli-first',
32
32
  codex: 'cli-first',
33
- opencode: 'cli-first',
34
- cline: 'editor-native',
35
33
  cursor: 'editor-native',
36
- copilot: 'editor-native',
37
- kilocode: 'editor-native',
38
- roo: 'editor-native',
39
34
  });
40
35
 
41
36
  function isPlainObject(value) {
@@ -0,0 +1,186 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * PR-state bundle — the "everything ready for the agent" gather half of the
5
+ * monitor (kernel 9b24257f, increment 1).
6
+ *
7
+ * `gatherPrBundle` aggregates the COMPLETE, read-only state of an open PR into
8
+ * one structured object a fixer-agent can act on WITHOUT fetching anything
9
+ * itself: every unresolved review thread (any author), the merge state, the
10
+ * full CI rollup (tagged with required-check membership), branch divergence, and
11
+ * predicted merge conflicts.
12
+ *
13
+ * This module is pure aggregation over a validated pr-state adapter. It makes NO
14
+ * decisions and takes NO actions — it never merges, never resolves threads,
15
+ * never writes code. The decision state machine stays in lib/pr-shepherd.js; the
16
+ * agent dispatch, the continuous loop, and close-on-merge that will consume this
17
+ * bundle are follow-ups, not part of this increment.
18
+ *
19
+ * @module pr-bundle
20
+ */
21
+
22
+ const { isGreen, isFailed } = require('./pr-shepherd');
23
+
24
+ /**
25
+ * Map an unresolved review thread to the agent-input comment shape. The named
26
+ * `author`/`body` come from the thread opener (the canonical anchor), but the
27
+ * FULL `comments` array is carried too — so a later human reply on a
28
+ * bot-opened thread is never hidden from the fixer-agent (which fetches
29
+ * nothing and acts only on this input). Tolerates adapters/payloads that don't
30
+ * surface every field.
31
+ *
32
+ * @param {object} thread
33
+ * @returns {{ author: string, path: string|null, line: number|null, body: string, threadId: string|null, comments: {author: string, body: string}[] }}
34
+ */
35
+ function toUnresolvedComment(thread) {
36
+ const comments = (Array.isArray(thread.comments) ? thread.comments : []).map((c) => ({
37
+ author: String((c.author && c.author.login) || c.author || ''),
38
+ body: String(c.body || ''),
39
+ }));
40
+ const first = comments[0] || {};
41
+ return {
42
+ author: first.author || String(thread.author || ''),
43
+ path: thread.path || null,
44
+ line: typeof thread.line === 'number' ? thread.line : null,
45
+ body: first.body || String(thread.body || ''),
46
+ threadId: thread.threadId || thread.id || null,
47
+ comments,
48
+ };
49
+ }
50
+
51
+ /**
52
+ * Read ALL unresolved review threads (any author) and map them to the agent
53
+ * comment shape.
54
+ *
55
+ * Returns `{ comments, available, error }` — NOT a bare array — so a consumer can
56
+ * tell three states apart that a `[]`-on-everything collapse would conflate:
57
+ * - capability absent → available:false, error:null (adapter has no readComments)
58
+ * - read succeeded → available:true, error:null (comments is authoritative)
59
+ * - read THREW → available:false, error:msg (comments unreadable — NOT "none")
60
+ *
61
+ * The third case is the critical one: a monitor must never treat "GitHub comments
62
+ * were unreadable" as "this PR has no unresolved feedback" and silently skip work.
63
+ * A failed read still degrades gracefully (the rest of the bundle survives), but
64
+ * the failure is surfaced rather than swallowed.
65
+ *
66
+ * @returns {Promise<{ comments: object[], available: boolean, error: string|null }>}
67
+ */
68
+ async function gatherUnresolvedComments(adapter, { owner, repo, pr }) {
69
+ if (typeof adapter.readComments !== 'function') {
70
+ return { comments: [], available: false, error: null };
71
+ }
72
+ try {
73
+ const threads = await adapter.readComments({ owner, repo, pr });
74
+ const comments = (Array.isArray(threads) ? threads : [])
75
+ .filter((t) => !(t.isResolved || t.resolved))
76
+ .map(toUnresolvedComment);
77
+ return { comments, available: true, error: null };
78
+ } catch (err) {
79
+ // A failed comment read must not sink the rest of the bundle, but it MUST be
80
+ // distinguishable from "no unresolved threads" — preserve the error signal.
81
+ return { comments: [], available: false, error: err.message || 'comment read failed' };
82
+ }
83
+ }
84
+
85
+ /**
86
+ * Predict merge conflicts. `detectConflicts` is optional on the adapter; when
87
+ * absent or failing, report it as unsupported rather than throwing.
88
+ */
89
+ async function gatherConflicts(adapter, { baseRef, cwd }) {
90
+ if (typeof adapter.detectConflicts !== 'function') {
91
+ return { supported: false, reason: 'adapter has no detectConflicts capability' };
92
+ }
93
+ try {
94
+ return await adapter.detectConflicts({ baseRef, cwd });
95
+ } catch (error) {
96
+ return { supported: false, reason: error.message || 'conflict detection failed' };
97
+ }
98
+ }
99
+
100
+ /**
101
+ * Tag the CI rollup with required-check membership and split out failing/pending.
102
+ *
103
+ * `requiredSet` is the branch-protection required set (array), or `null` when it
104
+ * is unreadable — in which case `required` is left `null` per check rather than
105
+ * `false`, so the agent is never told "nothing is required" on bad data.
106
+ *
107
+ * @param {object[]} checks - Normalized rollup from `readState`.
108
+ * @param {string[]|null} requiredSet
109
+ * @returns {{ checks: object[], failing: object[], pending: object[] }}
110
+ */
111
+ function buildCi(checks, requiredSet) {
112
+ const isRequired = (name) => (requiredSet === null ? null : requiredSet.includes(name));
113
+ const tagged = (Array.isArray(checks) ? checks : []).map((c) => ({
114
+ name: c.name || '',
115
+ status: c.status || '',
116
+ conclusion: c.conclusion || '',
117
+ required: isRequired(c.name || ''),
118
+ detailsUrl: c.detailsUrl || null,
119
+ }));
120
+ return {
121
+ checks: tagged,
122
+ failing: tagged.filter((c) => isFailed(c)),
123
+ pending: tagged.filter((c) => !isGreen(c) && !isFailed(c)),
124
+ };
125
+ }
126
+
127
+ /**
128
+ * Gather the complete read-only PR-state bundle for the monitor's fixer-agent.
129
+ *
130
+ * @param {object} ctx
131
+ * @param {string} ctx.pr
132
+ * @param {string} ctx.owner
133
+ * @param {string} ctx.repo
134
+ * @param {string} ctx.base - Base BRANCH name (for branch-protection lookup).
135
+ * @param {string} ctx.baseRef - Base REF for divergence/conflicts (e.g. `origin/master`).
136
+ * @param {string} [ctx.cwd]
137
+ * @param {object} ctx.adapter - A validated pr-state adapter.
138
+ * @returns {Promise<object>} the structured bundle.
139
+ */
140
+ async function gatherPrBundle({ pr, owner, repo, base, baseRef, cwd, adapter }) {
141
+ if (!adapter || typeof adapter.readState !== 'function') {
142
+ throw new Error('gatherPrBundle requires a pr-state adapter with readState');
143
+ }
144
+
145
+ const state = await adapter.readState(pr);
146
+ // NOTE: required-check lookup needs the base BRANCH name (`base`), not the
147
+ // remote ref (`baseRef`); passing the ref builds a bad protection path and
148
+ // silently yields a null required set.
149
+ const requiredRaw = await adapter.readRequiredChecks({ owner, repo, base });
150
+ const divergence = await adapter.readDivergence({ baseRef, cwd });
151
+ const comments = await gatherUnresolvedComments(adapter, { owner, repo, pr });
152
+ const conflicts = await gatherConflicts(adapter, { baseRef, cwd });
153
+
154
+ const requiredSet = requiredRaw === undefined ? null : requiredRaw;
155
+
156
+ return {
157
+ pr: String(pr),
158
+ owner,
159
+ repo,
160
+ base,
161
+ baseRef,
162
+ unresolvedComments: comments.comments,
163
+ // Availability signal for the comment read: when false with a non-null error,
164
+ // the threads were UNREADABLE (not absent) — a monitor must not treat the empty
165
+ // unresolvedComments list as "no feedback" and skip work.
166
+ unresolvedCommentsAvailable: comments.available,
167
+ unresolvedCommentsError: comments.error,
168
+ mergeState: {
169
+ mergeable: state.mergeable || 'UNKNOWN',
170
+ mergeStateStatus: state.mergeStateStatus || 'UNKNOWN',
171
+ state: String(state.state || 'OPEN').toUpperCase(),
172
+ },
173
+ ci: buildCi(state.checks, requiredSet),
174
+ branch: {
175
+ ahead: divergence.ahead || 0,
176
+ behind: divergence.behind || 0,
177
+ },
178
+ conflicts,
179
+ };
180
+ }
181
+
182
+ module.exports = {
183
+ gatherPrBundle,
184
+ buildCi,
185
+ toUnresolvedComment,
186
+ };
@@ -0,0 +1,195 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * PR-monitor differ — the PURE heart of the monitor. `diffSnapshots(prev, next)`
5
+ * returns the ordered candidate events for the transition between two normalized
6
+ * snapshots (see lib/pr-monitor/gather.js for the shape). No I/O, no clock, no
7
+ * randomness — same inputs always yield the same events, which is what makes the
8
+ * monitor deterministic and duplicate-free across restarts.
9
+ *
10
+ * Each event carries a CONTENT key (head sha, threadId, check name+sha, comment
11
+ * id, review author+commit, surface) so the journal can dedup by identity.
12
+ *
13
+ * @module pr-monitor/differ
14
+ */
15
+
16
+ const { EVENT_TYPES: T, makeEvent } = require('./events');
17
+
18
+ /** Index an array of objects by a key field into a Map. */
19
+ function indexBy(list, field) {
20
+ const map = new Map();
21
+ for (const item of (Array.isArray(list) ? list : [])) {
22
+ if (item?.[field] != null) map.set(String(item[field]), item);
23
+ }
24
+ return map;
25
+ }
26
+
27
+ /** True when every check is green (none failed, none pending). */
28
+ function allGreen(checks) {
29
+ const list = Array.isArray(checks) ? checks : [];
30
+ return list.length > 0 && list.every((c) => c.class === 'green');
31
+ }
32
+
33
+ /** head.pushed — the PR's head sha advanced. */
34
+ function diffHead(prev, next, out) {
35
+ if (next.headSha && prev.headSha !== next.headSha) {
36
+ out.push(makeEvent(T.HEAD_PUSHED, next.headSha, { from: prev.headSha || null, to: next.headSha }));
37
+ }
38
+ }
39
+
40
+ /** check.failed / check.recovered / checks.green — keyed by name+sha. */
41
+ function diffChecks(prev, next, out) {
42
+ const prevByName = indexBy(prev.checks, 'name');
43
+ for (const c of (next.checks || [])) {
44
+ const before = prevByName.get(String(c.name));
45
+ const wasFailed = before?.class === 'failed';
46
+ if (c.class === 'failed' && !wasFailed) {
47
+ out.push(makeEvent(T.CHECK_FAILED, `${c.name}:${next.headSha}`, { name: c.name }));
48
+ } else if (c.class === 'green' && wasFailed) {
49
+ out.push(makeEvent(T.CHECK_RECOVERED, `${c.name}:${next.headSha}`, { name: c.name }));
50
+ }
51
+ }
52
+ if (allGreen(next.checks) && !allGreen(prev.checks)) {
53
+ out.push(makeEvent(T.CHECKS_GREEN, next.headSha, {}));
54
+ }
55
+ }
56
+
57
+ /** thread.opened / thread.reply / thread.resolved — keyed by threadId. */
58
+ function diffThreads(prev, next, out) {
59
+ const prevById = indexBy(prev.threads, 'threadId');
60
+ for (const t of (next.threads || [])) {
61
+ const before = prevById.get(String(t.threadId));
62
+ if (!before) {
63
+ if (t.actionable) out.push(makeEvent(T.THREAD_OPENED, t.threadId, { path: t.path || null }));
64
+ continue;
65
+ }
66
+ if ((t.commentCount || 0) > (before.commentCount || 0)) {
67
+ out.push(makeEvent(T.THREAD_REPLY, `${t.threadId}:${t.commentCount}`, { threadId: t.threadId }));
68
+ }
69
+ if (t.isResolved && !before.isResolved) {
70
+ out.push(makeEvent(T.THREAD_RESOLVED, t.threadId, {}));
71
+ }
72
+ }
73
+ }
74
+
75
+ /** comment.posted — a new direct PR issue comment, keyed by comment id. */
76
+ function diffComments(prev, next, out) {
77
+ const prevIds = indexBy(prev.comments, 'id');
78
+ for (const c of (next.comments || [])) {
79
+ if (!prevIds.has(String(c.id))) {
80
+ out.push(makeEvent(T.COMMENT_POSTED, c.id, { author: c.author || '' }));
81
+ }
82
+ }
83
+ }
84
+
85
+ /** review.submitted — a new/updated review per author (state or commit changed). */
86
+ function diffReviews(prev, next, out) {
87
+ const prevByAuthor = indexBy(prev.reviews, 'author');
88
+ for (const r of (next.reviews || [])) {
89
+ const before = prevByAuthor.get(String(r.author));
90
+ const changed = !before
91
+ || before.submittedAt !== r.submittedAt
92
+ || before.commitOid !== r.commitOid;
93
+ if (changed) {
94
+ out.push(makeEvent(T.REVIEW_SUBMITTED, `${r.author}:${r.commitOid || r.submittedAt}`, {
95
+ author: r.author, verdict: r.state, commitOid: r.commitOid || null,
96
+ }));
97
+ }
98
+ }
99
+ }
100
+
101
+ /** conflict.appeared / conflict.cleared — keyed by head sha. */
102
+ function diffConflict(prev, next, out) {
103
+ if (next.conflicts === true && prev.conflicts !== true) {
104
+ out.push(makeEvent(T.CONFLICT_APPEARED, next.headSha, {}));
105
+ } else if (next.conflicts === false && prev.conflicts === true) {
106
+ out.push(makeEvent(T.CONFLICT_CLEARED, next.headSha, {}));
107
+ }
108
+ }
109
+
110
+ /** branch.behind — the branch fell behind its base. */
111
+ function diffBehind(prev, next, out) {
112
+ if ((next.behind || 0) > 0 && (prev.behind || 0) === 0) {
113
+ out.push(makeEvent(T.BRANCH_BEHIND, `${next.headSha}:${next.behind}`, { behind: next.behind }));
114
+ }
115
+ }
116
+
117
+ /** verdict.changed — the fail-closed merge verdict flipped. */
118
+ function diffVerdict(prev, next, out) {
119
+ const from = prev.verdict ? prev.verdict.state : null;
120
+ const to = next.verdict ? next.verdict.state : null;
121
+ if (from !== to) {
122
+ out.push(makeEvent(T.VERDICT_CHANGED, `${from}->${to}:${next.headSha}`, {
123
+ from, to, reason: next.verdict ? next.verdict.reason || null : null,
124
+ }));
125
+ }
126
+ }
127
+
128
+ /** pr.merged / pr.closed — terminal transitions from an OPEN PR. */
129
+ function diffPrState(prev, next, out) {
130
+ if (prev.prState === 'OPEN' && next.prState === 'MERGED') {
131
+ out.push(makeEvent(T.PR_MERGED, 'MERGED', {}));
132
+ } else if (prev.prState === 'OPEN' && next.prState === 'CLOSED') {
133
+ out.push(makeEvent(T.PR_CLOSED, 'CLOSED', {}));
134
+ }
135
+ }
136
+
137
+ /** monitor.degraded — a read surface newly became unreadable (fail-closed). */
138
+ function diffDegraded(prev, next, out) {
139
+ const prevSurfaces = new Set((prev.degraded || []).map((d) => d.surface));
140
+ for (const d of (next.degraded || [])) {
141
+ if (!prevSurfaces.has(d.surface)) {
142
+ out.push(makeEvent(T.MONITOR_DEGRADED, d.surface, { surface: d.surface, error: d.error || '' }));
143
+ }
144
+ }
145
+ }
146
+
147
+ /**
148
+ * Baseline events for the FIRST pass (no prior snapshot): a single verdict
149
+ * baseline plus any already-terminal/degraded state — bounded, never a replay of
150
+ * every historical thread/check.
151
+ */
152
+ function diffBaseline(next, out) {
153
+ out.push(makeEvent(T.VERDICT_CHANGED, `baseline:${next.headSha}`, {
154
+ from: null, to: next.verdict ? next.verdict.state : null,
155
+ reason: next.verdict ? next.verdict.reason || null : null,
156
+ }));
157
+ if (next.prState === 'MERGED') out.push(makeEvent(T.PR_MERGED, 'MERGED', {}));
158
+ if (next.prState === 'CLOSED') out.push(makeEvent(T.PR_CLOSED, 'CLOSED', {}));
159
+ for (const d of (next.degraded || [])) {
160
+ out.push(makeEvent(T.MONITOR_DEGRADED, d.surface, { surface: d.surface, error: d.error || '' }));
161
+ }
162
+ }
163
+
164
+ /**
165
+ * Diff two normalized snapshots into ordered candidate events. `prev === null`
166
+ * → first-pass baseline.
167
+ *
168
+ * @param {object|null} prev
169
+ * @param {object} next
170
+ * @returns {Array<{ type: string, key: string, data: object }>}
171
+ */
172
+ function diffSnapshots(prev, next) {
173
+ const out = [];
174
+ if (!prev) {
175
+ diffBaseline(next, out);
176
+ return out;
177
+ }
178
+ diffHead(prev, next, out);
179
+ diffChecks(prev, next, out);
180
+ diffThreads(prev, next, out);
181
+ diffComments(prev, next, out);
182
+ diffReviews(prev, next, out);
183
+ diffConflict(prev, next, out);
184
+ diffBehind(prev, next, out);
185
+ diffVerdict(prev, next, out);
186
+ diffPrState(prev, next, out);
187
+ diffDegraded(prev, next, out);
188
+ return out;
189
+ }
190
+
191
+ module.exports = {
192
+ diffSnapshots,
193
+ allGreen,
194
+ indexBy,
195
+ };
Binary file
@@ -0,0 +1,124 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * PR-monitor gather — turn ONE `gatherPrSnapshot` read (the SAME shared read the
5
+ * `--pull` verdict uses) into the normalized snapshot the differ compares. Because
6
+ * the verdict and the monitor events both derive from this one read, they can
7
+ * never disagree (the frame in docs/work/2026-07-13-pr-monitor/plan.md).
8
+ *
9
+ * The normalized snapshot is the diff subject:
10
+ * { repo, pr, headSha, prState, draft, verdict:{state,reason},
11
+ * checks:[{name,class}], threads:[{threadId,isResolved,isOutdated,commentCount,actionable,path}],
12
+ * reviews:[{author,state,commitOid,submittedAt}], comments:[{id,author}],
13
+ * behind, conflicts:(true|false|null), degraded:[{surface,error}] }
14
+ *
15
+ * @module pr-monitor/gather
16
+ */
17
+
18
+ const { gatherPrSnapshot } = require('../pr-pull');
19
+ const { isFailed, isGreen } = require('../pr-shepherd');
20
+
21
+ /** green | failed | pending — reuses the SAME predicates as the verdict core. */
22
+ function classifyCheck(check) {
23
+ if (isFailed(check)) return 'failed';
24
+ if (isGreen(check)) return 'green';
25
+ return 'pending';
26
+ }
27
+
28
+ function normalizeChecks(checks) {
29
+ return (Array.isArray(checks) ? checks : []).map((c) => ({
30
+ name: c.name || '',
31
+ class: classifyCheck(c),
32
+ }));
33
+ }
34
+
35
+ function normalizeThreads(threads) {
36
+ return (Array.isArray(threads) ? threads : []).map((t) => ({
37
+ threadId: t.threadId || '',
38
+ isResolved: Boolean(t.isResolved),
39
+ isOutdated: Boolean(t.isOutdated),
40
+ commentCount: Array.isArray(t.comments) ? t.comments.length : 0,
41
+ actionable: !t.isResolved && !t.isOutdated,
42
+ path: t.path || null,
43
+ }));
44
+ }
45
+
46
+ function normalizeReviews(reviews) {
47
+ return (Array.isArray(reviews) ? reviews : []).map((r) => ({
48
+ author: r.author || '',
49
+ state: r.state || '',
50
+ commitOid: r.commitOid || null,
51
+ submittedAt: r.submittedAt || null,
52
+ }));
53
+ }
54
+
55
+ function normalizeComments(comments) {
56
+ return (Array.isArray(comments) ? comments : []).map((c) => ({
57
+ id: c.id || `${c.author || ''}:${c.createdAt || ''}`,
58
+ author: c.author || '',
59
+ }));
60
+ }
61
+
62
+ function normalizeDegraded(degraded) {
63
+ return (Array.isArray(degraded) ? degraded : []).map((d) => ({
64
+ surface: d.source || d.surface || 'unknown',
65
+ error: d.error || '',
66
+ }));
67
+ }
68
+
69
+ /**
70
+ * Conflict prediction → tri-state boolean: `true`/`false` when supported,
71
+ * `null` when unknown (unsupported git, unreadable ref) so the differ never
72
+ * emits a false conflict.appeared/cleared on missing data.
73
+ */
74
+ function conflictBool(conflicts) {
75
+ if (conflicts?.supported === true) return Boolean(conflicts.conflicted);
76
+ return null;
77
+ }
78
+
79
+ /**
80
+ * Normalize a raw `gatherPrSnapshot` result into the monitor diff subject.
81
+ *
82
+ * @param {object} snap - gatherPrSnapshot result.
83
+ * @param {{ repo: string, pr: string|number }} ctx
84
+ * @returns {object}
85
+ */
86
+ function normalizeSnapshot(snap, ctx) {
87
+ const state = snap.state || {};
88
+ return {
89
+ repo: ctx.repo,
90
+ pr: String(ctx.pr),
91
+ headSha: state.headSha || '',
92
+ prState: String(state.state || 'OPEN').toUpperCase(),
93
+ draft: Boolean(snap.draft),
94
+ verdict: { state: snap.verdict || 'UNKNOWN', reason: null },
95
+ checks: normalizeChecks(state.checks),
96
+ threads: normalizeThreads(snap.threads),
97
+ reviews: normalizeReviews(snap.reviews),
98
+ comments: normalizeComments(snap.issueComments),
99
+ behind: snap.behind || 0,
100
+ conflicts: conflictBool(snap.conflicts),
101
+ degraded: normalizeDegraded(snap.degraded),
102
+ };
103
+ }
104
+
105
+ /**
106
+ * Gather + normalize the monitor snapshot for a PR. `ctx.gatherSnapshot` is
107
+ * injectable for tests; production uses the shared `gatherPrSnapshot`.
108
+ *
109
+ * @param {object} ctx - the same ctx shape gatherPrSnapshot takes (pr, owner,
110
+ * repo, base, baseRef, cwd, self, adapter, now, settleWindowMs).
111
+ * @returns {Promise<object>} normalized snapshot.
112
+ */
113
+ async function gatherMonitorSnapshot(ctx) {
114
+ const gather = ctx.gatherSnapshot || gatherPrSnapshot;
115
+ const snap = await gather(ctx);
116
+ return normalizeSnapshot(snap, ctx);
117
+ }
118
+
119
+ module.exports = {
120
+ gatherMonitorSnapshot,
121
+ normalizeSnapshot,
122
+ classifyCheck,
123
+ conflictBool,
124
+ };