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
@@ -0,0 +1,237 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Gate composition + real sub-runners for `forge preflight`.
5
+ *
6
+ * Each real runner takes an injectable `spawn` (defaults to spawnSync) so the
7
+ * composition and the runners are unit-testable without touching the shell.
8
+ * The four gates give DETERMINISTIC-GATE PARITY with CI on the blast radius of
9
+ * a change, without the slow full matrix:
10
+ *
11
+ * 1. lint — eslint --max-warnings 0 on changed files (or all with --all)
12
+ * 2. drift — skills-mirror + registry-sync + agentic-workflow structural asserts
13
+ * 3. sonar — eslint-plugin-sonarjs (cognitive-complexity=15 + parity rules)
14
+ * 4. affected — only the tests mapped from the changed files
15
+ *
16
+ * @module preflight/gates
17
+ */
18
+
19
+ const path = require('node:path');
20
+ const { spawnSync } = require('node:child_process');
21
+ const { execFileSync } = require('node:child_process');
22
+ const nodeFs = require('node:fs');
23
+ const { getAffectedTestFiles } = require('../commands/test');
24
+
25
+ const IS_WINDOWS = process.platform === 'win32';
26
+
27
+ /** Filter a changed-file list down to the JS files eslint should look at. */
28
+ function lintableFiles(files) {
29
+ if (!Array.isArray(files)) return [];
30
+ return files.filter((f) => /\.(mjs|cjs|js)$/.test(f) && !f.includes('node_modules'));
31
+ }
32
+
33
+ /** Short, bounded summary for a spawn result. */
34
+ function statusSummary(result, okMsg, failMsg) {
35
+ return result && result.status === 0
36
+ ? { ok: true, summary: okMsg }
37
+ : { ok: false, summary: failMsg };
38
+ }
39
+
40
+ /**
41
+ * Gate 1 — ESLint on the change blast radius (or the whole tree with --all).
42
+ * `files === null` means "lint everything" (`eslint .`).
43
+ */
44
+ function runEslint(files, { projectRoot, spawn = spawnSync } = {}) {
45
+ const lintAll = files === null;
46
+ const targets = lintAll ? ['.'] : lintableFiles(files);
47
+ if (targets.length === 0) {
48
+ return { ok: true, summary: 'no changed JS files' };
49
+ }
50
+ const result = spawn(
51
+ 'npx',
52
+ ['eslint', '--max-warnings', '0', ...targets],
53
+ { cwd: projectRoot, stdio: 'inherit', shell: IS_WINDOWS },
54
+ );
55
+ return statusSummary(
56
+ result,
57
+ lintAll ? 'whole tree clean' : `${targets.length} changed file(s) clean`,
58
+ 'eslint reported errors/warnings',
59
+ );
60
+ }
61
+
62
+ /**
63
+ * Gate 2 — deterministic structural asserts that CI enforces:
64
+ * - skills mirror drift (test/structural/skills-sync-drift.test.js)
65
+ * - SUBCOMMANDS<->ISSUE_COMMANDS registry sync (test/commands/_resolve-command-opts.test.js)
66
+ * - committed agentic-workflow mirror (scripts/sync-agentic-workflow.js --check)
67
+ */
68
+ function runStructural({ projectRoot, spawn = spawnSync, fs = nodeFs } = {}) {
69
+ const exists = (rel) => {
70
+ try {
71
+ return fs.existsSync(path.join(projectRoot, rel));
72
+ } catch {
73
+ return false;
74
+ }
75
+ };
76
+
77
+ // These are Forge's OWN internal structural asserts. A consumer repo does not
78
+ // contain them and can NEVER satisfy them, so only run the ones that actually
79
+ // exist in this project. If none do, the gate is not applicable here — report
80
+ // an explicit SKIP rather than a false pass or an impossible failure.
81
+ const structuralTests = [
82
+ 'test/structural/skills-sync-drift.test.js',
83
+ 'test/commands/_resolve-command-opts.test.js',
84
+ ].filter(exists);
85
+ const agenticScript = 'scripts/sync-agentic-workflow.js';
86
+ const hasAgentic = exists(agenticScript);
87
+
88
+ if (structuralTests.length === 0 && !hasAgentic) {
89
+ return {
90
+ ok: true,
91
+ skipped: true,
92
+ summary: 'no Forge-internal structural checks in this repo (consumer context)',
93
+ };
94
+ }
95
+
96
+ if (structuralTests.length > 0) {
97
+ const bun = spawn(
98
+ 'bun',
99
+ ['test', '--timeout', '15000', ...structuralTests],
100
+ { cwd: projectRoot, stdio: 'inherit', shell: IS_WINDOWS },
101
+ );
102
+ if (!bun || bun.status !== 0) {
103
+ return { ok: false, summary: 'skills-mirror / registry-sync asserts failed' };
104
+ }
105
+ }
106
+
107
+ if (!hasAgentic) {
108
+ return { ok: true, summary: 'skills + registry in sync (no agentic-workflow mirror here)' };
109
+ }
110
+
111
+ const agentic = spawn(
112
+ 'node',
113
+ [agenticScript, '--check'],
114
+ { cwd: projectRoot, stdio: 'inherit', shell: IS_WINDOWS },
115
+ );
116
+ return statusSummary(
117
+ agentic,
118
+ 'skills + registry + agentic-workflow in sync',
119
+ 'agentic-workflow mirror drift (run: node scripts/sync-agentic-workflow.js)',
120
+ );
121
+ }
122
+
123
+ /**
124
+ * Gate 3 — SonarCloud parity via eslint-plugin-sonarjs, using an isolated flat
125
+ * config (cognitive-complexity pinned to 15 + the parity rules) so it never
126
+ * merges with the repo's main eslint config.
127
+ * `files === null` means "scan everything" (whole-tree `--all` mode) — mirrors
128
+ * runEslint so `--all` never leaves sonar as a vacuous "no changed files" pass.
129
+ */
130
+ function runSonar(files, { projectRoot, spawn = spawnSync } = {}) {
131
+ const scanAll = files === null;
132
+ const targets = scanAll ? ['.'] : lintableFiles(files);
133
+ if (targets.length === 0) {
134
+ return { ok: true, summary: 'no changed JS files' };
135
+ }
136
+ const config = path.join(projectRoot, 'scripts', 'preflight-sonar.eslint.config.mjs');
137
+ const result = spawn(
138
+ 'npx',
139
+ ['eslint', '--no-config-lookup', '--config', config, '--max-warnings', '0', ...targets],
140
+ { cwd: projectRoot, stdio: 'inherit', shell: IS_WINDOWS },
141
+ );
142
+ return statusSummary(
143
+ result,
144
+ scanAll ? 'sonarjs clean (whole tree)' : `sonarjs clean (${targets.length} file(s))`,
145
+ 'sonarjs parity violations (e.g. cognitive-complexity > 15)',
146
+ );
147
+ }
148
+
149
+ /**
150
+ * Gate 4 — run ONLY the tests mapped from the changed files (reuses the
151
+ * pre-push affected-test mapping). No affected tests → fast-lane pass.
152
+ */
153
+ function runAffectedTests({
154
+ projectRoot,
155
+ changedFiles: _changedFiles,
156
+ spawn = spawnSync,
157
+ resolveTests,
158
+ } = {}) {
159
+ const resolver = typeof resolveTests === 'function'
160
+ ? resolveTests
161
+ // strict:true so a failed `git diff` THROWS here instead of returning [] —
162
+ // otherwise a git error would masquerade as "no affected tests" (fast-lane
163
+ // green), a fail-OPEN. The catch below turns that throw into a closed gate.
164
+ : () => getAffectedTestFiles(projectRoot, execFileSync, nodeFs, { strict: true });
165
+ let targets;
166
+ try {
167
+ targets = resolver() || [];
168
+ } catch (err) {
169
+ // A resolver ERROR must NOT masquerade as "no affected tests" (green).
170
+ // We could not determine what to run, so fail closed — never a vacuous pass.
171
+ const reason = err && err.message ? err.message : String(err);
172
+ return { ok: false, summary: `affected-test resolution failed — fail-closed (${reason})` };
173
+ }
174
+ if (targets.length === 0) {
175
+ return { ok: true, summary: 'no affected tests resolved (fast lane)' };
176
+ }
177
+ const result = spawn(
178
+ 'bun',
179
+ ['test', '--timeout', '15000', ...targets],
180
+ { cwd: projectRoot, stdio: 'inherit', shell: IS_WINDOWS },
181
+ );
182
+ return statusSummary(
183
+ result,
184
+ `${targets.length} affected test file(s) passed`,
185
+ 'affected tests failed',
186
+ );
187
+ }
188
+
189
+ /**
190
+ * Compose the ordered gate list. Each gate delegates to a sub-runner that can
191
+ * be injected via `deps` for testing; defaults wire the real runners above.
192
+ *
193
+ * @param {Object} args
194
+ * @param {string} args.projectRoot
195
+ * @param {string[]} args.changedFiles
196
+ * @param {boolean} [args.runAll] - lint/scan whole tree instead of changed files
197
+ * @param {Object} [args.deps] - { eslint, structural, sonar, affected }
198
+ * @returns {{ name: string, run: () => Promise<{ok:boolean, summary?:string}> }[]}
199
+ */
200
+ function buildGates({ projectRoot, changedFiles = [], runAll = false, deps = {} }) {
201
+ const eslint = deps.eslint || ((files) => runEslint(files, { projectRoot }));
202
+ const structural = deps.structural || (() => runStructural({ projectRoot }));
203
+ const sonar = deps.sonar || ((files) => runSonar(files, { projectRoot }));
204
+ const affected = deps.affected || (() => runAffectedTests({ projectRoot, changedFiles }));
205
+
206
+ // Under --all, scope BOTH lint and sonar to the whole tree (null). Otherwise
207
+ // sonar would receive changedFiles=[] and report a vacuous "no changed files"
208
+ // pass while lint scanned everything — a fail-open hole on the remedy path.
209
+ const scanTargets = runAll ? null : changedFiles;
210
+
211
+ // Affected-tests maps from the change set, which is empty under --all. Running
212
+ // the WHOLE suite here defeats preflight's fast purpose (and hangs on Windows),
213
+ // so mark it explicitly not-run — never a vacuous green. CI runs the full suite.
214
+ const affectedGate = runAll
215
+ ? async () => ({
216
+ ok: true,
217
+ skipped: true,
218
+ summary: 'whole-tree mode (--all): affected-test mapping N/A — run the full suite (CI does)',
219
+ })
220
+ : async () => affected();
221
+
222
+ return [
223
+ { name: 'lint', run: async () => eslint(scanTargets) },
224
+ { name: 'drift/registry/mirror', run: async () => structural() },
225
+ { name: 'sonar (cognitive-complexity=15)', run: async () => sonar(scanTargets) },
226
+ { name: 'affected-tests', run: affectedGate },
227
+ ];
228
+ }
229
+
230
+ module.exports = {
231
+ buildGates,
232
+ lintableFiles,
233
+ runEslint,
234
+ runStructural,
235
+ runSonar,
236
+ runAffectedTests,
237
+ };
@@ -0,0 +1,116 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Fast-fail gate runner for `forge preflight`.
5
+ *
6
+ * Executes an ordered list of gates one at a time. The first gate to fail
7
+ * short-circuits the run: every later gate is reported as `skipped` and never
8
+ * executed. This keeps preflight FAST — an agent fixes the first broken gate
9
+ * and re-runs rather than waiting for the whole matrix.
10
+ *
11
+ * A gate is `{ name: string, run: () => Promise<{ ok: boolean, summary?: string }> }`.
12
+ * The sub-runner behind each `run` is injected by the composer (see gates.js),
13
+ * so this module stays pure and trivially testable.
14
+ *
15
+ * @module preflight/runner
16
+ */
17
+
18
+ /**
19
+ * @typedef {Object} GateOutcome
20
+ * @property {boolean} ok - Whether the gate passed.
21
+ * @property {string} [summary] - Short human-readable result line.
22
+ *
23
+ * @typedef {Object} Gate
24
+ * @property {string} name
25
+ * @property {() => Promise<GateOutcome>} run
26
+ *
27
+ * @typedef {Object} GateResult
28
+ * @property {string} name
29
+ * @property {boolean|null} ok - true/false, or null when skipped.
30
+ * @property {boolean} skipped
31
+ * @property {string} summary
32
+ * @property {number} [durationMs]
33
+ */
34
+
35
+ /**
36
+ * Execute a single gate, normalizing thrown errors into a failed outcome.
37
+ *
38
+ * @param {Gate} gate
39
+ * @returns {Promise<GateResult>}
40
+ */
41
+ async function executeGate(gate) {
42
+ const started = Date.now();
43
+ let outcome;
44
+ try {
45
+ outcome = await gate.run();
46
+ } catch (err) {
47
+ const message = err && err.message ? err.message : String(err);
48
+ outcome = { ok: false, summary: `error: ${message}` };
49
+ }
50
+ const durationMs = Date.now() - started;
51
+ const skipped = !!outcome?.skipped;
52
+ // A skipped gate is a pass-through: it is neither a pass nor a failure, so it
53
+ // must never short-circuit later gates (see runGates fast-fail). BUT a skip
54
+ // must never MASK an explicit failure — a gate that reports {ok:false} has
55
+ // failed regardless of a skipped flag, so it stays failed and short-circuits.
56
+ const explicitOk = !!outcome?.ok;
57
+ const ok = skipped ? outcome?.ok !== false : explicitOk;
58
+ // Contract: an explicit ok:false can never normalize to a pass. Guards against
59
+ // a future edit reintroducing the skip-masks-failure bug (4b73b6bf).
60
+ if (outcome && outcome.ok === false && ok === true) {
61
+ throw new Error('executeGate contract violation: skipped must not override an explicit ok:false');
62
+ }
63
+ return {
64
+ name: gate.name,
65
+ ok,
66
+ skipped,
67
+ summary: outcome?.summary || '',
68
+ durationMs,
69
+ };
70
+ }
71
+
72
+ /** Live-log label for a gate result — SKIP must never read as PASS. */
73
+ function gateLogMark(result) {
74
+ if (result.skipped) return 'SKIP';
75
+ return result.ok ? 'PASS' : 'FAIL';
76
+ }
77
+
78
+ /**
79
+ * Run gates in order, fast-failing at the first failure.
80
+ *
81
+ * @param {Gate[]} gates
82
+ * @param {{ log?: (line: string) => void }} [options]
83
+ * @returns {Promise<{ ok: boolean, results: GateResult[], failedIndex: number }>}
84
+ */
85
+ async function runGates(gates, options = {}) {
86
+ const log = typeof options.log === 'function' ? options.log : () => {};
87
+ const results = [];
88
+ let ok = true;
89
+ let failedIndex = -1;
90
+
91
+ for (const gate of gates) {
92
+ if (!ok) {
93
+ results.push({
94
+ name: gate.name,
95
+ ok: null,
96
+ skipped: true,
97
+ summary: 'skipped (earlier gate failed)',
98
+ });
99
+ continue;
100
+ }
101
+
102
+ const result = await executeGate(gate);
103
+ results.push(result);
104
+ const suffix = result.summary ? ` — ${result.summary}` : '';
105
+ log(`${gateLogMark(result)} ${gate.name}${suffix}`);
106
+
107
+ if (!result.ok) {
108
+ ok = false;
109
+ failedIndex = results.length - 1;
110
+ }
111
+ }
112
+
113
+ return { ok, results, failedIndex };
114
+ }
115
+
116
+ module.exports = { runGates };
@@ -263,16 +263,6 @@ async function detectInstalledAgents(projectPath) {
263
263
  }
