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.
- package/.claude/rules/{greptile-review-process.md → review-process.md} +56 -41
- package/.claude/scripts/{greptile-resolve.sh → review-resolve.sh} +13 -3
- package/.cursor/rules/permissions-guidance.mdc +2 -2
- package/.forge/hooks/check-tdd.js +3 -0
- package/.forge/hooks/forge-native-hook.js +245 -0
- package/.forge/protected-paths.yaml +157 -0
- package/AGENTS.md +151 -61
- package/CHANGELOG.md +681 -0
- package/CLAUDE.md +9 -106
- package/QUICKSTART.md +171 -0
- package/README.md +271 -363
- package/bin/forge-cmd.js +120 -9
- package/bin/forge-preflight.js +26 -5
- package/bin/forge.js +466 -489
- package/docs/INDEX.md +93 -0
- package/docs/PROJECT_DESIGN.md +685 -0
- package/docs/architecture/index.md +66 -0
- package/docs/architecture/notes/README.md +35 -0
- package/docs/architecture/subsystems/README.md +46 -0
- package/docs/{TOOLCHAIN.md → forge/TOOLCHAIN.md} +56 -47
- package/docs/forge/VALIDATION.md +82 -0
- package/docs/{AGENT_INSTALL_PROMPT.md → guides/AGENT_INSTALL_PROMPT.md} +3 -3
- package/docs/guides/BEADS_GITHUB_SYNC.md +32 -0
- package/docs/{ENHANCED_ONBOARDING.md → guides/ENHANCED_ONBOARDING.md} +16 -12
- package/docs/guides/GREPTILE_SETUP.md +46 -0
- package/docs/guides/MANUAL_REVIEW_GUIDE.md +58 -0
- package/docs/guides/MIGRATION.md +56 -0
- package/docs/guides/SETUP.md +118 -0
- package/docs/guides/SUPPORT.md +185 -0
- package/docs/guides/WORKFLOW_TEMPLATES.md +74 -0
- package/docs/guides/memory-backends.md +183 -0
- package/docs/reference/ADAPTERS.md +128 -0
- package/docs/reference/AGENT_SKILL_PARITY.md +175 -0
- package/docs/reference/COMMANDS.md +205 -0
- package/docs/reference/DECISION_DRIFT_GUARDS.md +97 -0
- package/docs/{EXAMPLES.md → reference/EXAMPLES.md} +7 -5
- package/docs/reference/FORGE_KERNEL_STORAGE_MODEL.md +135 -0
- package/docs/reference/HERMES_INTEGRATION.md +118 -0
- package/docs/reference/INSIGHTS_RECAP.md +63 -0
- package/docs/reference/INSTALL.md +164 -0
- package/docs/reference/KERNEL_TAXONOMY_VALIDATION.md +161 -0
- package/docs/reference/PROTECTED_PATH_MANIFEST.md +25 -0
- package/docs/reference/RELEASE.md +68 -0
- package/docs/reference/RESEARCH_TEMPLATE.md +292 -0
- package/docs/{ROADMAP.md → reference/ROADMAP.md} +12 -9
- package/docs/reference/SKILLS.md +35 -0
- package/docs/reference/STATUS_BOARD.md +80 -0
- package/docs/reference/TEMPLATES.md +106 -0
- package/docs/reference/TOOLCHAIN.md +658 -0
- package/docs/reference/VALIDATION.md +82 -0
- package/docs/reference/agent-permissions.md +169 -0
- package/docs/reference/beads-to-kernel-migration-ux.md +61 -0
- package/docs/reference/control-plane-guarantees.md +125 -0
- package/docs/reference/dependency-chain.md +331 -0
- package/docs/reference/forge-kernel-issue-command-contract.md +161 -0
- package/docs/reference/forge-kernel-schema.md +72 -0
- package/docs/reference/kernel-conflict-evaluators.md +27 -0
- package/docs/reference/patch-md-format.md +77 -0
- package/docs/reference/protected-state-surfaces.md +59 -0
- package/docs/reference/shepherd.md +115 -0
- package/docs/reference/superpowers-analysis.md +320 -0
- package/docs/reference/superpowers-integration-options.md +404 -0
- package/docs/reference/test-environment.md +519 -0
- package/docs/reference/upgrade-safety.md +59 -0
- package/lefthook.yml +18 -0
- package/lib/adapter-cli.js +307 -0
- package/lib/adapters/beads-issue-adapter.js +127 -0
- package/lib/adapters/beads-kernel-compat.js +1042 -0
- package/lib/adapters/greptile-review-adapter.js +141 -0
- package/lib/adapters/kernel-issue-adapter.js +101 -0
- package/lib/adapters/pr-state-adapter.js +484 -0
- package/lib/adoption-profiles.js +126 -0
- package/lib/agents/README.md +2 -6
- package/lib/agents/claude.plugin.json +3 -8
- package/lib/agents/codex.plugin.json +9 -1
- package/lib/agents/cursor.plugin.json +2 -6
- package/lib/agents/hermes.plugin.json +22 -0
- package/lib/agents-config.js +39 -1236
- package/lib/audit-evidence.js +282 -0
- package/lib/beads-setup.js +225 -28
- package/lib/beads-sync-scaffold.js +36 -107
- package/lib/codex-skills.js +51 -1
- package/lib/commands/_issue.js +744 -70
- package/lib/commands/_manifest.js +91 -0
- package/lib/commands/_registry.js +85 -34
- package/lib/commands/_resolve-command-opts.js +261 -0
- package/lib/commands/_serve-security.js +270 -0
- package/lib/commands/adapter.js +12 -0
- package/lib/commands/add.js +118 -0
- package/lib/commands/audit.js +70 -0
- package/lib/commands/blocked.js +5 -0
- package/lib/commands/board.js +64 -0
- package/lib/commands/claim.js +21 -2
- package/lib/commands/claims.js +7 -0
- package/lib/commands/clean.js +485 -75
- package/lib/commands/close.js +2 -2
- package/lib/commands/comment.js +5 -0
- package/lib/commands/control.js +148 -0
- package/lib/commands/create.js +2 -2
- package/lib/commands/dev.js +185 -7
- package/lib/commands/doc-gate.js +336 -0
- package/lib/commands/doctor.js +156 -0
- package/lib/commands/explain.js +15 -0
- package/lib/commands/export.js +237 -0
- package/lib/commands/gate.js +192 -0
- package/lib/commands/hooks.js +242 -0
- package/lib/commands/inbox.js +118 -0
- package/lib/commands/init.js +598 -0
- package/lib/commands/insights.js +79 -0
- package/lib/commands/issue.js +12 -1
- package/lib/commands/issues.js +66 -0
- package/lib/commands/lint.js +5 -0
- package/lib/commands/list.js +2 -2
- package/lib/commands/merge.js +312 -0
- package/lib/commands/migrate.js +523 -0
- package/lib/commands/new.js +12 -0
- package/lib/commands/options.js +241 -0
- package/lib/commands/orient.js +13 -0
- package/lib/commands/orphans.js +5 -0
- package/lib/commands/patch.js +67 -0
- package/lib/commands/plan.js +436 -24
- package/lib/commands/preflight.js +211 -0
- package/lib/commands/prime.js +13 -0
- package/lib/commands/push.js +69 -2
- package/lib/commands/ready.js +2 -2
- package/lib/commands/recall.js +116 -0
- package/lib/commands/recap.js +61 -0
- package/lib/commands/recommend.js +22 -2
- package/lib/commands/release.js +91 -0
- package/lib/commands/remember.js +74 -0
- package/lib/commands/role.js +99 -0
- package/lib/commands/serve.js +581 -0
- package/lib/commands/setup.js +851 -979
- package/lib/commands/shepherd.js +436 -0
- package/lib/commands/ship.js +23 -1
- package/lib/commands/show.js +2 -2
- package/lib/commands/stage.js +192 -0
- package/lib/commands/stale.js +5 -0
- package/lib/commands/status.js +329 -11
- package/lib/commands/sync.js +34 -46
- package/lib/commands/team.js +15 -2
- package/lib/commands/test.js +58 -7
- package/lib/commands/update.js +2 -2
- package/lib/commands/upgrade.js +47 -0
- package/lib/commands/validate.js +56 -25
- package/lib/commands/worktree.js +308 -128
- package/lib/config-writer.js +202 -0
- package/lib/control-plane.js +236 -0
- package/lib/core/runtime-graph.js +946 -0
- package/lib/dep-guard/keyword-ripple.js +184 -0
- package/lib/deprecated-sync-cleanup.js +362 -0
- package/lib/detect-agent.js +2 -28
- package/lib/detect-worktree.js +42 -17
- package/lib/doc-gate/declaration.js +177 -0
- package/lib/doc-gate/detect.js +289 -0
- package/lib/doc-gate/gate.js +375 -0
- package/lib/doc-gate/okf-config.js +128 -0
- package/lib/doc-gate/okf.js +429 -0
- package/lib/docs-command.js +1161 -6
- package/lib/forge-issues.js +697 -0
- package/lib/forge-lock.js +262 -0
- package/lib/gate-events.js +193 -0
- package/lib/global-flags.js +74 -0
- package/lib/greptile-match.js +7 -63
- package/lib/harness-capability-matrix.js +380 -0
- package/lib/hook-global-installer.js +347 -0
- package/lib/hook-renderer.js +451 -0
- package/lib/inbox.js +391 -0
- package/lib/insights.js +397 -0
- package/lib/issue-adapter.js +156 -0
- package/lib/issue-backend.js +145 -0
- package/lib/issue-render.js +220 -0
- package/lib/issue-sync/authority.js +100 -0
- package/lib/issue-sync/github-pull.js +184 -0
- package/lib/issue-sync/import-primitives.js +98 -0
- package/lib/issue-sync/legacy-link-bridge.js +436 -0
- package/lib/issue-sync/link-store.js +292 -0
- package/lib/issue-sync/project-github.js +123 -0
- package/lib/issue-sync/reconcile.js +195 -0
- package/lib/issue-sync/schema.js +126 -0
- package/lib/kernel/backing-issue.js +305 -0
- package/lib/kernel/broker.js +1218 -0
- package/lib/kernel/cli-broker-factory.js +130 -0
- package/lib/kernel/conflict-signal.js +82 -0
- package/lib/kernel/evaluators.js +195 -0
- package/lib/kernel/fs-class.js +495 -0
- package/lib/kernel/issue-command-contract.js +559 -0
- package/lib/kernel/issue-id-resolver.js +186 -0
- package/lib/kernel/lease-enforcer.js +158 -0
- package/lib/kernel/migrations.js +333 -0
- package/lib/kernel/planning-buckets-schema.js +109 -0
- package/lib/kernel/projection-jsonl-writer.js +450 -0
- package/lib/kernel/readiness-model.js +329 -0
- package/lib/kernel/schema.js +356 -0
- package/lib/kernel/sqlite-driver.js +2504 -0
- package/lib/kernel/taxonomy-validator.js +394 -0
- package/lib/lefthook-check.js +8 -4
- package/lib/lefthook-wiring.js +413 -0
- package/lib/mcp-config-renderer.js +288 -0
- package/lib/memory/graphiti-mcp.js +106 -0
- package/lib/memory/router.js +387 -0
- package/lib/memory/typed-api.js +102 -0
- package/lib/memory-digest.js +195 -0
- package/lib/merge-rules.js +395 -0
- package/lib/migrate-dry-run.js +466 -0
- package/lib/orientation.js +863 -0
- package/lib/package-manager-remediation.js +103 -0
- package/lib/package-root.js +381 -0
- package/lib/patch-intent.js +890 -0
- package/lib/plugin-catalog.js +3 -4
- package/lib/plugin-manager.js +0 -5
- package/lib/pr-bundle.js +186 -0
- package/lib/pr-monitor/differ.js +195 -0
- package/lib/pr-monitor/events.js +0 -0
- package/lib/pr-monitor/gather.js +124 -0
- package/lib/pr-monitor/journal.js +299 -0
- package/lib/pr-monitor/monitor.js +146 -0
- package/lib/pr-monitor/render-sticky.js +157 -0
- package/lib/pr-monitor/watch-lifecycle.js +95 -0
- package/lib/pr-monitor/watch.js +247 -0
- package/lib/pr-pull.js +1273 -0
- package/lib/pr-shepherd.js +494 -0
- package/lib/pr-state-validator.js +59 -0
- package/lib/preflight/gates.js +237 -0
- package/lib/preflight/runner.js +116 -0
- package/lib/project-discovery.js +0 -53
- package/lib/project-memory.js +166 -0
- package/lib/protected-path-manifest.js +281 -0
- package/lib/protected-state-surfaces.js +387 -0
- package/lib/release-readiness.js +2089 -0
- package/lib/reset.js +59 -45
- package/lib/review-adapter.js +68 -0
- package/lib/rules-sync.js +260 -0
- package/lib/runtime-health.js +332 -23
- package/lib/safety-config-renderer.js +268 -0
- package/lib/setup-action-log.js +1 -7
- package/lib/setup.js +27 -65
- package/lib/shell-utils.js +76 -6
- package/lib/skills-sync.js +330 -0
- package/lib/smart-status/conflicts.js +205 -0
- package/lib/smart-status/scoring.js +191 -0
- package/lib/status/beads-snapshot.js +145 -0
- package/lib/status/presenter.js +216 -0
- package/lib/status/snapshot.js +186 -0
- package/lib/sync-backend.js +202 -0
- package/lib/untrusted-content.js +52 -0
- package/lib/upgrade-safety.js +199 -0
- package/lib/workflow/enforce-stage.js +298 -47
- package/lib/workflow/stage-transition.js +115 -0
- package/lib/workflow/stages.js +30 -6
- package/lib/workflow/state-manager.js +159 -14
- package/lib/workflow/state.js +23 -1
- package/lib/workflow-profiles.js +17 -5
- package/package.json +46 -36
- package/rules/documentation.md +19 -0
- package/rules/kernel-tracking.md +26 -0
- package/rules/security.md +22 -0
- package/rules/tdd.md +20 -0
- package/rules/workflow.md +27 -0
- package/scripts/auto-backing-issue.js +47 -0
- package/scripts/beads-context.sh +165 -22
- package/scripts/beads-migrate-to-dolt.sh +7 -0
- package/scripts/beads-upgrade-smoke.sh +284 -0
- package/scripts/behavioral-judge.sh +115 -11
- package/scripts/benchmark.js +349 -63
- package/scripts/bootstrap-windows-tools.sh +78 -0
- package/scripts/branch-protection.js +2 -3
- package/scripts/check-agents.js +34 -137
- package/scripts/commitlint.js +3 -1
- package/scripts/conflict-detect.sh +3 -0
- package/scripts/dep-guard-analyze.js +52 -17
- package/scripts/dep-guard-keyword-ripple.js +29 -0
- package/scripts/dep-guard-render-review.js +86 -0
- package/scripts/dep-guard.sh +64 -232
- package/scripts/file-index.sh +3 -0
- package/scripts/forge-team/lib/claim.sh +34 -18
- package/scripts/forge-team/lib/dashboard.sh +61 -86
- package/scripts/forge-team/lib/epic.sh +99 -263
- package/scripts/forge-team/lib/hooks.sh +26 -28
- package/scripts/forge-team/lib/identity.sh +4 -4
- package/scripts/forge-team/lib/sync-github.sh +144 -47
- package/scripts/forge-team/lib/verify.sh +93 -83
- package/scripts/forge-team/lib/workload.sh +41 -65
- package/scripts/forge-team/tests/claim.test.sh +25 -19
- package/scripts/forge-team/tests/dashboard.test.sh +31 -46
- package/scripts/forge-team/tests/epic.test.sh +52 -71
- package/scripts/forge-team/tests/hooks.test.sh +38 -50
- package/scripts/forge-team/tests/identity.test.sh +3 -3
- package/scripts/forge-team/tests/integration.test.sh +44 -66
- package/scripts/forge-team/tests/sync-github.test.sh +183 -79
- package/scripts/forge-team/tests/verify.test.sh +37 -46
- package/scripts/forge-team/tests/workflow-integration.test.sh +4 -4
- package/scripts/forge-team/tests/workload.test.sh +32 -66
- package/scripts/gen-command-manifest.js +153 -0
- package/scripts/gen-embedded-assets.mjs +129 -0
- package/scripts/install.ps1 +139 -0
- package/scripts/install.sh +268 -0
- package/scripts/lib/beads-migrate-to-dolt.mjs +503 -0
- package/scripts/lib/release-asset.mjs +84 -0
- package/scripts/parity-check.mjs +145 -0
- package/scripts/parity-check.test.mjs +58 -0
- package/scripts/pin-agentic-workflow-images.js +112 -0
- package/scripts/pr-coordinator.sh +3 -0
- package/scripts/preflight-sonar.eslint.config.mjs +44 -0
- package/scripts/preflight.sh +108 -0
- package/scripts/protected-state-check.js +104 -0
- package/scripts/smart-status-score.js +31 -0
- package/scripts/smart-status-sessions.js +51 -0
- package/scripts/smart-status.sh +117 -369
- package/scripts/spikes/config-race-bench.js +111 -0
- package/scripts/spikes/harness-capability-matrix.js +13 -0
- package/scripts/spikes/patch-anchor-stability-bench.js +125 -0
- package/scripts/spikes/protected-path-manifest.js +20 -0
- package/scripts/spikes/skill-auto-invoke-parity.js +292 -0
- package/scripts/sync-agent-skills.js +62 -0
- package/scripts/sync-agentic-workflow.js +48 -0
- package/scripts/sync-utils.sh +3 -0
- package/scripts/test-ci-shard.js +251 -0
- package/scripts/test-dashboard.js +188 -52
- package/scripts/test-full-suite.js +186 -0
- package/scripts/test-profile.js +278 -0
- package/scripts/test.js +302 -28
- package/scripts/validate.js +143 -0
- package/scripts/validate.sh +18 -1
- package/skills/claim-safety/SKILL.md +102 -0
- package/skills/claim-safety/evals/evals.json +46 -0
- package/{.github/prompts/dev.prompt.md → skills/dev/SKILL.md} +46 -52
- package/skills/dev/evals/evals.json +50 -0
- package/skills/hermes-forge/SKILL.md +185 -0
- package/skills/hermes-forge/evals/evals.json +46 -0
- package/skills/issue-basics/SKILL.md +111 -0
- package/skills/issue-basics/evals/evals.json +46 -0
- package/skills/kernel/SKILL.md +166 -0
- package/skills/kernel/evals/evals.json +50 -0
- package/skills/memory/SKILL.md +102 -0
- package/skills/parallel-deep-research/SKILL.md +14 -11
- package/skills/parallel-deep-research/evals/evals.json +11 -27
- package/{.github/prompts/plan.prompt.md → skills/plan/SKILL.md} +134 -159
- package/skills/plan/evals/evals.json +42 -0
- package/skills/research/SKILL.md +195 -0
- package/skills/research/evals/evals.json +42 -0
- package/{.github/prompts/review.prompt.md → skills/review/SKILL.md} +98 -62
- package/skills/review/evals/evals.json +42 -0
- package/skills/rollback/SKILL.md +110 -0
- package/skills/rollback/evals/evals.json +46 -0
- package/skills/rollback/references/methods.md +204 -0
- package/{.cursor/commands/rollback.md → skills/rollback/references/workflow-integration.md} +10 -284
- package/skills/shepherd/SKILL.md +66 -0
- package/skills/shepherd/evals/evals.json +42 -0
- package/skills/ship/SKILL.md +251 -0
- package/skills/ship/evals/evals.json +42 -0
- package/skills/smith/SKILL.md +142 -0
- package/skills/smith/evals/evals.json +46 -0
- package/skills/smith/references/autonomy-and-gates.md +94 -0
- package/{.github/prompts/sonarcloud.prompt.md → skills/sonarcloud/SKILL.md} +14 -3
- package/skills/sonarcloud/evals/evals.json +46 -0
- package/skills/sonarcloud-analysis/SKILL.md +18 -13
- package/skills/sonarcloud-analysis/evals/evals.json +11 -15
- package/skills/status/SKILL.md +102 -0
- package/skills/status/evals/evals.json +50 -0
- package/skills/triage-ready/SKILL.md +121 -0
- package/skills/triage-ready/evals/evals.json +42 -0
- package/{.github/prompts/validate.prompt.md → skills/validate/SKILL.md} +52 -29
- package/skills/validate/evals/evals.json +42 -0
- package/skills/verify/SKILL.md +299 -0
- package/skills/verify/evals/evals.json +50 -0
- package/.claude/commands/dev.md +0 -345
- package/.claude/commands/plan.md +0 -566
- package/.claude/commands/premerge.md +0 -186
- package/.claude/commands/research.md +0 -42
- package/.claude/commands/review.md +0 -451
- package/.claude/commands/rollback.md +0 -721
- package/.claude/commands/ship.md +0 -213
- package/.claude/commands/sonarcloud.md +0 -152
- package/.claude/commands/status.md +0 -90
- package/.claude/commands/validate.md +0 -288
- package/.claude/commands/verify.md +0 -269
- package/.claude/rules/workflow.md +0 -121
- package/.cline/workflows/dev.md +0 -342
- package/.cline/workflows/plan.md +0 -563
- package/.cline/workflows/premerge.md +0 -183
- package/.cline/workflows/research.md +0 -39
- package/.cline/workflows/review.md +0 -448
- package/.cline/workflows/rollback.md +0 -718
- package/.cline/workflows/ship.md +0 -210
- package/.cline/workflows/sonarcloud.md +0 -146
- package/.cline/workflows/status.md +0 -87
- package/.cline/workflows/validate.md +0 -285
- package/.cline/workflows/verify.md +0 -266
- package/.codex/config.toml +0 -11
- package/.codex/skills/dev/SKILL.md +0 -345
- package/.codex/skills/plan/SKILL.md +0 -566
- package/.codex/skills/premerge/SKILL.md +0 -186
- package/.codex/skills/research/SKILL.md +0 -42
- package/.codex/skills/review/SKILL.md +0 -451
- package/.codex/skills/rollback/SKILL.md +0 -721
- package/.codex/skills/ship/SKILL.md +0 -213
- package/.codex/skills/sonarcloud/SKILL.md +0 -149
- package/.codex/skills/status/SKILL.md +0 -90
- package/.codex/skills/validate/SKILL.md +0 -288
- package/.codex/skills/verify/SKILL.md +0 -269
- package/.cursor/commands/dev.md +0 -342
- package/.cursor/commands/plan.md +0 -563
- package/.cursor/commands/premerge.md +0 -183
- package/.cursor/commands/research.md +0 -39
- package/.cursor/commands/review.md +0 -448
- package/.cursor/commands/ship.md +0 -210
- package/.cursor/commands/sonarcloud.md +0 -146
- package/.cursor/commands/status.md +0 -87
- package/.cursor/commands/validate.md +0 -285
- package/.cursor/commands/verify.md +0 -266
- package/.cursorrules +0 -149
- package/.github/prompts/premerge.prompt.md +0 -188
- package/.github/prompts/research.prompt.md +0 -44
- package/.github/prompts/rollback.prompt.md +0 -723
- package/.github/prompts/ship.prompt.md +0 -215
- package/.github/prompts/status.prompt.md +0 -92
- package/.github/prompts/verify.prompt.md +0 -271
- package/.github/workflows/beads-to-github.yml +0 -56
- package/.github/workflows/github-to-beads.yml +0 -97
- package/.kilocode/workflows/dev.md +0 -346
- package/.kilocode/workflows/plan.md +0 -567
- package/.kilocode/workflows/premerge.md +0 -187
- package/.kilocode/workflows/research.md +0 -43
- package/.kilocode/workflows/review.md +0 -452
- package/.kilocode/workflows/rollback.md +0 -722
- package/.kilocode/workflows/ship.md +0 -214
- package/.kilocode/workflows/sonarcloud.md +0 -150
- package/.kilocode/workflows/status.md +0 -91
- package/.kilocode/workflows/validate.md +0 -289
- package/.kilocode/workflows/verify.md +0 -270
- package/.opencode/commands/dev.md +0 -345
- package/.opencode/commands/plan.md +0 -566
- package/.opencode/commands/premerge.md +0 -186
- package/.opencode/commands/research.md +0 -42
- package/.opencode/commands/review.md +0 -451
- package/.opencode/commands/rollback.md +0 -721
- package/.opencode/commands/ship.md +0 -213
- package/.opencode/commands/sonarcloud.md +0 -149
- package/.opencode/commands/status.md +0 -90
- package/.opencode/commands/validate.md +0 -288
- package/.opencode/commands/verify.md +0 -269
- package/.roo/commands/dev.md +0 -346
- package/.roo/commands/plan.md +0 -567
- package/.roo/commands/premerge.md +0 -187
- package/.roo/commands/research.md +0 -43
- package/.roo/commands/review.md +0 -452
- package/.roo/commands/rollback.md +0 -722
- package/.roo/commands/ship.md +0 -214
- package/.roo/commands/sonarcloud.md +0 -150
- package/.roo/commands/status.md +0 -91
- package/.roo/commands/validate.md +0 -289
- package/.roo/commands/verify.md +0 -270
- package/docs/BEADS_GITHUB_SYNC.md +0 -255
- package/docs/GREPTILE_SETUP.md +0 -400
- package/docs/MANUAL_REVIEW_GUIDE.md +0 -106
- package/docs/SETUP.md +0 -663
- package/docs/VALIDATION.md +0 -363
- package/lib/agents/cline.plugin.json +0 -29
- package/lib/agents/copilot.plugin.json +0 -24
- package/lib/agents/kilocode.plugin.json +0 -22
- package/lib/agents/opencode.plugin.json +0 -23
- package/lib/agents/roo.plugin.json +0 -30
- package/lib/beads-health-check.js +0 -143
- package/lib/commands/commands-reset.js +0 -147
- package/opencode.json +0 -67
- package/scripts/beads-context.test.js +0 -567
- package/scripts/github-beads-sync/comment.mjs +0 -64
- package/scripts/github-beads-sync/config.mjs +0 -148
- package/scripts/github-beads-sync/github-api.mjs +0 -131
- package/scripts/github-beads-sync/index.mjs +0 -332
- package/scripts/github-beads-sync/label-mapper.mjs +0 -54
- package/scripts/github-beads-sync/mapping.mjs +0 -78
- package/scripts/github-beads-sync/reverse-sync-cli.mjs +0 -31
- package/scripts/github-beads-sync/reverse-sync.mjs +0 -138
- package/scripts/github-beads-sync/run-bd.mjs +0 -161
- package/scripts/github-beads-sync/sanitize.mjs +0 -121
- package/scripts/github-beads-sync.config.json +0 -26
- package/scripts/sync-commands.js +0 -600
|
@@ -0,0 +1,523 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('node:fs');
|
|
4
|
+
const os = require('node:os');
|
|
5
|
+
const path = require('node:path');
|
|
6
|
+
const { execFileSync } = require('node:child_process');
|
|
7
|
+
|
|
8
|
+
const {
|
|
9
|
+
buildMigrationDryRunReport,
|
|
10
|
+
renderMigrationDryRunReport,
|
|
11
|
+
} = require('../migrate-dry-run');
|
|
12
|
+
const {
|
|
13
|
+
loadBeadsSnapshotFromDirectory,
|
|
14
|
+
importBeadsSnapshot,
|
|
15
|
+
} = require('../adapters/beads-kernel-compat');
|
|
16
|
+
const { buildMigratedKernelIssueDeps } = require('../kernel/cli-broker-factory');
|
|
17
|
+
|
|
18
|
+
function hasArg(args, name) {
|
|
19
|
+
return Array.isArray(args) && args.includes(name);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Parse `forge migrate` options from positional args (with a parsed flags object
|
|
24
|
+
* as a fallback). Supported: --from <source>, --source <dir>, --dry-run, --json.
|
|
25
|
+
* Unknown tokens are ignored so the legacy v2→v3 PoC flags pass through untouched.
|
|
26
|
+
*/
|
|
27
|
+
function parseMigrateOptions(args = [], flags = {}) {
|
|
28
|
+
const options = {
|
|
29
|
+
from: flags.from,
|
|
30
|
+
source: flags.source,
|
|
31
|
+
dryRun: Boolean(flags.dryRun || flags['dry-run']),
|
|
32
|
+
json: Boolean(flags.json),
|
|
33
|
+
errors: [],
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const takeValue = (arg, prefix, current, index) => {
|
|
37
|
+
if (arg.startsWith(`${prefix}=`)) {
|
|
38
|
+
const value = arg.slice(prefix.length + 1);
|
|
39
|
+
if (!value) options.errors.push(`${prefix} requires a value`);
|
|
40
|
+
return { value: value || current, next: index };
|
|
41
|
+
}
|
|
42
|
+
const candidate = args[index + 1];
|
|
43
|
+
if (candidate && !candidate.startsWith('--')) {
|
|
44
|
+
return { value: candidate, next: index + 1 };
|
|
45
|
+
}
|
|
46
|
+
options.errors.push(`${prefix} requires a value`);
|
|
47
|
+
return { value: current, next: index };
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
let i = 0;
|
|
51
|
+
while (i < args.length) {
|
|
52
|
+
const arg = args[i];
|
|
53
|
+
if (arg === '--dry-run') options.dryRun = true;
|
|
54
|
+
else if (arg === '--json') options.json = true;
|
|
55
|
+
else if (arg === '--from' || arg.startsWith('--from=')) {
|
|
56
|
+
const parsed = takeValue(arg, '--from', options.from, i);
|
|
57
|
+
options.from = parsed.value;
|
|
58
|
+
i = parsed.next;
|
|
59
|
+
} else if (arg === '--source' || arg.startsWith('--source=')) {
|
|
60
|
+
const parsed = takeValue(arg, '--source', options.source, i);
|
|
61
|
+
options.source = parsed.value;
|
|
62
|
+
i = parsed.next;
|
|
63
|
+
}
|
|
64
|
+
i += 1;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return options;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function directoryHasJsonl(dir, fsImpl) {
|
|
71
|
+
try {
|
|
72
|
+
return fsImpl.readdirSync(dir).some(entry => entry.endsWith('.jsonl'));
|
|
73
|
+
} catch (_err) {
|
|
74
|
+
/* intentional: unreadable directory has no usable jsonl */ // NOSONAR S2486
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Export a Dolt-only Beads store to JSONL in a temp dir via `bd export --all`,
|
|
81
|
+
* then return that dir for the loader. Defensive: a missing `bd` on PATH yields a
|
|
82
|
+
* clear, actionable error instead of a stack trace.
|
|
83
|
+
*
|
|
84
|
+
* NOTE (flagged ambiguity): the exact on-disk shape `bd export --all` emits is not
|
|
85
|
+
* pinned in this repo. We capture stdout into `<temp>/issues.jsonl`, which the
|
|
86
|
+
* loader reads. Operators with a different layout should prefer `--source <dir>`.
|
|
87
|
+
*/
|
|
88
|
+
function exportDoltStoreToJsonl(beadsDir, projectRoot, deps) {
|
|
89
|
+
const exec = deps.execFileSync || execFileSync;
|
|
90
|
+
const fsImpl = deps.fs || fs;
|
|
91
|
+
const tempDir = fsImpl.mkdtempSync(path.join(os.tmpdir(), 'forge-migrate-bd-'));
|
|
92
|
+
try {
|
|
93
|
+
const stdout = exec('bd', ['export', '--all'], {
|
|
94
|
+
cwd: projectRoot || process.cwd(),
|
|
95
|
+
encoding: 'utf8',
|
|
96
|
+
});
|
|
97
|
+
fsImpl.writeFileSync(path.join(tempDir, 'issues.jsonl'), stdout || '');
|
|
98
|
+
return { dir: tempDir, kind: 'dolt-export', temp: tempDir };
|
|
99
|
+
} catch (err) {
|
|
100
|
+
fsImpl.rmSync(tempDir, { recursive: true, force: true });
|
|
101
|
+
const message = err && err.message ? err.message : String(err);
|
|
102
|
+
if (/ENOENT|not found|not recognized|no such file/i.test(message)) {
|
|
103
|
+
return {
|
|
104
|
+
error:
|
|
105
|
+
"Beads CLI 'bd' is not on PATH — cannot export the Dolt store at " +
|
|
106
|
+
`${beadsDir}. Install bd, or pass --source <dir> with exported *.jsonl files.`,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
return { error: `bd export --all failed: ${message}` };
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Resolve the directory holding Beads JSONL the loader will read.
|
|
115
|
+
* 1. --source wins (used as given).
|
|
116
|
+
* 2. else auto-detect a `.beads/` dir containing *.jsonl.
|
|
117
|
+
* 3. else a Dolt-only `.beads/` (no jsonl) → `bd export --all` into a temp dir.
|
|
118
|
+
* 4. else nothing found.
|
|
119
|
+
*/
|
|
120
|
+
function resolveBeadsSource(options, projectRoot, deps) {
|
|
121
|
+
const fsImpl = deps.fs || fs;
|
|
122
|
+
const root = projectRoot || process.cwd();
|
|
123
|
+
|
|
124
|
+
if (options.source) {
|
|
125
|
+
const dir = path.resolve(root, options.source);
|
|
126
|
+
if (!fsImpl.existsSync(dir)) {
|
|
127
|
+
return { error: `No beads data found at --source ${dir} (directory does not exist)` };
|
|
128
|
+
}
|
|
129
|
+
return { dir, kind: 'source' };
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const beadsDir = path.join(root, '.beads');
|
|
133
|
+
if (fsImpl.existsSync(beadsDir)) {
|
|
134
|
+
if (directoryHasJsonl(beadsDir, fsImpl)) {
|
|
135
|
+
return { dir: beadsDir, kind: 'jsonl-autodetect' };
|
|
136
|
+
}
|
|
137
|
+
return exportDoltStoreToJsonl(beadsDir, root, deps);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return {
|
|
141
|
+
error:
|
|
142
|
+
'No beads data found. Looked for a .beads/ directory with *.jsonl under ' +
|
|
143
|
+
`${root}. Pass --source <dir> to point at an exported Beads store.`,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function cleanupResolved(resolved, deps) {
|
|
148
|
+
if (resolved && resolved.temp) {
|
|
149
|
+
const fsImpl = deps.fs || fs;
|
|
150
|
+
fsImpl.rmSync(resolved.temp, { recursive: true, force: true });
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function summarizeGaps(gaps) {
|
|
155
|
+
const items = Array.isArray(gaps) ? gaps : [];
|
|
156
|
+
return {
|
|
157
|
+
count: items.length,
|
|
158
|
+
items: items.map(gap => ({ field: gap.field, reason: gap.reason })),
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function renderHuman(payload) {
|
|
163
|
+
if (payload.error) {
|
|
164
|
+
return `forge migrate failed: ${payload.error}`;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const gaps = payload.gaps || { count: 0, items: [] };
|
|
168
|
+
const gapBrief = gaps.count > 0
|
|
169
|
+
? ` (${gaps.items.map(item => item.field).join(', ')})`
|
|
170
|
+
: '';
|
|
171
|
+
|
|
172
|
+
if (payload.dryRun) {
|
|
173
|
+
return [
|
|
174
|
+
`Dry run — would import beads → kernel from ${payload.source}:`,
|
|
175
|
+
` issues: ${payload.planned.issues}, comments: ${payload.planned.comments}, ` +
|
|
176
|
+
`dependencies: ${payload.planned.dependencies}, events: ${payload.planned.events ?? 0}`,
|
|
177
|
+
` gaps: ${gaps.count}${gapBrief}`,
|
|
178
|
+
' (nothing written to the kernel)',
|
|
179
|
+
].join('\n');
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const imported = payload.imported;
|
|
183
|
+
const importedEvents = imported.events || { inserted: 0, skipped: 0 };
|
|
184
|
+
return [
|
|
185
|
+
`Migrated beads → kernel from ${payload.source}:`,
|
|
186
|
+
` issues: ${imported.issues.inserted} imported, ${imported.issues.skipped} skipped`,
|
|
187
|
+
` comments: ${imported.comments.inserted} imported, ${imported.comments.skipped || 0} skipped`,
|
|
188
|
+
` dependencies: ${imported.dependencies.inserted} imported, ` +
|
|
189
|
+
`${imported.dependencies.skipped} skipped`,
|
|
190
|
+
` events: ${importedEvents.inserted} imported, ${importedEvents.skipped || 0} skipped`,
|
|
191
|
+
` gaps: ${gaps.count}${gapBrief}`,
|
|
192
|
+
].join('\n');
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function finalize(options, payload, exitCode) {
|
|
196
|
+
const output = options.json ? JSON.stringify(payload, null, 2) : renderHuman(payload);
|
|
197
|
+
const result = { ...payload, output, json: Boolean(options.json) };
|
|
198
|
+
if (payload.success === false && Number.isInteger(exitCode)) {
|
|
199
|
+
result.exitCode = exitCode;
|
|
200
|
+
}
|
|
201
|
+
return result;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Beads → Forge Kernel transport. Reuses the faithful-import spine end-to-end:
|
|
206
|
+
* loadBeadsSnapshotFromDirectory → importBeadsSnapshot → broker.importIssues.
|
|
207
|
+
*/
|
|
208
|
+
async function runBeadsMigration(options, projectRoot, opts) {
|
|
209
|
+
const now = opts._now || new Date().toISOString();
|
|
210
|
+
const deps = { execFileSync: opts._execFileSync, fs: opts._fs };
|
|
211
|
+
|
|
212
|
+
const resolved = resolveBeadsSource(options, projectRoot, deps);
|
|
213
|
+
if (resolved.error) {
|
|
214
|
+
return finalize(options, { success: false, from: 'beads', error: resolved.error }, 1);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
let snapshot;
|
|
218
|
+
try {
|
|
219
|
+
snapshot = loadBeadsSnapshotFromDirectory(resolved.dir);
|
|
220
|
+
} catch (err) {
|
|
221
|
+
cleanupResolved(resolved, deps);
|
|
222
|
+
return finalize(
|
|
223
|
+
options,
|
|
224
|
+
{ success: false, from: 'beads', source: resolved.dir, error: `Failed to read beads data: ${err.message}` },
|
|
225
|
+
1,
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (snapshot.issues.length === 0 && snapshot.comments.length === 0 && snapshot.dependencies.length === 0) {
|
|
230
|
+
cleanupResolved(resolved, deps);
|
|
231
|
+
return finalize(
|
|
232
|
+
options,
|
|
233
|
+
{ success: false, from: 'beads', source: resolved.dir, error: `No beads data found in ${resolved.dir}` },
|
|
234
|
+
1,
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
let kernel;
|
|
239
|
+
let report;
|
|
240
|
+
try {
|
|
241
|
+
({ kernel, report } = importBeadsSnapshot(snapshot, { importedAt: now }));
|
|
242
|
+
} catch (err) {
|
|
243
|
+
cleanupResolved(resolved, deps);
|
|
244
|
+
return finalize(
|
|
245
|
+
options,
|
|
246
|
+
{ success: false, from: 'beads', source: resolved.dir, error: `Failed to map beads data: ${err.message}` },
|
|
247
|
+
1,
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
const gaps = summarizeGaps(report?.gaps);
|
|
251
|
+
const planned = {
|
|
252
|
+
issues: kernel.issues.length,
|
|
253
|
+
comments: kernel.comments.length,
|
|
254
|
+
dependencies: kernel.dependencies.length,
|
|
255
|
+
events: Array.isArray(kernel.activityEvents) ? kernel.activityEvents.length : 0,
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
if (options.dryRun) {
|
|
259
|
+
cleanupResolved(resolved, deps);
|
|
260
|
+
return finalize(options, {
|
|
261
|
+
success: true,
|
|
262
|
+
from: 'beads',
|
|
263
|
+
dryRun: true,
|
|
264
|
+
source: resolved.dir,
|
|
265
|
+
sourceKind: resolved.kind,
|
|
266
|
+
planned,
|
|
267
|
+
gaps,
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
try {
|
|
272
|
+
let broker = opts._broker;
|
|
273
|
+
if (!broker) {
|
|
274
|
+
const built = await buildMigratedKernelIssueDeps({ projectRoot });
|
|
275
|
+
broker = built.kernelBroker;
|
|
276
|
+
}
|
|
277
|
+
const summary = await broker.importIssues(kernel, { now });
|
|
278
|
+
cleanupResolved(resolved, deps);
|
|
279
|
+
return finalize(options, {
|
|
280
|
+
success: true,
|
|
281
|
+
from: 'beads',
|
|
282
|
+
dryRun: false,
|
|
283
|
+
source: resolved.dir,
|
|
284
|
+
sourceKind: resolved.kind,
|
|
285
|
+
planned,
|
|
286
|
+
imported: {
|
|
287
|
+
issues: summary.issues,
|
|
288
|
+
comments: summary.comments,
|
|
289
|
+
dependencies: summary.dependencies,
|
|
290
|
+
events: summary.events,
|
|
291
|
+
},
|
|
292
|
+
gaps,
|
|
293
|
+
});
|
|
294
|
+
} catch (err) {
|
|
295
|
+
cleanupResolved(resolved, deps);
|
|
296
|
+
return finalize(
|
|
297
|
+
options,
|
|
298
|
+
{ success: false, from: 'beads', source: resolved.dir, error: `Kernel import failed: ${err.message}` },
|
|
299
|
+
1,
|
|
300
|
+
);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Detect a jsonl-backed Beads store under `<projectRoot>/.beads`. Returns the
|
|
306
|
+
* directory path when it holds *.jsonl sidecars, else null.
|
|
307
|
+
*
|
|
308
|
+
* Onboarding (forge setup/init) uses this to decide whether to auto-migrate.
|
|
309
|
+
* It deliberately never falls back to `bd export` (a Dolt-only store with no
|
|
310
|
+
* jsonl returns null), so setup stays bd-free even when Dolt is down.
|
|
311
|
+
*/
|
|
312
|
+
function detectBeadsJsonlSource(projectRoot, deps = {}) {
|
|
313
|
+
const fsImpl = deps.fs || fs;
|
|
314
|
+
const beadsDir = path.join(projectRoot || process.cwd(), '.beads');
|
|
315
|
+
return fsImpl.existsSync(beadsDir) && directoryHasJsonl(beadsDir, fsImpl)
|
|
316
|
+
? beadsDir
|
|
317
|
+
: null;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Idempotently import an existing jsonl-backed Beads store into the Kernel as
|
|
322
|
+
* part of onboarding. Reuses the exact `forge migrate --from beads` spine, so
|
|
323
|
+
* gaps are surfaced honestly and a second run inserts nothing.
|
|
324
|
+
*
|
|
325
|
+
* No-op (`{ migrated: false, reason: 'no-beads-jsonl' }`) when there is no jsonl
|
|
326
|
+
* `.beads/` present. Never requires the `bd` binary.
|
|
327
|
+
*
|
|
328
|
+
* @param {string} projectRoot
|
|
329
|
+
* @param {object} [opts] - Passed through to runBeadsMigration (_broker/_now/_fs seams).
|
|
330
|
+
* @returns {Promise<{ migrated: boolean, reason?: string, result?: object }>}
|
|
331
|
+
*/
|
|
332
|
+
async function autoMigrateBeadsIfPresent(projectRoot, opts = {}) {
|
|
333
|
+
if (!detectBeadsJsonlSource(projectRoot, { fs: opts._fs })) {
|
|
334
|
+
return { migrated: false, reason: 'no-beads-jsonl' };
|
|
335
|
+
}
|
|
336
|
+
const result = await runBeadsMigration({ from: 'beads' }, projectRoot, opts);
|
|
337
|
+
return { migrated: result.success === true, result };
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
const BEADS_MIGRATE_NUDGE =
|
|
341
|
+
'Forge could not import your Beads issues into the Kernel automatically. '
|
|
342
|
+
+ 'Run `forge migrate --from beads` to import them.';
|
|
343
|
+
|
|
344
|
+
// The "already imported" marker lives as a row in the broker's kernel_migrations
|
|
345
|
+
// ledger (created unconditionally by broker.initialize()), NOT as a file beside the
|
|
346
|
+
// DB. Keeping it INSIDE the kernel DB means a DB reset drops the marker and the import
|
|
347
|
+
// self-heals; a file sentinel would survive the reset and leave issues dark forever.
|
|
348
|
+
// The id uses underscores so it passes the ledger charset /^[0-9a-z_]+$/i and stays
|
|
349
|
+
// clear of the tokens the D20 retirement audit counts (see release-readiness.js).
|
|
350
|
+
const IMPORT_MARKER_TABLE = 'kernel_migrations';
|
|
351
|
+
const IMPORT_MARKER_ID = 'data_import_beads_jsonl';
|
|
352
|
+
|
|
353
|
+
// Read/record the import marker via the DRIVER (the broker exposes no raw SQL). Both
|
|
354
|
+
// calls are param-less (raw SQL + config); the id is a hardcoded constant and appliedAt
|
|
355
|
+
// is an ISO string, so interpolation is injection-safe — mirrors broker.recordMigrationSql.
|
|
356
|
+
async function importMarkerPresent(driver, config) {
|
|
357
|
+
if (!driver || typeof driver.queryAll !== 'function') {
|
|
358
|
+
return false;
|
|
359
|
+
}
|
|
360
|
+
try {
|
|
361
|
+
const rows = await driver.queryAll(
|
|
362
|
+
`SELECT 1 FROM ${IMPORT_MARKER_TABLE} WHERE id = '${IMPORT_MARKER_ID}' LIMIT 1;`,
|
|
363
|
+
config,
|
|
364
|
+
);
|
|
365
|
+
return Array.isArray(rows) && rows.length > 0;
|
|
366
|
+
} catch {
|
|
367
|
+
// Ledger table absent / driver unavailable → treat as not-yet-imported.
|
|
368
|
+
return false;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
async function recordImportMarker(driver, config, appliedAt) {
|
|
373
|
+
if (!driver || typeof driver.exec !== 'function') {
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
try {
|
|
377
|
+
await driver.exec(
|
|
378
|
+
`INSERT OR IGNORE INTO ${IMPORT_MARKER_TABLE} (id, applied_at) VALUES ('${IMPORT_MARKER_ID}', '${appliedAt}');`,
|
|
379
|
+
config,
|
|
380
|
+
);
|
|
381
|
+
} catch {
|
|
382
|
+
// Best-effort marker; never break the command we ride on.
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* First-use safety net for the kernel default backend. Onboarding auto-migrate runs
|
|
388
|
+
* only from `forge setup`/`init`; an existing repo whose user merely upgrades forge
|
|
389
|
+
* reads an EMPTY kernel on the first issue command, so their existing Beads issues
|
|
390
|
+
* appear to vanish. This imports them ONCE — gated by an in-DB marker row in the
|
|
391
|
+
* kernel_migrations ledger, so the gate shares the DB lifecycle and a DB reset
|
|
392
|
+
* self-heals — idempotently, announcing on stderr only so `--json` stdout stays a pure
|
|
393
|
+
* contract. NEVER throws: the safety net must never break the command it rides on.
|
|
394
|
+
*
|
|
395
|
+
* Only a jsonl-backed store is auto-imported (the migration binary is never shelled).
|
|
396
|
+
* A store with no jsonl export is skipped silently. The marker is SUCCESS-ONLY: a
|
|
397
|
+
* failed import records nothing and is retried (with a nudge) on the next kernel
|
|
398
|
+
* command, so transient failures and DB resets self-heal. Imported issues land in the
|
|
399
|
+
* read model directly and arrive UNCLAIMED — they surface via `forge issue ready`.
|
|
400
|
+
*
|
|
401
|
+
* @param {object} params
|
|
402
|
+
* @param {string} params.projectRoot
|
|
403
|
+
* @param {string} params.databasePath - kernel DB path (used for the driver config).
|
|
404
|
+
* @param {object} params.broker - an initialized kernel broker to import through.
|
|
405
|
+
* @param {object} params.driver - kernel driver, for the ledger marker read/write.
|
|
406
|
+
* @param {object} [deps] - { fs, warn, now, driver, _fs } seams for tests.
|
|
407
|
+
* @returns {Promise<{action:'migrated'|'nudge'|'skip', reason?:string, inserted?:number}>}
|
|
408
|
+
*/
|
|
409
|
+
async function autoMigrateBeadsAtRuntime({ projectRoot, databasePath, broker, driver } = {}, deps = {}) {
|
|
410
|
+
const fsImpl = deps.fs || fs;
|
|
411
|
+
const warn = deps.warn || ((msg) => process.stderr.write(`${msg}\n`));
|
|
412
|
+
const appliedAt = deps.now || new Date().toISOString();
|
|
413
|
+
const markerDriver = deps.driver || driver;
|
|
414
|
+
const markerConfig = databasePath ? { databasePath } : {};
|
|
415
|
+
try {
|
|
416
|
+
if (!databasePath) {
|
|
417
|
+
return { action: 'skip', reason: 'no-db-path' };
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// One-time gate: the import already ran on THIS kernel DB (the marker dies with the
|
|
421
|
+
// DB, so deleting kernel.sqlite to reset correctly re-triggers the import).
|
|
422
|
+
if (await importMarkerPresent(markerDriver, markerConfig)) {
|
|
423
|
+
return { action: 'skip', reason: 'already-imported' };
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
// Only a jsonl export can be imported automatically; anything else is skipped
|
|
427
|
+
// silently (an empty or export-less store must never trigger a false nudge).
|
|
428
|
+
if (!detectBeadsJsonlSource(projectRoot, { fs: fsImpl })) {
|
|
429
|
+
return { action: 'skip', reason: 'no-jsonl' };
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// Import (idempotent) through the already-initialized broker.
|
|
433
|
+
let outcome;
|
|
434
|
+
try {
|
|
435
|
+
outcome = await autoMigrateBeadsIfPresent(projectRoot, { _broker: broker, _now: deps.now, _fs: deps._fs });
|
|
436
|
+
} catch (err) {
|
|
437
|
+
outcome = { migrated: false, result: { success: false, error: err.message } };
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
if (outcome.migrated) {
|
|
441
|
+
const inserted = outcome.result?.imported?.issues?.inserted ?? 0;
|
|
442
|
+
const skipped = outcome.result?.imported?.issues?.skipped ?? 0;
|
|
443
|
+
// Record the marker LAST so a concurrent first run is a benign idempotent skip.
|
|
444
|
+
await recordImportMarker(markerDriver, markerConfig, appliedAt);
|
|
445
|
+
if (inserted > 0) {
|
|
446
|
+
warn(`Forge: imported ${inserted} Beads issue(s) into the Kernel — your issues are here, not lost.`);
|
|
447
|
+
}
|
|
448
|
+
return { action: 'migrated', inserted, skipped };
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
// Success-only marker: a failed import records nothing and re-nudges next run,
|
|
452
|
+
// so a transient failure self-heals once the underlying problem is resolved.
|
|
453
|
+
const error = outcome.result?.error || outcome.reason || 'unknown error';
|
|
454
|
+
warn(BEADS_MIGRATE_NUDGE);
|
|
455
|
+
return { action: 'nudge', reason: 'migrate-failed', error };
|
|
456
|
+
} catch (err) {
|
|
457
|
+
// Absolutely never break the command we ride on.
|
|
458
|
+
return { action: 'skip', reason: 'error', error: err.message };
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
module.exports = {
|
|
463
|
+
name: 'migrate',
|
|
464
|
+
description: 'Migrate a Beads issue store into the Forge Kernel with --from beads, '
|
|
465
|
+
+ 'or preview the v2→v3 migration (preview only — --dry-run required; applying it is not yet available)',
|
|
466
|
+
detectBeadsJsonlSource,
|
|
467
|
+
autoMigrateBeadsIfPresent,
|
|
468
|
+
autoMigrateBeadsAtRuntime,
|
|
469
|
+
usage: 'forge migrate --from beads [--dry-run] [--source <dir>] [--json]',
|
|
470
|
+
flags: {
|
|
471
|
+
'--from <source>': 'Migration source. Use "beads" to import a Beads issue store into the Kernel.',
|
|
472
|
+
'--source <dir>': 'Directory of exported beads *.jsonl files (defaults to auto-detecting .beads/). '
|
|
473
|
+
+ 'A split .beads layout is tolerated: files missing from the given dir are also read from a '
|
|
474
|
+
+ 'backup/ subdir and the parent dir, so events (under .beads/backup/) and interactions '
|
|
475
|
+
+ '(at .beads/interactions.jsonl) are both picked up regardless of which you point at.',
|
|
476
|
+
'--dry-run': 'Read + map only; print what WOULD be imported without writing to the Kernel. '
|
|
477
|
+
+ 'Without --from, this is the only supported mode (preview only).',
|
|
478
|
+
'--json': 'Emit a structured JSON result (counts + gaps + dryRun flag).',
|
|
479
|
+
'--fixture-corpus': 'v2→v3 preview only: also dry-run the source-tree v2 fixture corpus when available.',
|
|
480
|
+
},
|
|
481
|
+
|
|
482
|
+
async handler(args, flags, projectRoot, opts = {}) {
|
|
483
|
+
const options = parseMigrateOptions(args, flags);
|
|
484
|
+
if (options.errors.length > 0) {
|
|
485
|
+
return finalize(options, { success: false, error: options.errors.join('; ') }, 1);
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
// New beads → kernel transport, gated on `--from beads`. Any other (or absent)
|
|
489
|
+
// `--from` falls through to the pre-existing v2→v3 dry-run PoC so its contract
|
|
490
|
+
// (and tests) stay intact.
|
|
491
|
+
if (options.from) {
|
|
492
|
+
if (options.from === 'beads') {
|
|
493
|
+
return runBeadsMigration(options, projectRoot, opts);
|
|
494
|
+
}
|
|
495
|
+
return finalize(
|
|
496
|
+
options,
|
|
497
|
+
{ success: false, error: `Unsupported migration source '${options.from}'. Supported sources: beads.` },
|
|
498
|
+
1,
|
|
499
|
+
);
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
// --- legacy v2 → v3 dry-run PoC (unchanged) ---
|
|
503
|
+
const dryRun = flags?.dryRun === true || hasArg(args, '--dry-run');
|
|
504
|
+
if (!dryRun) {
|
|
505
|
+
return {
|
|
506
|
+
success: false,
|
|
507
|
+
error: "forge migrate currently supports preview only. Run 'forge migrate --dry-run' "
|
|
508
|
+
+ 'to see what would change; applying migrations is not yet available.',
|
|
509
|
+
};
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
const report = buildMigrationDryRunReport(projectRoot, {
|
|
513
|
+
fixtureCorpus: hasArg(args, '--fixture-corpus'),
|
|
514
|
+
});
|
|
515
|
+
const output = renderMigrationDryRunReport(report);
|
|
516
|
+
|
|
517
|
+
return {
|
|
518
|
+
success: report.ok,
|
|
519
|
+
output,
|
|
520
|
+
error: report.ok ? undefined : output,
|
|
521
|
+
};
|
|
522
|
+
},
|
|
523
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { scaffoldAdapter } = require('../adapter-cli');
|
|
4
|
+
|
|
5
|
+
module.exports = {
|
|
6
|
+
name: 'new',
|
|
7
|
+
description: 'Create Forge extension scaffolds',
|
|
8
|
+
usage: 'forge new adapter <name> --kind=review --template=greptile',
|
|
9
|
+
async handler(args, _flags, projectRoot) {
|
|
10
|
+
return scaffoldAdapter(args, projectRoot);
|
|
11
|
+
},
|
|
12
|
+
};
|