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,1042 @@
1
+ 'use strict';
2
+
3
+ const fs = require('node:fs');
4
+ const path = require('node:path');
5
+
6
+ const BEADS_EXPORT_FILES = ['issues.jsonl', 'comments.jsonl', 'dependencies.jsonl'];
7
+ const FORGE_PROJECTION_METADATA_KEY = 'forge_projection';
8
+ // Issue fields with no dedicated Kernel column, dropped only after being surfaced as a gap.
9
+ // labels/acceptance_criteria (D18 TEXT columns), created_by/metadata (migration 006),
10
+ // assignee/design (migration 004), and owner (carried onto assignee/created_by/metadata) all
11
+ // moved OUT of this set: they are mapped, not dropped. The list stays as the extension point
12
+ // for any future beads field the Kernel cannot yet represent.
13
+ const UNSUPPORTED_ISSUE_FIELDS = Object.freeze([]);
14
+ const PRESERVED_FIELDS = Object.freeze([
15
+ 'issues.id',
16
+ 'issues.title',
17
+ 'issues.body',
18
+ 'issues.notes',
19
+ 'issues.type',
20
+ 'issues.status',
21
+ 'issues.priority',
22
+ 'issues.labels',
23
+ 'issues.acceptance_criteria',
24
+ 'issues.created_at',
25
+ 'issues.updated_at',
26
+ 'issues.design',
27
+ 'issues.assignee',
28
+ 'issues.owner',
29
+ 'issues.created_by',
30
+ 'issues.metadata',
31
+ 'dependencies.parent-child',
32
+ 'dependencies.blocks',
33
+ 'comments.body',
34
+ 'comments.actor',
35
+ 'comments.created_at',
36
+ 'events.close_reason',
37
+ 'events.closed_at',
38
+ // Legacy activity log: events.jsonl lifecycle events and interactions.jsonl agent
39
+ // interaction/memory records both land in kernel_events (origin=beads_import).
40
+ 'events.activity',
41
+ 'interactions.activity',
42
+ ]);
43
+
44
+ // D18 taxonomy collapse (see docs/reference/KERNEL_TAXONOMY_VALIDATION.md). Legacy Beads
45
+ // issue_types that are no longer Kernel types become labels on a `task`; the canonical
46
+ // types are epic/task/bug/decision, enforced by lib/kernel/taxonomy-validator. Normalizing
47
+ // here — on the adapter boundary — keeps legacy imports from being rejected by the D18
48
+ // validation layer downstream.
49
+ const LEGACY_TYPE_ALIASES = Object.freeze({
50
+ feature: 'task',
51
+ story: 'task',
52
+ chore: 'task',
53
+ spike: 'task',
54
+ });
55
+ // Legacy Beads `closed` collapses to the terminal `done`, unless the close reason signals an
56
+ // abandonment, in which case it maps to `cancelled`. Both are terminal Kernel statuses.
57
+ const CANCELLED_CLOSE_REASON = /\b(cancel|won'?t.?fix|wontfix|abandon|obsolet|duplicat|invalid|not.?planned|supersed|declin)/i;
58
+
59
+ function parseJsonl(content = '', file = 'jsonl') {
60
+ return String(content)
61
+ .split(/\r?\n/)
62
+ .map(line => line.trim())
63
+ .filter(Boolean)
64
+ .map((line, index) => {
65
+ try {
66
+ return JSON.parse(line);
67
+ } catch (error) {
68
+ throw new Error(`Invalid JSONL in ${file} at line ${index + 1}: ${error.message}`);
69
+ }
70
+ });
71
+ }
72
+
73
+ function stringifyJsonl(records = []) {
74
+ if (!Array.isArray(records) || records.length === 0) {
75
+ return '';
76
+ }
77
+
78
+ return `${records.map(record => JSON.stringify(record)).join('\n')}\n`;
79
+ }
80
+
81
+ // Read one beads sidecar file, tolerant of the SPLIT layout a real `.beads/` store uses: the
82
+ // backup export (issues/comments/dependencies/labels/events) lives under `.beads/backup/`, while
83
+ // the live interactions/memory log lives at `.beads/interactions.jsonl`. So neither `.beads/` nor
84
+ // `.beads/backup/` alone holds every file. We read from `beadsDir` first (it always wins), then
85
+ // fall back to a `backup/` subdir and the parent dir — so pointing `--source` at EITHER location
86
+ // picks up both events and interactions instead of silently half-reading (a missed memory sidecar
87
+ // is exactly the data-loss class this migrater exists to close).
88
+ function readBeadsSidecar(beadsDir, file) {
89
+ const candidates = [
90
+ path.join(beadsDir, file),
91
+ path.join(beadsDir, 'backup', file),
92
+ path.join(path.dirname(beadsDir), file),
93
+ ];
94
+ const found = candidates.find(candidate => fs.existsSync(candidate));
95
+ return found ? parseJsonl(fs.readFileSync(found, 'utf8'), path.basename(found)) : [];
96
+ }
97
+
98
+ function loadBeadsSnapshotFromDirectory(beadsDir) {
99
+ if (!beadsDir || typeof beadsDir !== 'string') {
100
+ throw new TypeError('beadsDir must be a directory path');
101
+ }
102
+
103
+ return {
104
+ beadsDir,
105
+ issues: readBeadsSidecar(beadsDir, 'issues.jsonl'),
106
+ comments: readBeadsSidecar(beadsDir, 'comments.jsonl'),
107
+ dependencies: readBeadsSidecar(beadsDir, 'dependencies.jsonl'),
108
+ events: readBeadsSidecar(beadsDir, 'events.jsonl'),
109
+ interactions: readBeadsSidecar(beadsDir, 'interactions.jsonl'),
110
+ labels: readBeadsSidecar(beadsDir, 'labels.jsonl'),
111
+ };
112
+ }
113
+
114
+ function normalizeStatus(status) {
115
+ const normalized = String(status || 'open').trim().toLowerCase().replace(/[-\s]+/g, '_');
116
+ return normalized || 'open';
117
+ }
118
+
119
+ // Map a Beads issue_type to a canonical Kernel type, returning the alias label to preserve
120
+ // when a legacy type (feature/story/chore/spike) collapses to `task`.
121
+ function normalizeKernelType(beadsType) {
122
+ const raw = String(beadsType || '').trim().toLowerCase();
123
+ if (Object.hasOwn(LEGACY_TYPE_ALIASES, raw)) {
124
+ return { type: LEGACY_TYPE_ALIASES[raw], aliasLabel: raw };
125
+ }
126
+ return { type: raw || 'task', aliasLabel: null };
127
+ }
128
+
129
+ // Collapse the legacy Beads `closed` status onto a terminal Kernel status before it reaches
130
+ // the D18 validation layer. Non-`closed` statuses pass through their normalized form.
131
+ function normalizeKernelStatus(issue) {
132
+ const status = normalizeStatus(issue.status);
133
+ if (status !== 'closed') {
134
+ return status;
135
+ }
136
+ return CANCELLED_CLOSE_REASON.test(String(issue.close_reason || '')) ? 'cancelled' : 'done';
137
+ }
138
+
139
+ // Project terminal Kernel statuses back onto the Beads `closed` vocabulary on export so a
140
+ // Beads -> Kernel -> Beads round-trip stays identity (Beads has no done/cancelled status).
141
+ function beadsStatusForKernelStatus(status) {
142
+ return status === 'done' || status === 'cancelled' ? 'closed' : status;
143
+ }
144
+
145
+ // Serialize a Beads labels array into the Kernel `labels` TEXT column as a JSON array string,
146
+ // deduping and dropping blanks. Returns null when there is nothing to store.
147
+ function serializeLabels(labels) {
148
+ if (!Array.isArray(labels) || labels.length === 0) {
149
+ return null;
150
+ }
151
+ const unique = Array.from(new Set(
152
+ labels.filter(label => label !== null && label !== undefined && String(label).trim() !== ''),
153
+ ));
154
+ return unique.length > 0 ? JSON.stringify(unique) : null;
155
+ }
156
+
157
+ // Inverse of serializeLabels for the export path. Tolerates already-array values, a null/empty
158
+ // column, and malformed JSON (never throws — JSON.parse(null) would).
159
+ function deserializeLabels(value) {
160
+ if (Array.isArray(value)) {
161
+ return value;
162
+ }
163
+ if (typeof value !== 'string' || value.trim() === '') {
164
+ return [];
165
+ }
166
+ try {
167
+ const parsed = JSON.parse(value);
168
+ return Array.isArray(parsed) ? parsed : [];
169
+ } catch (_error) { // NOSONAR S2486 — malformed labels column is non-fatal; fall back to no labels
170
+ return [];
171
+ }
172
+ }
173
+
174
+ // Serialize Beads acceptance_criteria into the Kernel TEXT column: strings are stored as-is,
175
+ // structured values are JSON-encoded. Returns null when absent.
176
+ function serializeAcceptanceCriteria(value) {
177
+ if (value === null || value === undefined || value === '') {
178
+ return null;
179
+ }
180
+ return typeof value === 'string' ? value : JSON.stringify(value);
181
+ }
182
+
183
+ // Inverse of serializeAcceptanceCriteria for the export path: a JSON-encoded array/object is
184
+ // parsed back to its structured shape so a round-trip restores the original value; plain prose
185
+ // strings (which never start with [ or {) pass through unchanged.
186
+ function deserializeAcceptanceCriteria(value) {
187
+ if (typeof value !== 'string') {
188
+ return value;
189
+ }
190
+ const trimmed = value.trim();
191
+ if (trimmed.startsWith('[') || trimmed.startsWith('{')) {
192
+ try {
193
+ return JSON.parse(value);
194
+ } catch (_error) { // NOSONAR S2486 — bracket-prefixed but invalid JSON; treat as a plain string
195
+ return value;
196
+ }
197
+ }
198
+ return value;
199
+ }
200
+
201
+ function normalizePriority(priority) {
202
+ if (typeof priority === 'number' && Number.isFinite(priority)) {
203
+ return {
204
+ label: `P${priority}`,
205
+ rank: priority,
206
+ beads: priority,
207
+ };
208
+ }
209
+
210
+ const raw = String(priority ?? 'P2').trim().toUpperCase();
211
+ const match = raw.match(/^P?(\d+)$/);
212
+ if (!match) {
213
+ return {
214
+ label: raw || 'P2',
215
+ rank: 2,
216
+ beads: 2,
217
+ };
218
+ }
219
+
220
+ const rank = Number.parseInt(match[1], 10);
221
+ return {
222
+ label: `P${rank}`,
223
+ rank,
224
+ beads: rank,
225
+ };
226
+ }
227
+
228
+ function safeIdPart(value) {
229
+ const input = String(value || 'unknown').toLowerCase();
230
+ const parts = [];
231
+ let pendingSeparator = false;
232
+
233
+ for (const char of input) {
234
+ const code = char.charCodeAt(0);
235
+ const isAsciiLetter = code >= 97 && code <= 122;
236
+ const isDigit = code >= 48 && code <= 57;
237
+ if (isAsciiLetter || isDigit) {
238
+ if (pendingSeparator && parts.length > 0) {
239
+ parts.push('-');
240
+ }
241
+ parts.push(char);
242
+ pendingSeparator = false;
243
+ } else {
244
+ pendingSeparator = parts.length > 0;
245
+ }
246
+ }
247
+
248
+ return parts.join('') || 'unknown';
249
+ }
250
+
251
+ function encodedIdPart(value) {
252
+ return Buffer.from(String(value ?? ''), 'utf8').toString('hex') || '00';
253
+ }
254
+
255
+ function uniqueBy(records, keyFn) {
256
+ const seen = new Set();
257
+ const result = [];
258
+ for (const record of records) {
259
+ const key = keyFn(record);
260
+ if (!seen.has(key)) {
261
+ seen.add(key);
262
+ result.push(record);
263
+ }
264
+ }
265
+ return result;
266
+ }
267
+
268
+ function collectDependencies(snapshot) {
269
+ const dependencies = [...(snapshot.dependencies || [])];
270
+ for (const issue of snapshot.issues || []) {
271
+ if (Array.isArray(issue.dependencies)) {
272
+ dependencies.push(...issue.dependencies);
273
+ }
274
+ }
275
+
276
+ return uniqueBy(
277
+ dependencies.filter(dependency => dependency?.issue_id && dependency?.depends_on_id),
278
+ dependency => `${dependency.issue_id}\0${dependency.depends_on_id}\0${dependency.type || 'blocks'}`,
279
+ );
280
+ }
281
+
282
+ function getLabelsByIssue(snapshot) {
283
+ const byIssue = new Map();
284
+
285
+ for (const labelRecord of snapshot.labels || []) {
286
+ const issueId = labelRecord.issue_id || labelRecord.issueId;
287
+ const label = labelRecord.label || labelRecord.name;
288
+ if (!issueId || !label) continue;
289
+ if (!byIssue.has(issueId)) {
290
+ byIssue.set(issueId, []);
291
+ }
292
+ byIssue.get(issueId).push(label);
293
+ }
294
+
295
+ return byIssue;
296
+ }
297
+
298
+ function applyLabelSidecar(issues, snapshot) {
299
+ const labelsByIssue = getLabelsByIssue(snapshot);
300
+ if (labelsByIssue.size === 0) {
301
+ return issues;
302
+ }
303
+
304
+ return issues.map(issue => {
305
+ const sidecarLabels = labelsByIssue.get(issue.id) || [];
306
+ if (sidecarLabels.length === 0) {
307
+ return issue;
308
+ }
309
+ const labels = Array.from(new Set([...(Array.isArray(issue.labels) ? issue.labels : []), ...sidecarLabels]));
310
+ return {
311
+ ...issue,
312
+ labels,
313
+ };
314
+ });
315
+ }
316
+
317
+ function collectComments(snapshot) {
318
+ const comments = [...(snapshot.comments || [])];
319
+ for (const issue of snapshot.issues || []) {
320
+ if (Array.isArray(issue.comments)) {
321
+ for (const comment of issue.comments) {
322
+ comments.push({
323
+ ...comment,
324
+ issue_id: comment.issue_id || issue.id,
325
+ });
326
+ }
327
+ }
328
+ if (issue.notes && issue.notes !== issue.description && issue.notes !== issue.body) {
329
+ comments.push({
330
+ id: `beads-note-${encodedIdPart(issue.id)}`,
331
+ issue_id: issue.id,
332
+ body: issue.notes,
333
+ actor: issue.updated_by || issue.created_by || 'beads',
334
+ created_at: issue.updated_at || issue.created_at,
335
+ });
336
+ }
337
+ }
338
+
339
+ return uniqueBy(
340
+ comments.filter(comment => comment?.issue_id && (comment.body || comment.text)),
341
+ comment => `${comment.id || ''}\0${comment.issue_id}\0${comment.created_at || ''}\0${comment.body || comment.text}`,
342
+ );
343
+ }
344
+
345
+ function addGap(gaps, seen, field, reason) {
346
+ if (seen.has(field)) return;
347
+ seen.add(field);
348
+ gaps.push({ field, reason });
349
+ }
350
+
351
+ function hasBeadsFieldValue(value) {
352
+ if (value === null || value === undefined) return false;
353
+ if (typeof value === 'string') return value.trim() !== '' && value !== '{}';
354
+ if (Array.isArray(value)) return value.length > 0;
355
+ if (typeof value === 'object') return Object.keys(value).length > 0;
356
+ return true;
357
+ }
358
+
359
+ function parseMetadataObject(value) {
360
+ if (value === null || value === undefined || value === '') return {};
361
+ if (typeof value === 'object' && !Array.isArray(value)) return { ...value };
362
+ if (typeof value !== 'string') return {};
363
+ try {
364
+ const parsed = JSON.parse(value);
365
+ return parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : {};
366
+ } catch (_error) {
367
+ return {};
368
+ }
369
+ }
370
+
371
+ // Carry a Beads issue's metadata onto the Kernel `metadata` TEXT column for full-fidelity import.
372
+ // The forge_projection key is an internal Forge -> Beads projection marker (written on export and
373
+ // consumed as close-event provenance via getForgeProjectionOrigin), NOT user data, so it is
374
+ // stripped here; the remaining object keys are preserved verbatim as a JSON string. Non-object
375
+ // metadata is kept as its raw value. Returns null when nothing user-authored remains.
376
+ function serializeBeadsMetadata(value) {
377
+ if (!hasBeadsFieldValue(value)) return null;
378
+ let metadata = null;
379
+ if (typeof value === 'object' && !Array.isArray(value)) {
380
+ metadata = { ...value };
381
+ } else if (typeof value === 'string') {
382
+ try {
383
+ const parsed = JSON.parse(value);
384
+ if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
385
+ metadata = parsed;
386
+ }
387
+ } catch (_error) { // NOSONAR S2486 — non-JSON metadata is preserved verbatim below
388
+ metadata = null;
389
+ }
390
+ }
391
+ if (metadata === null) {
392
+ return typeof value === 'string' ? value : JSON.stringify(value);
393
+ }
394
+ delete metadata[FORGE_PROJECTION_METADATA_KEY];
395
+ return Object.keys(metadata).length > 0 ? JSON.stringify(metadata) : null;
396
+ }
397
+
398
+ // Beads issue fields with no dedicated Kernel column are folded into the metadata blob so nothing
399
+ // is silently dropped: the `owner` (only when not already captured on created_by/assignee — it
400
+ // primarily maps to the Kernel assignee), the `external_ref` (e.g. a linked GitHub issue/run),
401
+ // and the work-start timestamp `started_at`. A raw (non-object) metadata blob that must coexist
402
+ // is nested under metadata.beads_metadata rather than discarded. NOTE: beads-internal/derived
403
+ // fields (content_hash, agent_state, compaction_*, ephemeral, and other runtime plumbing) are
404
+ // intentionally NOT carried — they are not user data and would only pollute the Kernel row.
405
+ function serializeIssueMetadata(issue, resolved = {}) {
406
+ const base = serializeBeadsMetadata(issue.metadata);
407
+ let metadata = {};
408
+ let rawPreserved = null;
409
+ if (base !== null) {
410
+ try {
411
+ const parsed = JSON.parse(base);
412
+ if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
413
+ metadata = parsed;
414
+ } else {
415
+ rawPreserved = base;
416
+ }
417
+ } catch (_error) { // NOSONAR S2486 — non-JSON metadata is preserved via beads_metadata below
418
+ rawPreserved = base;
419
+ }
420
+ }
421
+
422
+ // Compare trimmed on BOTH sides so a padded assignee/created_by does not double-store the owner.
423
+ const trim = value => (typeof value === 'string' ? value.trim() : value);
424
+ const owner = trim(issue.owner);
425
+ const ownerCaptured = owner && (owner === trim(resolved.createdBy) || owner === trim(resolved.assignee));
426
+ if (owner && !ownerCaptured) {
427
+ metadata.beads_owner = issue.owner;
428
+ }
429
+ if (hasBeadsFieldValue(issue.external_ref)) {
430
+ metadata.beads_external_ref = issue.external_ref;
431
+ }
432
+ if (hasBeadsFieldValue(issue.started_at)) {
433
+ metadata.beads_started_at = issue.started_at;
434
+ }
435
+
436
+ if (rawPreserved !== null) {
437
+ if (Object.keys(metadata).length === 0) return rawPreserved;
438
+ metadata.beads_metadata = rawPreserved;
439
+ }
440
+ return Object.keys(metadata).length > 0 ? JSON.stringify(metadata) : null;
441
+ }
442
+
443
+ function hasFiniteRevisionValue(value) {
444
+ if (typeof value === 'number') return Number.isFinite(value);
445
+ if (typeof value !== 'string') return false;
446
+ const trimmed = value.trim();
447
+ return trimmed !== '' && Number.isFinite(Number(trimmed));
448
+ }
449
+
450
+ function isValidForgeProjection(projection, issueId) {
451
+ if (!projection || typeof projection !== 'object' || Array.isArray(projection)) return false;
452
+ return projection.source === 'forge-kernel'
453
+ && projection.target === 'beads'
454
+ && projection.entity_type === 'issue'
455
+ && projection.entity_id === issueId
456
+ && hasFiniteRevisionValue(projection.entity_revision)
457
+ && typeof projection.payload_hash === 'string'
458
+ && projection.payload_hash !== '';
459
+ }
460
+
461
+ function getForgeProjectionOrigin(issue = {}, currentPayload = {}) {
462
+ const metadata = parseMetadataObject(issue.metadata);
463
+ const projection = metadata[FORGE_PROJECTION_METADATA_KEY];
464
+ if (!isValidForgeProjection(projection, issue.id)) return null;
465
+ const importedPayloadHash = JSON.stringify(currentPayload);
466
+ if (projection.payload_hash !== importedPayloadHash) return null;
467
+
468
+ return {
469
+ source: 'forge-kernel',
470
+ target: 'beads',
471
+ entity_type: 'issue',
472
+ entity_id: issue.id,
473
+ entity_revision: normalizeEntityRevision(projection.entity_revision),
474
+ payload_hash: projection.payload_hash,
475
+ imported_payload_hash: importedPayloadHash,
476
+ };
477
+ }
478
+
479
+ function buildCloseProjectionPayload(closeMetadata = {}) {
480
+ return {
481
+ closed_at: closeMetadata.closed_at || null,
482
+ close_reason: closeMetadata.close_reason || null,
483
+ };
484
+ }
485
+
486
+ function buildCloseProjectionFingerprint(issue = {}) {
487
+ return {
488
+ ...buildCloseProjectionPayload(issue),
489
+ actor: issue.closed_by || issue.created_by || 'beads',
490
+ };
491
+ }
492
+
493
+ function buildForgeProjectionMetadata(issue = {}, closeMetadata = {}) {
494
+ const metadata = parseMetadataObject(issue.metadata);
495
+ metadata[FORGE_PROJECTION_METADATA_KEY] = {
496
+ source: 'forge-kernel',
497
+ target: 'beads',
498
+ entity_type: 'issue',
499
+ entity_id: issue.id,
500
+ entity_revision: normalizeEntityRevision(issue.entity_revision),
501
+ payload_hash: JSON.stringify(buildCloseProjectionFingerprint({ ...issue, ...closeMetadata })),
502
+ };
503
+ return metadata;
504
+ }
505
+
506
+ function buildFidelityReport({ kernel, gaps, eventCount = 0, interactionCount = 0 }) {
507
+ return {
508
+ summary: {
509
+ issues: kernel.issues.length,
510
+ dependencies: kernel.dependencies.length,
511
+ comments: kernel.comments.length,
512
+ closeEvents: kernel.events.filter(event => event.event_type === 'beads.issue.closed').length,
513
+ // Legacy activity records that now land in kernel_events instead of being dropped.
514
+ events: eventCount,
515
+ interactions: interactionCount,
516
+ activityEvents: kernel.activityEvents ? kernel.activityEvents.length : 0,
517
+ unsupportedFields: gaps.length,
518
+ },
519
+ preservedFields: [...PRESERVED_FIELDS],
520
+ gaps,
521
+ };
522
+ }
523
+
524
+ function mapBeadsIssueToKernel(issue, importedAt, gaps, seenGaps) {
525
+ const priority = normalizePriority(issue.priority);
526
+ const { type, aliasLabel } = normalizeKernelType(issue.issue_type || issue.type);
527
+ const beadsLabels = Array.isArray(issue.labels) ? issue.labels : [];
528
+ const labels = aliasLabel ? [...beadsLabels, aliasLabel] : beadsLabels;
529
+ for (const [field, reason] of UNSUPPORTED_ISSUE_FIELDS) {
530
+ if (hasBeadsFieldValue(issue[field])) {
531
+ addGap(gaps, seenGaps, `issues.${field}`, reason);
532
+ }
533
+ }
534
+
535
+ // Full-fidelity import onto dedicated Kernel columns: the beads author lands on created_by
536
+ // (falling back to owner when no author is recorded); the responsible owner lands on the
537
+ // assignee column (falling back after an explicit beads assignee); design/notes-adjacent
538
+ // authored content lands on the design column. Any owner NOT captured by created_by/assignee
539
+ // is preserved in the metadata blob (serializeIssueMetadata) so nothing is dropped.
540
+ const createdBy = issue.created_by || issue.owner || null;
541
+ const assignee = issue.assignee || issue.owner || null;
542
+
543
+ return {
544
+ id: issue.id,
545
+ title: issue.title || issue.id,
546
+ body: issue.description ?? issue.body ?? issue.notes ?? '',
547
+ type,
548
+ status: normalizeKernelStatus(issue),
549
+ priority: priority.label,
550
+ priority_rank: priority.rank,
551
+ labels: serializeLabels(labels),
552
+ acceptance_criteria: serializeAcceptanceCriteria(issue.acceptance_criteria),
553
+ design: hasBeadsFieldValue(issue.design) ? issue.design : null,
554
+ assignee,
555
+ created_by: createdBy,
556
+ metadata: serializeIssueMetadata(issue, { createdBy, assignee }),
557
+ created_at: issue.created_at || importedAt,
558
+ updated_at: issue.updated_at || issue.created_at || importedAt,
559
+ entity_revision: 0,
560
+ };
561
+ }
562
+
563
+ function mapBeadsDependencyToKernel(dependency, importedAt, gaps, seenGaps) {
564
+ // An empty "{}" metadata blob carries no data, so it is not a loss — only surface a gap when
565
+ // the dependency actually holds metadata (the Kernel dependency row has no metadata column).
566
+ if (hasBeadsFieldValue(dependency.metadata)) {
567
+ addGap(gaps, seenGaps, 'dependencies.metadata', 'no Kernel dependency metadata column in schema v1');
568
+ }
569
+ // The dependency creator has no dedicated Kernel column; retained as a documented gap rather
570
+ // than silently dropped (resolving it would require an additive kernel_dependencies column).
571
+ if (dependency.created_by) {
572
+ addGap(gaps, seenGaps, 'dependencies.created_by', 'no Kernel dependency creator column in schema v1');
573
+ }
574
+
575
+ return {
576
+ id: [
577
+ 'beads-dependency',
578
+ encodedIdPart(dependency.issue_id),
579
+ encodedIdPart(dependency.depends_on_id),
580
+ encodedIdPart(dependency.type || 'blocks'),
581
+ ].join('-'),
582
+ issue_id: dependency.issue_id,
583
+ blocks_issue_id: dependency.depends_on_id,
584
+ dependency_type: dependency.type || 'blocks',
585
+ created_at: dependency.created_at || importedAt,
586
+ };
587
+ }
588
+
589
+ function mapBeadsCommentToKernel(comment, importedAt) {
590
+ return {
591
+ id: comment.id || [
592
+ 'beads-comment',
593
+ encodedIdPart(comment.issue_id),
594
+ encodedIdPart(comment.created_at || importedAt),
595
+ ].join('-'),
596
+ issue_id: comment.issue_id,
597
+ body: comment.body ?? comment.text ?? '',
598
+ actor: comment.actor || comment.author || 'beads',
599
+ visibility: comment.visibility || 'local',
600
+ created_at: comment.created_at || importedAt,
601
+ };
602
+ }
603
+
604
+ function buildPriorityEvent(issue, importedAt) {
605
+ const priority = normalizePriority(issue.priority);
606
+ return {
607
+ id: `beads-priority-${encodedIdPart(issue.id)}`,
608
+ issue_id: issue.id,
609
+ old_priority: null,
610
+ new_priority: priority.label,
611
+ priority_rank: priority.rank,
612
+ actor: issue.created_by || 'beads',
613
+ created_at: issue.updated_at || issue.created_at || importedAt,
614
+ };
615
+ }
616
+
617
+ function normalizeEntityRevision(value) {
618
+ const revision = Number(value);
619
+ return Number.isFinite(revision) ? revision : 0;
620
+ }
621
+
622
+ function buildCloseEvent(issue, importedAt) {
623
+ if (!issue.closed_at && !issue.close_reason) {
624
+ return null;
625
+ }
626
+
627
+ const createdAt = issue.closed_at || issue.updated_at || importedAt;
628
+ const payload = buildCloseProjectionPayload(issue);
629
+ const projectionOrigin = getForgeProjectionOrigin(issue, buildCloseProjectionFingerprint(issue));
630
+ if (projectionOrigin) {
631
+ payload.projection_origin = projectionOrigin;
632
+ }
633
+
634
+ return {
635
+ id: `beads-close-${encodedIdPart(issue.id)}`,
636
+ entity_type: 'issue',
637
+ entity_id: issue.id,
638
+ event_type: 'beads.issue.closed',
639
+ idempotency_key: `beads-close:${issue.id}:${createdAt}`,
640
+ expected_revision: normalizeEntityRevision(projectionOrigin?.entity_revision ?? issue.entity_revision),
641
+ actor: issue.closed_by || issue.created_by || 'beads',
642
+ origin: 'beads_import',
643
+ payload_json: JSON.stringify(payload),
644
+ created_at: createdAt,
645
+ };
646
+ }
647
+
648
+ // Map a legacy Beads activity event (events.jsonl: created/closed/status_changed/updated/
649
+ // label_added/claimed/…) onto a Kernel event row destined for kernel_events. The beads kind is
650
+ // namespaced as `beads.event.<kind>` and preserved verbatim in the payload alongside the
651
+ // old/new value and any inline comment, so no field is lost. id/idempotency_key derive from the
652
+ // original beads event id, making the write idempotent on re-migration.
653
+ function mapBeadsActivityEventToKernel(event = {}, importedAt) {
654
+ const kind = String(event.event_type || event.kind || 'event');
655
+ const createdAt = event.created_at || importedAt;
656
+ // For id-less events, derive the id/idempotency_key from STABLE record fields
657
+ // ONLY (never importedAt) so re-migrating the same legacy record maps to the
658
+ // same id and does not insert a duplicate kernel_events row.
659
+ const source = event.id != null && event.id !== ''
660
+ ? String(event.id)
661
+ : `${event.issue_id ?? ''}:${kind}:${event.created_at ?? ''}:${event.old_value ?? ''}:${event.new_value ?? ''}:${event.comment ?? ''}`;
662
+ return {
663
+ id: `beads-event-${encodedIdPart(source)}`,
664
+ entity_type: 'issue',
665
+ entity_id: event.issue_id != null ? String(event.issue_id) : '',
666
+ event_type: `beads.event.${kind}`,
667
+ idempotency_key: `beads-event:${source}`,
668
+ expected_revision: 0,
669
+ actor: event.actor || 'beads',
670
+ origin: 'beads_import',
671
+ payload_json: JSON.stringify({
672
+ kind,
673
+ old_value: event.old_value ?? null,
674
+ new_value: event.new_value ?? null,
675
+ comment: event.comment ?? null,
676
+ }),
677
+ created_at: createdAt,
678
+ };
679
+ }
680
+
681
+ // Map a legacy Beads interaction/memory record (interactions.jsonl: agent field_change/note/…
682
+ // audit entries) onto a Kernel event row destined for kernel_events. The interaction kind is
683
+ // namespaced as `beads.interaction.<kind>`; the structured `extra` payload is spread into the
684
+ // event payload (preserved verbatim) alongside the kind. id/idempotency_key derive from the
685
+ // original beads interaction id, making the write idempotent on re-migration.
686
+ function mapBeadsInteractionToKernel(interaction = {}, importedAt) {
687
+ const kind = String(interaction.kind || interaction.event_type || 'interaction');
688
+ const createdAt = interaction.created_at || importedAt;
689
+ // For id-less interactions, derive the id/idempotency_key from STABLE record
690
+ // fields ONLY (never importedAt) so re-migration is idempotent (no dup rows).
691
+ const source = interaction.id != null && interaction.id !== ''
692
+ ? String(interaction.id)
693
+ : `${interaction.issue_id ?? ''}:${kind}:${interaction.created_at ?? ''}:${JSON.stringify(interaction.extra ?? null)}`;
694
+ const extra = interaction.extra && typeof interaction.extra === 'object' && !Array.isArray(interaction.extra)
695
+ ? interaction.extra
696
+ : { extra: interaction.extra ?? null };
697
+ return {
698
+ id: `beads-interaction-${encodedIdPart(source)}`,
699
+ entity_type: 'issue',
700
+ entity_id: interaction.issue_id != null ? String(interaction.issue_id) : '',
701
+ event_type: `beads.interaction.${kind}`,
702
+ idempotency_key: `beads-interaction:${source}`,
703
+ expected_revision: 0,
704
+ actor: interaction.actor || 'beads',
705
+ origin: 'beads_import',
706
+ payload_json: JSON.stringify({ kind, ...extra }),
707
+ created_at: createdAt,
708
+ };
709
+ }
710
+
711
+ function importBeadsSnapshot(snapshot = {}, options = {}) {
712
+ const importedAt = options.importedAt || new Date().toISOString();
713
+ const gaps = [];
714
+ const seenGaps = new Set();
715
+ const rawIssues = Array.isArray(snapshot.issues) ? snapshot.issues : [];
716
+ const sourceIssues = applyLabelSidecar(rawIssues, snapshot);
717
+ const normalizedSnapshot = {
718
+ ...snapshot,
719
+ issues: sourceIssues,
720
+ };
721
+ // The legacy activity log (lifecycle events + agent interactions) is no longer dropped: both
722
+ // sidecars are mapped into a single kernel_events-bound bundle, preserving id/kind/actor/
723
+ // timestamp/payload. events first, then interactions, so the write order is deterministic.
724
+ const activityEvents = [
725
+ ...(Array.isArray(snapshot.events) ? snapshot.events : []).map(event => mapBeadsActivityEventToKernel(event, importedAt)),
726
+ ...(Array.isArray(snapshot.interactions) ? snapshot.interactions : []).map(interaction => mapBeadsInteractionToKernel(interaction, importedAt)),
727
+ ];
728
+ const kernel = {
729
+ issues: sourceIssues.map(issue => mapBeadsIssueToKernel(issue, importedAt, gaps, seenGaps)),
730
+ dependencies: collectDependencies(normalizedSnapshot)
731
+ .map(dependency => mapBeadsDependencyToKernel(dependency, importedAt, gaps, seenGaps)),
732
+ comments: collectComments(normalizedSnapshot).map(comment => mapBeadsCommentToKernel(comment, importedAt)),
733
+ priorityEvents: sourceIssues.map(issue => buildPriorityEvent(issue, importedAt)),
734
+ events: sourceIssues.map(issue => buildCloseEvent(issue, importedAt)).filter(Boolean),
735
+ activityEvents,
736
+ };
737
+
738
+ return {
739
+ source: 'beads',
740
+ authority: 'forge-kernel',
741
+ kernel,
742
+ report: buildFidelityReport({
743
+ kernel,
744
+ gaps,
745
+ eventCount: Array.isArray(snapshot.events) ? snapshot.events.length : 0,
746
+ interactionCount: Array.isArray(snapshot.interactions) ? snapshot.interactions.length : 0,
747
+ }),
748
+ rollback: {
749
+ available: true,
750
+ mode: 'import-only',
751
+ reason: 'Import did not mutate Beads files; discard imported Kernel records to roll back.',
752
+ },
753
+ };
754
+ }
755
+
756
+ function getCloseMetadataByIssue(kernel) {
757
+ const closeByIssue = new Map();
758
+ for (const event of kernel.events || []) {
759
+ if (event.event_type !== 'beads.issue.closed') continue;
760
+ let payload;
761
+ try {
762
+ payload = JSON.parse(event.payload_json || '{}');
763
+ } catch (_error) {
764
+ payload = {};
765
+ }
766
+ closeByIssue.set(event.entity_id, {
767
+ closed_at: payload.closed_at || event.created_at || null,
768
+ close_reason: payload.close_reason || null,
769
+ });
770
+ }
771
+ return closeByIssue;
772
+ }
773
+
774
+ function getDependenciesByIssue(kernel) {
775
+ const byIssue = new Map();
776
+ for (const dependency of kernel.dependencies || []) {
777
+ const beadsDependency = {
778
+ issue_id: dependency.issue_id,
779
+ depends_on_id: dependency.blocks_issue_id,
780
+ type: dependency.dependency_type || 'blocks',
781
+ created_at: dependency.created_at,
782
+ metadata: '{}',
783
+ };
784
+ if (dependency.created_by) {
785
+ beadsDependency.created_by = dependency.created_by;
786
+ }
787
+ if (!byIssue.has(dependency.issue_id)) {
788
+ byIssue.set(dependency.issue_id, []);
789
+ }
790
+ byIssue.get(dependency.issue_id).push(beadsDependency);
791
+ }
792
+ return byIssue;
793
+ }
794
+
795
+ function isBlockingDependencyType(type) {
796
+ return (type || 'blocks') === 'blocks';
797
+ }
798
+
799
+ function getBlockingDependentsByIssue(kernel) {
800
+ const byIssue = new Map();
801
+ for (const dependency of kernel.dependencies || []) {
802
+ if (!isBlockingDependencyType(dependency.dependency_type)) continue;
803
+ if (!dependency.blocks_issue_id) continue;
804
+ if (!byIssue.has(dependency.blocks_issue_id)) {
805
+ byIssue.set(dependency.blocks_issue_id, []);
806
+ }
807
+ byIssue.get(dependency.blocks_issue_id).push(dependency.issue_id);
808
+ }
809
+ return byIssue;
810
+ }
811
+
812
+ function mapKernelIssueToBeads(issue, dependenciesByIssue, dependentsByIssue, closeByIssue) {
813
+ const closeMetadata = closeByIssue.get(issue.id) || {};
814
+ const priority = normalizePriority(issue.priority);
815
+ const dependencies = dependenciesByIssue.get(issue.id) || [];
816
+ // Resolve the final Beads close payload — including the synthesized cancellation marker for a
817
+ // cancelled Kernel issue — BEFORE building projection metadata. forge_projection.payload_hash
818
+ // must be computed over the close_reason actually exported; otherwise re-import recomputes a
819
+ // different hash, drops projection_origin, and loses the entity_revision provenance.
820
+ const effectiveClose = { ...closeMetadata };
821
+ if (issue.status === 'cancelled' && !CANCELLED_CLOSE_REASON.test(String(effectiveClose.close_reason || ''))) {
822
+ effectiveClose.close_reason = effectiveClose.close_reason
823
+ ? `${effectiveClose.close_reason} (cancelled)`
824
+ : 'cancelled';
825
+ }
826
+ const exported = {
827
+ _type: 'issue',
828
+ id: issue.id,
829
+ title: issue.title,
830
+ description: issue.body || '',
831
+ status: beadsStatusForKernelStatus(issue.status),
832
+ priority: priority.beads,
833
+ issue_type: issue.type || 'task',
834
+ created_at: issue.created_at,
835
+ updated_at: issue.updated_at,
836
+ dependencies,
837
+ dependency_count: dependencies.filter(dependency => isBlockingDependencyType(dependency.type)).length,
838
+ dependent_count: (dependentsByIssue.get(issue.id) || []).length,
839
+ };
840
+ const labels = deserializeLabels(issue.labels);
841
+ if (labels.length > 0) {
842
+ exported.labels = labels;
843
+ }
844
+ const acceptanceCriteria = deserializeAcceptanceCriteria(issue.acceptance_criteria);
845
+ if (acceptanceCriteria !== null && acceptanceCriteria !== undefined && acceptanceCriteria !== '') {
846
+ exported.acceptance_criteria = acceptanceCriteria;
847
+ }
848
+ if (issue.created_by) {
849
+ exported.created_by = issue.created_by;
850
+ }
851
+ exported.metadata = JSON.stringify(buildForgeProjectionMetadata(issue, effectiveClose));
852
+
853
+ if (effectiveClose.closed_at) {
854
+ exported.closed_at = effectiveClose.closed_at;
855
+ }
856
+ if (effectiveClose.close_reason) {
857
+ exported.close_reason = effectiveClose.close_reason;
858
+ }
859
+
860
+ return exported;
861
+ }
862
+
863
+ function mapKernelCommentToBeads(comment) {
864
+ return {
865
+ id: comment.id,
866
+ issue_id: comment.issue_id,
867
+ author: comment.actor,
868
+ text: comment.body,
869
+ created_at: comment.created_at,
870
+ };
871
+ }
872
+
873
+ function mapKernelDependencyToBeads(dependency) {
874
+ const exported = {
875
+ issue_id: dependency.issue_id,
876
+ depends_on_id: dependency.blocks_issue_id,
877
+ type: dependency.dependency_type || 'blocks',
878
+ created_at: dependency.created_at,
879
+ metadata: '{}',
880
+ };
881
+ if (dependency.created_by) {
882
+ exported.created_by = dependency.created_by;
883
+ }
884
+ return exported;
885
+ }
886
+
887
+ function buildExportFiles(kernel) {
888
+ const dependenciesByIssue = getDependenciesByIssue(kernel);
889
+ const dependentsByIssue = getBlockingDependentsByIssue(kernel);
890
+ const closeByIssue = getCloseMetadataByIssue(kernel);
891
+ const issues = (kernel.issues || []).map(issue => (
892
+ mapKernelIssueToBeads(issue, dependenciesByIssue, dependentsByIssue, closeByIssue)
893
+ ));
894
+ const comments = (kernel.comments || []).map(mapKernelCommentToBeads);
895
+ const dependencies = (kernel.dependencies || []).map(mapKernelDependencyToBeads);
896
+
897
+ return {
898
+ records: {
899
+ issues,
900
+ comments,
901
+ dependencies,
902
+ },
903
+ files: {
904
+ 'issues.jsonl': stringifyJsonl(issues),
905
+ 'comments.jsonl': stringifyJsonl(comments),
906
+ 'dependencies.jsonl': stringifyJsonl(dependencies),
907
+ },
908
+ };
909
+ }
910
+
911
+ function buildExportReport(kernel, gaps = []) {
912
+ return buildFidelityReport({
913
+ kernel: {
914
+ issues: kernel.issues || [],
915
+ dependencies: kernel.dependencies || [],
916
+ comments: kernel.comments || [],
917
+ events: kernel.events || [],
918
+ },
919
+ gaps,
920
+ });
921
+ }
922
+
923
+ function captureRollbackSnapshot(beadsDir, files) {
924
+ const snapshot = {};
925
+ for (const file of files) {
926
+ const filePath = path.join(beadsDir, file);
927
+ snapshot[file] = fs.existsSync(filePath)
928
+ ? { existed: true, content: fs.readFileSync(filePath, 'utf8') }
929
+ : { existed: false, content: null };
930
+ }
931
+
932
+ return {
933
+ available: true,
934
+ beadsDir,
935
+ files: [...files],
936
+ snapshot,
937
+ };
938
+ }
939
+
940
+ function exportKernelToBeads(kernel = {}, options = {}) {
941
+ const dryRun = options.dryRun !== false;
942
+ const { files } = buildExportFiles(kernel);
943
+ const writes = BEADS_EXPORT_FILES.map(file => ({
944
+ file,
945
+ bytes: Buffer.byteLength(files[file], 'utf8'),
946
+ }));
947
+
948
+ if (dryRun) {
949
+ return {
950
+ source: 'forge-kernel',
951
+ target: 'beads',
952
+ dryRun: true,
953
+ files,
954
+ writes,
955
+ report: buildExportReport(kernel),
956
+ rollback: {
957
+ available: false,
958
+ reason: 'Dry-run did not write Beads files.',
959
+ },
960
+ };
961
+ }
962
+
963
+ if (!options.beadsDir || typeof options.beadsDir !== 'string') {
964
+ throw new TypeError('beadsDir is required when dryRun is false');
965
+ }
966
+
967
+ fs.mkdirSync(options.beadsDir, { recursive: true });
968
+ const rollback = captureRollbackSnapshot(options.beadsDir, BEADS_EXPORT_FILES);
969
+ try {
970
+ for (const file of BEADS_EXPORT_FILES) {
971
+ fs.writeFileSync(path.join(options.beadsDir, file), files[file]);
972
+ }
973
+ } catch (error) {
974
+ rollbackBeadsExport(rollback);
975
+ error.rollback = rollback;
976
+ throw error;
977
+ }
978
+
979
+ return {
980
+ source: 'forge-kernel',
981
+ target: 'beads',
982
+ dryRun: false,
983
+ files,
984
+ writes,
985
+ report: buildExportReport(kernel),
986
+ rollback,
987
+ };
988
+ }
989
+
990
+ function rollbackBeadsExport(rollback) {
991
+ if (!rollback?.available || !rollback.beadsDir || !rollback.snapshot) {
992
+ throw new Error('Rollback snapshot is not available');
993
+ }
994
+
995
+ for (const file of rollback.files || []) {
996
+ const filePath = path.join(rollback.beadsDir, file);
997
+ const entry = rollback.snapshot[file];
998
+ if (!entry) continue;
999
+ if (entry.existed) {
1000
+ fs.writeFileSync(filePath, entry.content);
1001
+ } else if (fs.existsSync(filePath)) {
1002
+ fs.unlinkSync(filePath);
1003
+ }
1004
+ }
1005
+
1006
+ return {
1007
+ rolledBack: true,
1008
+ files: [...(rollback.files || [])],
1009
+ };
1010
+ }
1011
+
1012
+ class BeadsKernelCompatibilityAdapter {
1013
+ constructor(options = {}) {
1014
+ this.id = options.id || 'beads-kernel-compat';
1015
+ this.kind = 'import-export';
1016
+ this.name = options.name || 'Beads Kernel Compatibility Adapter';
1017
+ this.version = options.version || '0.1.0';
1018
+ }
1019
+
1020
+ import(snapshot, options = {}) {
1021
+ return importBeadsSnapshot(snapshot, options);
1022
+ }
1023
+
1024
+ export(kernel, options = {}) {
1025
+ return exportKernelToBeads(kernel, options);
1026
+ }
1027
+ }
1028
+
1029
+ module.exports = {
1030
+ BEADS_EXPORT_FILES,
1031
+ PRESERVED_FIELDS,
1032
+ BeadsKernelCompatibilityAdapter,
1033
+ exportKernelToBeads,
1034
+ importBeadsSnapshot,
1035
+ mapBeadsActivityEventToKernel,
1036
+ mapBeadsInteractionToKernel,
1037
+ loadBeadsSnapshotFromDirectory,
1038
+ parseJsonl,
1039
+ rollbackBeadsExport,
1040
+ safeIdPart,
1041
+ stringifyJsonl,
1042
+ };