264
264
  ]
265
265
  },
266
- {
267
- name: 'copilot',
268
- checks: [
269
- // .github/copilot-instructions.md
270
- async () => {
271
- const copilotFile = path.join(projectPath, '.github', 'copilot-instructions.md');
272
- return fs.existsSync(copilotFile);
273
- }
274
- ]
275
- },
276
266
  {
277
267
  name: 'cursor',
278
268
  checks: [
@@ -283,39 +273,6 @@ async function detectInstalledAgents(projectPath) {
283
273
  }
284
274
  ]
285
275
  },
286
- {
287
- name: 'cline',
288
- checks: [
289
- async () => fs.existsSync(path.join(projectPath, '.clinerules')),
290
- async () => {
291
- const clineDir = path.join(projectPath, '.cline');
292
- return fs.existsSync(clineDir) && (await fs.promises.stat(clineDir)).isDirectory();
293
- }
294
- ]
295
- },
296
- {
297
- name: 'kilocode',
298
- checks: [
299
- async () => {
300
- const kilocodeDir = path.join(projectPath, '.kilocode');
301
- return fs.existsSync(kilocodeDir) && (await fs.promises.stat(kilocodeDir)).isDirectory();
302
- },
303
- async () => fs.existsSync(path.join(projectPath, '.kilocode', 'workflows', 'forge-workflow.md')),
304
- async () => fs.existsSync(path.join(projectPath, '.kilocode', 'rules', 'workflow.md')),
305
- async () => fs.existsSync(path.join(projectPath, '.kilocode', 'skills', 'forge-workflow', 'SKILL.md'))
306
- ]
307
- },
308
- {
309
- name: 'roo',
310
- checks: [
311
- async () => fs.existsSync(path.join(projectPath, '.roorules')),
312
- async () => {
313
- const rooDir = path.join(projectPath, '.roo');
314
- return fs.existsSync(rooDir) && (await fs.promises.stat(rooDir)).isDirectory();
315
- },
316
- async () => fs.existsSync(path.join(projectPath, '.roo', 'rules'))
317
- ]
318
- },
319
276
  {
320
277
  name: 'codex',
321
278
  checks: [
@@ -325,16 +282,6 @@ async function detectInstalledAgents(projectPath) {
325
282
  return fs.existsSync(codexDir) && (await fs.promises.stat(codexDir)).isDirectory();
326
283
  }
327
284
  ]
328
- },
329
- {
330
- name: 'opencode',
331
- checks: [
332
- // opencode.json file
333
- async () => {
334
- const opencodeJson = path.join(projectPath, 'opencode.json');
335
- return fs.existsSync(opencodeJson);
336
- }
337
- ]
338
285
  }
339
286
  ];
340
287
 
@@ -0,0 +1,166 @@
1
+ 'use strict';
2
+
3
+ const { resolveKernelDatabasePath } = require('./kernel/cli-broker-factory');
4
+ const { createBuiltinSQLiteDriver } = require('./kernel/sqlite-driver');
5
+
6
+ // Project memory is a Forge read model persisted in the kernel store (kernel_memories),
7
+ // written DIRECTLY rather than through the issue CAS/guarded-event path. The store seam
8
+ // (options.store) is a driver-like object — { recordMemory, loadMemory, searchMemories,
9
+ // listMemories } — so hermetic tests stay in-memory. The default seam resolves the
10
+ // per-repo kernel database path and reuses one driver per database path (the CLI process
11
+ // is short-lived, so the connection closing on exit is sufficient cleanup).
12
+
13
+ const storeCache = new Map();
14
+
15
+ function defaultStore(projectRoot, options = {}) {
16
+ const databasePath = resolveKernelDatabasePath({
17
+ projectRoot,
18
+ gitCommonDir: options.gitCommonDir,
19
+ databasePath: options.databasePath,
20
+ });
21
+ let store = storeCache.get(databasePath);
22
+ if (!store) {
23
+ store = createBuiltinSQLiteDriver({ databasePath });
24
+ storeCache.set(databasePath, store);
25
+ }
26
+ return store;
27
+ }
28
+
29
+ function resolveStore(projectRoot, options = {}) {
30
+ return options.store ?? defaultStore(projectRoot, options);
31
+ }
32
+
33
+ function assertEntryObject(entry) {
34
+ if (!entry || typeof entry !== 'object' || Array.isArray(entry)) {
35
+ throw new TypeError('project memory entry must be an object');
36
+ }
37
+ }
38
+
39
+ function assertRequiredString(value, fieldName) {
40
+ if (typeof value !== 'string' || value.trim() === '') {
41
+ throw new TypeError(`project memory entry ${fieldName} is required`);
42
+ }
43
+ }
44
+
45
+ function assertStringArray(value, fieldName) {
46
+ if (!Array.isArray(value) || value.some(item => typeof item !== 'string')) {
47
+ throw new TypeError(`project memory entry ${fieldName} must be an array of strings`);
48
+ }
49
+ }
50
+
51
+ function assertOptionalConfidence(value) {
52
+ if (value !== undefined && (!Number.isFinite(value) || value < 0 || value > 1)) {
53
+ throw new TypeError('project memory entry confidence must be a number from 0 to 1');
54
+ }
55
+ }
56
+
57
+ function validateEntry(entry) {
58
+ assertEntryObject(entry);
59
+ assertRequiredString(entry.key, 'key');
60
+ if (!Object.hasOwn(entry, 'value') || entry.value === undefined) {
61
+ throw new TypeError('project memory entry value is required');
62
+ }
63
+ assertRequiredString(entry.sourceAgent || entry['source-agent'], 'sourceAgent');
64
+ if (entry.timestamp !== undefined
65
+ && (typeof entry.timestamp !== 'string' || Number.isNaN(Date.parse(entry.timestamp)))) {
66
+ throw new TypeError('project memory entry timestamp must be an ISO timestamp string');
67
+ }
68
+ if (entry.scope !== undefined && (typeof entry.scope !== 'string' || entry.scope.trim() === '')) {
69
+ throw new TypeError('project memory entry scope must be a non-empty string');
70
+ }
71
+ assertOptionalConfidence(entry.confidence);
72
+ if (entry.tags !== undefined) assertStringArray(entry.tags, 'tags');
73
+ if (entry.supersedes !== undefined) assertStringArray(entry.supersedes, 'supersedes');
74
+ if (entry.beadsRefs !== undefined) assertStringArray(entry.beadsRefs, 'beadsRefs');
75
+ if (entry['beads-refs'] !== undefined) assertStringArray(entry['beads-refs'], 'beads-refs');
76
+ }
77
+
78
+ // Canonicalize an entry for storage: resolve the snake-case input aliases, default the
79
+ // timestamp and tags, and carry the optional fields only when present (so the persisted
80
+ // shape matches the legacy entry exactly).
81
+ function normalizeEntry(key, entry) {
82
+ const normalized = {
83
+ key,
84
+ value: entry.value,
85
+ sourceAgent: entry.sourceAgent || entry['source-agent'],
86
+ tags: Array.isArray(entry.tags) ? [...entry.tags] : [],
87
+ timestamp: entry.timestamp ?? new Date().toISOString(),
88
+ };
89
+
90
+ if (entry.scope !== undefined) normalized.scope = entry.scope;
91
+ if (entry.confidence !== undefined) normalized.confidence = entry.confidence;
92
+ if (entry.supersedes !== undefined) normalized.supersedes = [...entry.supersedes];
93
+ if (entry.beadsRefs !== undefined || entry['beads-refs'] !== undefined) {
94
+ normalized.beadsRefs = [...(entry.beadsRefs || entry['beads-refs'])];
95
+ }
96
+
97
+ return normalized;
98
+ }
99
+
100
+ function write(projectRoot, entry, options = {}) {
101
+ validateEntry(entry);
102
+ const normalized = normalizeEntry(entry.key.trim(), entry);
103
+ resolveStore(projectRoot, options).recordMemory(normalized);
104
+ return normalized;
105
+ }
106
+
107
+ function read(projectRoot, key, options = {}) {
108
+ assertRequiredString(key, 'read key');
109
+ return resolveStore(projectRoot, options).loadMemory(key.trim());
110
+ }
111
+
112
+ function search(projectRoot, query, options = {}) {
113
+ if (typeof query !== 'string' || query.trim() === '') {
114
+ return [];
115
+ }
116
+ return resolveStore(projectRoot, options).searchMemories(query.trim());
117
+ }
118
+
119
+ function list(projectRoot, options = {}) {
120
+ return resolveStore(projectRoot, options).listMemories();
121
+ }
122
+
123
+ // The newest `limit` entries (default recall with no query). Delegates to the FTS-backed
124
+ // driver so recall never loads and re-sorts the whole table. `options.agents` (a
125
+ // source_agent allow-list) scopes the read, e.g. to human `remember` notes only.
126
+ function recent(projectRoot, limit, options = {}) {
127
+ return resolveStore(projectRoot, options).recentMemories(limit, { agents: options.agents });
128
+ }
129
+
130
+ // Total stored memories (optionally scoped by `options.agents`) — paired with `recent` so
131
+ // recall can report "showing N of TOTAL".
132
+ function count(projectRoot, options = {}) {
133
+ return resolveStore(projectRoot, options).countMemories({ agents: options.agents });
134
+ }
135
+
136
+ // BM25 top-N recall over the FTS5 index (token-AND). Unlike `search` (the legacy LIKE
137
+ // helper) this does not short-circuit an empty query — the driver falls back to recent so
138
+ // recall stays capped either way.
139
+ function searchRanked(projectRoot, query, limit, options = {}) {
140
+ return resolveStore(projectRoot, options).searchMemoriesRanked(query, limit);
141
+ }
142
+
143
+ // Close and forget every cached default store. The CLI process is short-lived (the OS
144
+ // closes the handle on exit), so this is mainly a lifecycle helper for long-lived hosts and
145
+ // tests — it releases the SQLite/WAL handle before a temp dir is removed.
146
+ function closeAll() {
147
+ for (const store of storeCache.values()) {
148
+ try {
149
+ if (store && typeof store.close === 'function') store.close();
150
+ } catch {
151
+ // best-effort close
152
+ }
153
+ }
154
+ storeCache.clear();
155
+ }
156
+
157
+ module.exports = {
158
+ read,
159
+ write,
160
+ search,
161
+ list,
162
+ recent,
163
+ count,
164
+ searchRanked,
165
+ closeAll,
166
+ };