forge-workflow 0.0.10 → 0.1.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +150 -61
- package/CHANGELOG.md +681 -0
- package/CLAUDE.md +9 -118
- 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 +461 -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/forge/TOOLCHAIN.md +670 -0
- 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/{TOOLCHAIN.md → reference/TOOLCHAIN.md} +62 -47
- 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 +121 -0
- package/lib/beads-sync-scaffold.js +25 -101
- package/lib/codex-skills.js +51 -1
- package/lib/commands/_issue.js +741 -77
- 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 +17 -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 +0 -1
- 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 +838 -972
- 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 +158 -21
- package/lib/commands/sync.js +34 -46
- package/lib/commands/team.js +4 -1
- package/lib/commands/test.js +43 -27
- package/lib/commands/update.js +2 -2
- package/lib/commands/upgrade.js +47 -0
- package/lib/commands/validate.js +43 -18
- package/lib/commands/worktree.js +307 -100
- 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 +2 -2
- package/lib/deprecated-sync-cleanup.js +362 -0
- package/lib/detect-agent.js +2 -28
- package/lib/detect-worktree.js +35 -9
- 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 +382 -11
- 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/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 +3 -2
- 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 +99 -497
- 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 +241 -20
- 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/scoring.js +17 -3
- package/lib/status/beads-snapshot.js +45 -2
- package/lib/status/presenter.js +169 -18
- 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 +296 -47
- package/lib/workflow/stage-transition.js +115 -0
- package/lib/workflow/stages.js +30 -6
- package/lib/workflow/state-manager.js +11 -22
- package/lib/workflow/state.js +23 -1
- package/lib/workflow-profiles.js +17 -5
- package/package.json +37 -35
- 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 +81 -57
- package/scripts/beads-upgrade-smoke.sh +24 -3
- 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.sh +22 -3
- 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 +49 -84
- 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 +50 -83
- 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/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 +21 -94
- package/scripts/protected-state-check.js +104 -0
- package/scripts/smart-status.sh +60 -57
- 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-utils.sh +3 -0
- package/scripts/test-ci-shard.js +13 -6
- package/scripts/test.js +95 -12
- 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} +44 -50
- 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} +132 -157
- 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/{.github/prompts/ship.prompt.md → skills/ship/SKILL.md} +81 -45
- 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/{.github/prompts/status.prompt.md → skills/status/SKILL.md} +20 -10
- 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/verify.prompt.md +0 -271
- package/.github/workflows/beads-to-github.yml +0 -89
- package/.github/workflows/github-to-beads.yml +0 -100
- 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 -281
- 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-bootstrap.js +0 -225
- package/lib/beads-health-check.js +0 -188
- package/lib/commands/commands-reset.js +0 -147
- package/opencode.json +0 -67
- package/scripts/beads-context.test.js +0 -584
- 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 -356
- package/scripts/github-beads-sync/label-mapper.mjs +0 -54
- package/scripts/github-beads-sync/mapping.mjs +0 -132
- package/scripts/github-beads-sync/reverse-sync-cli.mjs +0 -31
- package/scripts/github-beads-sync/reverse-sync.mjs +0 -162
- 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
package/lib/forge-issues.js
CHANGED
|
@@ -4,14 +4,65 @@ const { spawn } = require('node:child_process');
|
|
|
4
4
|
const { existsSync } = require('node:fs');
|
|
5
5
|
const path = require('node:path');
|
|
6
6
|
const { isBeadsInitialized } = require('./beads-setup');
|
|
7
|
+
const { BeadsIssueAdapter } = require('./adapters/beads-issue-adapter');
|
|
8
|
+
const { KernelIssueAdapter } = require('./adapters/kernel-issue-adapter');
|
|
7
9
|
const { createGitHubProjectionPlan } = require('./issue-sync/project-github');
|
|
10
|
+
const { createLocalBroker, buildLocalBrokerConfig } = require('./kernel/broker');
|
|
11
|
+
const { createBuiltinSQLiteDriver } = require('./kernel/sqlite-driver');
|
|
12
|
+
const { shouldUseKernelBroker } = require('./issue-backend');
|
|
13
|
+
const { detectWorktree } = require('./detect-worktree');
|
|
14
|
+
const { DEFAULT_LEASE_TTL_MS } = require('./kernel/lease-enforcer');
|
|
8
15
|
|
|
9
16
|
const OPERATION_TO_BD = {
|
|
10
17
|
create: 'create',
|
|
11
18
|
list: 'list',
|
|
19
|
+
ready: 'ready',
|
|
12
20
|
show: 'show',
|
|
21
|
+
search: 'search',
|
|
22
|
+
stats: 'status',
|
|
13
23
|
close: 'close',
|
|
14
24
|
update: 'update',
|
|
25
|
+
comment: 'comments',
|
|
26
|
+
// KAP-7/KAP-12 derived reads: identity passthroughs to the matching bd subcommand.
|
|
27
|
+
// Routed here (not in _issue.js) so the issue command surface carries no bd args.
|
|
28
|
+
blocked: 'blocked',
|
|
29
|
+
stale: 'stale',
|
|
30
|
+
orphans: 'orphans',
|
|
31
|
+
lint: 'lint',
|
|
32
|
+
// Epic support: identity passthrough to `bd children` for the beads opt-out. The
|
|
33
|
+
// kernel default emits the richer `issue.children` rollup; this passthrough only
|
|
34
|
+
// keeps the beads path from a null-deref when children is invoked under --issue-backend
|
|
35
|
+
// beads (the bd output shape differs and carries no rollup).
|
|
36
|
+
children: 'children',
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
// Beads has no verified release operation; the claim lease lives only in the Kernel
|
|
40
|
+
// backend. Surfaced as an explicit error so `forge release <id>` on the beads opt-out
|
|
41
|
+
// fails loudly instead of silently mapping to an unrelated bd subcommand.
|
|
42
|
+
const BEADS_RELEASE_KERNEL_ONLY_ERROR =
|
|
43
|
+
'forge release <id> is defined for the Kernel issue backend; Beads passthrough has no verified release operation.';
|
|
44
|
+
|
|
45
|
+
// `bd children` emits a different payload (no rollup) than the kernel
|
|
46
|
+
// `issue.children` envelope this command promises. Reject the beads backend
|
|
47
|
+
// explicitly instead of silently proxying a contract-violating shape.
|
|
48
|
+
const BEADS_CHILDREN_KERNEL_ONLY_ERROR =
|
|
49
|
+
'forge issue children is defined for the Kernel issue backend; Beads passthrough has no equivalent issue.children rollup.';
|
|
50
|
+
|
|
51
|
+
// Lease ownership lives only in the Kernel backend (kernel_claims); Beads has no
|
|
52
|
+
// verified lease to check. Reject the beads path explicitly so `forge issue owns <id>`
|
|
53
|
+
// fails loudly instead of mapping to an unrelated/absent Beads subcommand.
|
|
54
|
+
const BEADS_OWNS_KERNEL_ONLY_ERROR =
|
|
55
|
+
'forge issue owns <id> is defined for the Kernel issue backend; Beads passthrough has no lease-ownership verification.';
|
|
56
|
+
|
|
57
|
+
// Active leases live only in the Kernel backend (kernel_claims); Beads has no lease
|
|
58
|
+
// table to enumerate. Reject the beads path explicitly so `forge claims` fails loudly
|
|
59
|
+
// instead of mapping to an absent Beads subcommand.
|
|
60
|
+
const BEADS_CLAIMS_KERNEL_ONLY_ERROR =
|
|
61
|
+
'forge claims is defined for the Kernel issue backend; Beads passthrough has no active-lease listing.';
|
|
62
|
+
|
|
63
|
+
const OPERATION_METHOD_ALIASES = {
|
|
64
|
+
'dep.add': 'depAdd',
|
|
65
|
+
'dep.remove': 'depRemove',
|
|
15
66
|
};
|
|
16
67
|
|
|
17
68
|
function getSpawnOptions(projectRoot) {
|
|
@@ -117,6 +168,29 @@ function getCommandErrorMessage(result) {
|
|
|
117
168
|
}
|
|
118
169
|
|
|
119
170
|
function buildBdArgs(operation, args) {
|
|
171
|
+
if (operation === 'comment') {
|
|
172
|
+
return ['comments', 'add', ...args];
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// De-bead parity: `forge claim <id>` maps to bd `update <id> --claim`. The
|
|
176
|
+
// translation lived in _issue.js; it now lives in the beads layer so the issue
|
|
177
|
+
// command surface stays free of bd argv. The issue id is the first positional.
|
|
178
|
+
if (operation === 'claim') {
|
|
179
|
+
const [issueId, ...rest] = args;
|
|
180
|
+
if (!issueId) {
|
|
181
|
+
return { error: 'Missing issue id. Usage: forge claim <id> [bd-update-flags]' };
|
|
182
|
+
}
|
|
183
|
+
return ['update', issueId, '--claim', ...rest];
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (operation === 'dep.add') {
|
|
187
|
+
return ['dep', 'add', ...args];
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (operation === 'dep.remove') {
|
|
191
|
+
return ['dep', 'remove', ...args];
|
|
192
|
+
}
|
|
193
|
+
|
|
120
194
|
const bdCommand = OPERATION_TO_BD[operation];
|
|
121
195
|
if (!bdCommand) {
|
|
122
196
|
return null;
|
|
@@ -134,6 +208,10 @@ function shouldCaptureOutput(operation, args, deps = {}) {
|
|
|
134
208
|
return true;
|
|
135
209
|
}
|
|
136
210
|
|
|
211
|
+
if (args.includes('--json')) {
|
|
212
|
+
return true;
|
|
213
|
+
}
|
|
214
|
+
|
|
137
215
|
return operation === 'update' || operation === 'show';
|
|
138
216
|
}
|
|
139
217
|
|
|
@@ -192,6 +270,31 @@ async function runBdCommand(operation, args, projectRoot, deps = {}) {
|
|
|
192
270
|
}
|
|
193
271
|
|
|
194
272
|
async function runBeadsOperation(operation, args, context, deps) {
|
|
273
|
+
// Beads has no verified release; fail before the init check so the kernel-only
|
|
274
|
+
// contract is reported even in an uninitialized repo (parity with the old
|
|
275
|
+
// _issue.js release guard, which short-circuited on the beads path).
|
|
276
|
+
if (operation === 'release' && !isHelpInvocation(args)) {
|
|
277
|
+
return { success: false, error: BEADS_RELEASE_KERNEL_ONLY_ERROR };
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// `children` only has a verified shape on the kernel backend (the
|
|
281
|
+
// issue.children rollup); fail loudly rather than proxy `bd children`.
|
|
282
|
+
if (operation === 'children' && !isHelpInvocation(args)) {
|
|
283
|
+
return { success: false, error: BEADS_CHILDREN_KERNEL_ONLY_ERROR };
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// `owns` verifies a Kernel lease; Beads has no such lease. Fail loudly rather
|
|
287
|
+
// than silently proxy to a non-existent Beads subcommand.
|
|
288
|
+
if (operation === 'owns' && !isHelpInvocation(args)) {
|
|
289
|
+
return { success: false, error: BEADS_OWNS_KERNEL_ONLY_ERROR };
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// `claims` enumerates the Kernel lease table; Beads has none. Fail loudly rather
|
|
293
|
+
// than silently proxy to a non-existent Beads subcommand.
|
|
294
|
+
if (operation === 'claims' && !isHelpInvocation(args)) {
|
|
295
|
+
return { success: false, error: BEADS_CLAIMS_KERNEL_ONLY_ERROR };
|
|
296
|
+
}
|
|
297
|
+
|
|
195
298
|
const checkInit = deps.isBeadsInitialized || isBeadsInitialized;
|
|
196
299
|
if (!isHelpInvocation(args) && !checkInit(context.projectRoot)) {
|
|
197
300
|
return {
|
|
@@ -199,10 +302,21 @@ async function runBeadsOperation(operation, args, context, deps) {
|
|
|
199
302
|
error: 'Beads is not initialized in this project. Run forge setup before using forge issues.',
|
|
200
303
|
};
|
|
201
304
|
}
|
|
202
|
-
|
|
305
|
+
|
|
306
|
+
const bdArgs = buildBdArgs(operation, args);
|
|
307
|
+
// A non-array result is a translation error (e.g. claim with no issue id).
|
|
308
|
+
if (!Array.isArray(bdArgs)) {
|
|
309
|
+
return { success: false, error: bdArgs.error };
|
|
310
|
+
}
|
|
311
|
+
// The reported operation is the logical operation name (comment -> 'comment',
|
|
312
|
+
// show -> 'show'), NOT the bd subcommand argv[0] (which is 'comments'/'status').
|
|
313
|
+
// The one exception is claim: it maps to `update <id> --claim`, and the historical
|
|
314
|
+
// _issue.js beads path reported operation 'update', so preserve that here.
|
|
315
|
+
const reportedOperation = operation === 'claim' ? 'update' : operation;
|
|
316
|
+
const runCommand = deps.runBdCommand || ((cmdArgs, projectRoot) => runBdCommand(operation, cmdArgs, projectRoot, deps));
|
|
203
317
|
|
|
204
318
|
try {
|
|
205
|
-
const result = await runCommand(
|
|
319
|
+
const result = await runCommand(bdArgs, context.projectRoot);
|
|
206
320
|
const stdout = normalizeExecOutput(result?.stdout);
|
|
207
321
|
const stderr = normalizeExecOutput(result?.stderr);
|
|
208
322
|
const combinedOutput = [stdout, stderr].filter(Boolean).join('\n');
|
|
@@ -230,7 +344,7 @@ async function runBeadsOperation(operation, args, context, deps) {
|
|
|
230
344
|
|
|
231
345
|
return {
|
|
232
346
|
success: true,
|
|
233
|
-
operation,
|
|
347
|
+
operation: reportedOperation,
|
|
234
348
|
output: stdout,
|
|
235
349
|
stderr,
|
|
236
350
|
};
|
|
@@ -243,21 +357,112 @@ async function runBeadsOperation(operation, args, context, deps) {
|
|
|
243
357
|
}
|
|
244
358
|
|
|
245
359
|
function createBeadsIssueBackend(deps = {}) {
|
|
360
|
+
return new BeadsIssueAdapter({
|
|
361
|
+
runBeadsOperation: (operation, args, context, contextDeps = {}) =>
|
|
362
|
+
runBeadsOperation(operation, args, context, {
|
|
363
|
+
...deps,
|
|
364
|
+
...contextDeps,
|
|
365
|
+
}),
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// Build the default local broker for the CLI path. createLocalBroker does NOT
|
|
370
|
+
// construct a driver on its own (`const driver = options.driver`), so when no
|
|
371
|
+
// driver is injected we build a builtin SQLite driver pointed at the resolved
|
|
372
|
+
// kernel DB path. Without this, broker.initialize()/runIssueOperation would throw
|
|
373
|
+
// on a missing `exec` method before ever reaching the "no tables" case.
|
|
374
|
+
function createDefaultKernelBroker(brokerContext, deps) {
|
|
375
|
+
let driver = deps.kernelDriver;
|
|
376
|
+
if (!driver) {
|
|
377
|
+
const config = buildLocalBrokerConfig({
|
|
378
|
+
projectRoot: brokerContext.projectRoot,
|
|
379
|
+
gitCommonDir: deps.gitCommonDir,
|
|
380
|
+
databasePath: deps.kernelDatabasePath,
|
|
381
|
+
execFileSync: deps.execFileSync,
|
|
382
|
+
});
|
|
383
|
+
driver = createBuiltinSQLiteDriver({ databasePath: config.databasePath });
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
return createLocalBroker({
|
|
387
|
+
projectRoot: brokerContext.projectRoot,
|
|
388
|
+
gitCommonDir: deps.gitCommonDir,
|
|
389
|
+
databasePath: deps.kernelDatabasePath,
|
|
390
|
+
driver,
|
|
391
|
+
execFileSync: deps.execFileSync,
|
|
392
|
+
// Pass-through for the D19 default-on filesystem gate (fires in the broker's
|
|
393
|
+
// getConfig chokepoint). Forwarding it lets callers/tests inject a deterministic
|
|
394
|
+
// classifier instead of paying the real Windows drive probe per per-op broker.
|
|
395
|
+
classifyFilesystem: deps.classifyFilesystem,
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// Symbol marker for the memoized lazy-init promise. Stored ON the broker object
|
|
400
|
+
// so that re-wrapping the SAME broker instance (e.g. a long-lived injected broker
|
|
401
|
+
// hit by several runIssueOperation calls) still initializes exactly once.
|
|
402
|
+
const LAZY_INIT_PROMISE = Symbol('forgeKernelLazyInitPromise');
|
|
403
|
+
|
|
404
|
+
// Wrap a broker so the kernel DB is migrated lazily and exactly once before the
|
|
405
|
+
// first issue operation. initialize() applies migrations idempotently
|
|
406
|
+
// (CREATE TABLE IF NOT EXISTS), so a fresh DB gains its tables automatically.
|
|
407
|
+
// Brokers without an initialize() method (e.g. test fakes) pass through untouched.
|
|
408
|
+
function withLazyKernelInit(broker) {
|
|
409
|
+
if (!broker || typeof broker.initialize !== 'function') {
|
|
410
|
+
return broker;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
const ensureInitialized = () => {
|
|
414
|
+
if (!broker[LAZY_INIT_PROMISE]) {
|
|
415
|
+
broker[LAZY_INIT_PROMISE] = Promise.resolve(broker.initialize()).catch((error) => {
|
|
416
|
+
// Reset so a transient init failure can be retried on the next op rather
|
|
417
|
+
// than permanently poisoning the broker with a rejected cached promise.
|
|
418
|
+
broker[LAZY_INIT_PROMISE] = null;
|
|
419
|
+
throw error;
|
|
420
|
+
});
|
|
421
|
+
}
|
|
422
|
+
return broker[LAZY_INIT_PROMISE];
|
|
423
|
+
};
|
|
424
|
+
|
|
425
|
+
// Delegate explicitly rather than spreading `...broker`: a spread copies only the
|
|
426
|
+
// broker's OWN enumerable props, so a future class-based broker would silently
|
|
427
|
+
// lose its prototype methods. The adapter only ever calls runIssueOperation (plus
|
|
428
|
+
// optional initialize), so forwarding exactly those two is the complete surface.
|
|
246
429
|
return {
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
430
|
+
async initialize() {
|
|
431
|
+
return ensureInitialized();
|
|
432
|
+
},
|
|
433
|
+
async runIssueOperation(operation, args = [], context = {}) {
|
|
434
|
+
await ensureInitialized();
|
|
435
|
+
return broker.runIssueOperation(operation, args, context);
|
|
436
|
+
},
|
|
252
437
|
};
|
|
253
438
|
}
|
|
254
439
|
|
|
440
|
+
function createKernelIssueBackend(context = {}) {
|
|
441
|
+
const deps = context.deps || {};
|
|
442
|
+
const createBroker = deps.createKernelBroker
|
|
443
|
+
|| ((brokerContext) => createDefaultKernelBroker(brokerContext, deps));
|
|
444
|
+
|
|
445
|
+
const broker = deps.kernelBroker || createBroker(context);
|
|
446
|
+
|
|
447
|
+
return new KernelIssueAdapter({
|
|
448
|
+
broker: withLazyKernelInit(broker),
|
|
449
|
+
});
|
|
450
|
+
}
|
|
451
|
+
|
|
255
452
|
function createIssueService({ backend } = {}) {
|
|
256
|
-
|
|
453
|
+
// Default issue backend is now the Kernel. Beads is reachable opt-OUT via the
|
|
454
|
+
// selector (--issue-backend beads / FORGE_ISSUE_BACKEND=beads / config), which
|
|
455
|
+
// resolves to an explicit `backend` upstream in runIssueOperation. This no-arg
|
|
456
|
+
// fallback fires only for a no-backend direct call to createIssueService — the
|
|
457
|
+
// CLI path always threads an explicit backend through resolveCommandOpts.
|
|
458
|
+
const resolvedBackend = backend || createKernelIssueBackend();
|
|
257
459
|
|
|
258
460
|
return {
|
|
259
461
|
async run(operation, args = [], context = {}) {
|
|
260
|
-
const
|
|
462
|
+
const methodName = operation === 'show' && typeof resolvedBackend?.show !== 'function'
|
|
463
|
+
? 'read'
|
|
464
|
+
: OPERATION_METHOD_ALIASES[operation] || operation;
|
|
465
|
+
const method = resolvedBackend?.[methodName];
|
|
261
466
|
if (typeof method !== 'function') {
|
|
262
467
|
return {
|
|
263
468
|
success: false,
|
|
@@ -270,8 +475,142 @@ function createIssueService({ backend } = {}) {
|
|
|
270
475
|
};
|
|
271
476
|
}
|
|
272
477
|
|
|
478
|
+
// Resolve a distinct per-agent actor for Kernel issue mutations. The Kernel keys a
|
|
479
|
+
// claim's idempotency on `claim.create:<issue_id>:<actor>` (lib/kernel/broker.js
|
|
480
|
+
// buildClaimMutationEvent), so two concurrent agents that both fall back to the 'forge'
|
|
481
|
+
// default collide on ONE key: the 2nd claim replays as an idempotent duplicate (ok:true)
|
|
482
|
+
// instead of reaching the lease-conflict path, so a genuine loser is told it won
|
|
483
|
+
// (kernel d71a824b). An explicit per-agent identity produces a distinct key so the
|
|
484
|
+
// second claimant reaches the claim_conflict guard.
|
|
485
|
+
//
|
|
486
|
+
// Precedence: FORGE_ACTOR (explicit per-agent id) -> FORGE_SESSION_ID (stable session id)
|
|
487
|
+
// -> undefined. Returning undefined is deliberate: buildIssueMutationEvent applies
|
|
488
|
+
// `context.actor || 'forge'`, so a no-env caller (the main repo, existing tests) is
|
|
489
|
+
// byte-identical to before — only an explicitly-distinct identity changes behavior.
|
|
490
|
+
// A git-worktree-derived identity is a deferred follow-up: it would change the default
|
|
491
|
+
// actor in every worktree checkout, so it must be validated against the suite first.
|
|
492
|
+
function resolveIssueActor(env = {}) {
|
|
493
|
+
const source = env || {};
|
|
494
|
+
const explicit = typeof source.FORGE_ACTOR === 'string' ? source.FORGE_ACTOR.trim() : '';
|
|
495
|
+
if (explicit) return explicit;
|
|
496
|
+
const session = typeof source.FORGE_SESSION_ID === 'string' ? source.FORGE_SESSION_ID.trim() : '';
|
|
497
|
+
if (session) return session;
|
|
498
|
+
return undefined;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
// Resolve the worktree_id stamped onto a claim lease (kernel d71a824b) so the dashboard
|
|
502
|
+
// can show WHICH worktree holds a lease (the kernel DB is shared across all worktrees of
|
|
503
|
+
// a repo). Precedence: FORGE_WORKTREE_ID (explicit override) -> the current worktree's
|
|
504
|
+
// directory name (git-common-dir/registry-derived via detectWorktree) -> undefined.
|
|
505
|
+
// Best-effort: any failure (not a git repo, git unavailable) yields undefined, so the
|
|
506
|
+
// lease's worktree_id is simply null — never a thrown error on the claim path.
|
|
507
|
+
function resolveWorktreeId(projectRoot, env = {}, deps = {}) {
|
|
508
|
+
const override = typeof env.FORGE_WORKTREE_ID === 'string' ? env.FORGE_WORKTREE_ID.trim() : '';
|
|
509
|
+
if (override) return override;
|
|
510
|
+
try {
|
|
511
|
+
const detect = deps.detectWorktree || detectWorktree;
|
|
512
|
+
const info = detect(projectRoot);
|
|
513
|
+
if (info && typeof info.currentWorktree === 'string' && info.currentWorktree) {
|
|
514
|
+
return path.basename(info.currentWorktree);
|
|
515
|
+
}
|
|
516
|
+
} catch {
|
|
517
|
+
// best-effort: worktree_id is an optional presence signal, never a hard failure
|
|
518
|
+
}
|
|
519
|
+
return undefined;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
// Resolve the lease TTL (ms) applied to a claim when the caller pins no explicit
|
|
523
|
+
// --expires. FORGE_LEASE_TTL_MS overrides the DEFAULT_LEASE_TTL_MS constant; an explicit
|
|
524
|
+
// 0 / negative / non-numeric value is an OPT-OUT (returns null → never-expiring lease,
|
|
525
|
+
// the historical default), preserving the null-expiry capability from the CLI.
|
|
526
|
+
function resolveLeaseTtlMs(env = {}) {
|
|
527
|
+
const raw = env.FORGE_LEASE_TTL_MS;
|
|
528
|
+
if (raw === undefined || raw === null || String(raw).trim() === '') return DEFAULT_LEASE_TTL_MS;
|
|
529
|
+
const ttl = Number(raw);
|
|
530
|
+
if (!Number.isFinite(ttl) || ttl <= 0) return null;
|
|
531
|
+
return ttl;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
// Graceful Beads→Kernel fallback (issue 7f09ae93). When the resolved backend is
|
|
535
|
+
// Beads but Beads is NOT initialized in this project AND a Forge Kernel store
|
|
536
|
+
// already exists at `<gitCommonDir>/forge/kernel.sqlite`, route the operation to
|
|
537
|
+
// the kernel instead of dead-ending on "Beads is not initialized". This unblocks
|
|
538
|
+
// upgraders whose repo predates the kernel default (or whose global `forge` binary
|
|
539
|
+
// is stale and still resolves Beads): the kernel is the correct default and already
|
|
540
|
+
// holds their issues. Returns the resolved kernel paths when the fallback applies,
|
|
541
|
+
// or null to keep the Beads path (so a genuine no-store repo still gets the Beads
|
|
542
|
+
// not-initialized error). Path resolution reuses buildLocalBrokerConfig so the probed
|
|
543
|
+
// location matches exactly what the broker would open. Never throws.
|
|
544
|
+
function resolveBeadsFallbackToKernel(projectRoot, deps = {}) {
|
|
545
|
+
const checkInit = deps.isBeadsInitialized || isBeadsInitialized;
|
|
546
|
+
if (checkInit(projectRoot)) {
|
|
547
|
+
// Beads is initialized — honor the explicit Beads selection, no fallback.
|
|
548
|
+
return null;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
let config;
|
|
552
|
+
try {
|
|
553
|
+
config = buildLocalBrokerConfig({
|
|
554
|
+
projectRoot,
|
|
555
|
+
gitCommonDir: deps.gitCommonDir,
|
|
556
|
+
databasePath: deps.kernelDatabasePath,
|
|
557
|
+
execFileSync: deps.execFileSync,
|
|
558
|
+
});
|
|
559
|
+
} catch {
|
|
560
|
+
// No resolvable kernel location (e.g. projectRoot is not a git repo) — there is
|
|
561
|
+
// no safe fallback target, so let the Beads path report its own error.
|
|
562
|
+
return null;
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
const fileExists = deps.existsSync || existsSync;
|
|
566
|
+
if (!fileExists(config.databasePath)) {
|
|
567
|
+
// No kernel store either — preserve the Beads not-initialized error.
|
|
568
|
+
return null;
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
return { kernelDatabasePath: config.databasePath, gitCommonDir: config.gitCommonDir };
|
|
572
|
+
}
|
|
573
|
+
|
|
273
574
|
async function runIssueOperation(operation, rawArgs, projectRoot, deps = {}) {
|
|
274
|
-
const
|
|
575
|
+
const injectedService = deps.createService;
|
|
576
|
+
let routeToKernel = shouldUseKernelBroker(deps);
|
|
577
|
+
let effectiveDeps = deps;
|
|
578
|
+
|
|
579
|
+
// Beads→Kernel graceful fallback (7f09ae93). Only considered when a service is not
|
|
580
|
+
// injected (an injected service owns backend selection), the selection is not
|
|
581
|
+
// already Kernel, and this is not a help invocation (help must never touch a
|
|
582
|
+
// backend). When it applies, treat the op as Kernel-routed end-to-end (backend
|
|
583
|
+
// construction AND the projection-target seam below).
|
|
584
|
+
if (!injectedService && !routeToKernel && !isHelpInvocation(rawArgs)) {
|
|
585
|
+
const fallback = resolveBeadsFallbackToKernel(projectRoot, deps);
|
|
586
|
+
if (fallback) {
|
|
587
|
+
routeToKernel = true;
|
|
588
|
+
effectiveDeps = {
|
|
589
|
+
...deps,
|
|
590
|
+
useKernelBroker: true,
|
|
591
|
+
kernelDatabasePath: fallback.kernelDatabasePath,
|
|
592
|
+
gitCommonDir: fallback.gitCommonDir,
|
|
593
|
+
};
|
|
594
|
+
const notify = deps.warn || ((msg) => process.stderr.write(`${msg}\n`));
|
|
595
|
+
notify(
|
|
596
|
+
'Notice: Beads is not initialized, but a Forge Kernel store exists — using the '
|
|
597
|
+
+ 'kernel backend (the default). Run `forge setup` to finish migrating.',
|
|
598
|
+
);
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
const createService = injectedService || (() => {
|
|
603
|
+
const context = {
|
|
604
|
+
projectRoot,
|
|
605
|
+
deps: effectiveDeps,
|
|
606
|
+
};
|
|
607
|
+
|
|
608
|
+
if (routeToKernel) {
|
|
609
|
+
return createIssueService({
|
|
610
|
+
backend: createKernelIssueBackend(context),
|
|
611
|
+
});
|
|
612
|
+
}
|
|
613
|
+
|
|
275
614
|
const backendDeps = {
|
|
276
615
|
isBeadsInitialized: deps.isBeadsInitialized,
|
|
277
616
|
runBdCommand: deps.runBdCommand,
|
|
@@ -286,9 +625,37 @@ async function runIssueOperation(operation, rawArgs, projectRoot, deps = {}) {
|
|
|
286
625
|
});
|
|
287
626
|
|
|
288
627
|
const service = createService();
|
|
628
|
+
// Thread a distinct per-agent actor (and, when present, a stable session id) into the
|
|
629
|
+
// mutation context so the Kernel's claim idempotency key is scoped per agent rather than
|
|
630
|
+
// to the shared 'forge' default (kernel d71a824b). Undefined values are omitted so the
|
|
631
|
+
// historical default is preserved for no-env callers, and the Beads backend — which
|
|
632
|
+
// ignores context.actor/sessionId — is unaffected.
|
|
633
|
+
const actorEnv = deps.env || process.env;
|
|
634
|
+
const actor = resolveIssueActor(actorEnv);
|
|
635
|
+
const sessionId = typeof actorEnv.FORGE_SESSION_ID === 'string' && actorEnv.FORGE_SESSION_ID.trim()
|
|
636
|
+
? actorEnv.FORGE_SESSION_ID.trim()
|
|
637
|
+
: undefined;
|
|
638
|
+
// The worktree id and lease TTL only shape a claim's lease row, so resolve them ONLY
|
|
639
|
+
// for the claim op — worktree detection shells out to git, and running it on every read
|
|
640
|
+
// (list/show/ready) would be pure cost. leaseTtlMs=null (explicit opt-out) is omitted so
|
|
641
|
+
// the broker keeps a null (never-expiring) lease.
|
|
642
|
+
const isClaim = operation === 'claim';
|
|
643
|
+
const worktreeId = isClaim ? resolveWorktreeId(projectRoot, actorEnv, deps) : undefined;
|
|
644
|
+
const leaseTtlMs = isClaim ? resolveLeaseTtlMs(actorEnv) : undefined;
|
|
289
645
|
const result = await service.run(operation, rawArgs, {
|
|
290
646
|
projectRoot,
|
|
291
647
|
deps,
|
|
648
|
+
...(actor ? { actor } : {}),
|
|
649
|
+
...(sessionId ? { sessionId } : {}),
|
|
650
|
+
...(worktreeId ? { worktreeId } : {}),
|
|
651
|
+
...(leaseTtlMs ? { leaseTtlMs } : {}),
|
|
652
|
+
// Kernel mutations enqueue a projection-outbox "dirty marker" that `forge export`
|
|
653
|
+
// (the D16 git-JSONL portability projection) drains under target 'jsonl'. The
|
|
654
|
+
// broker's legacy primitive default is 'beads', so without steering the target
|
|
655
|
+
// here every Kernel-created issue is enqueued as 'beads' and `forge export` — which
|
|
656
|
+
// drains 'jsonl' — finds nothing, silently never emitting git-tracked JSONL. Only
|
|
657
|
+
// the Kernel path uses this seam (`context.projectionTarget`); Beads ignores it.
|
|
658
|
+
...(routeToKernel ? { projectionTarget: 'jsonl' } : {}),
|
|
292
659
|
});
|
|
293
660
|
|
|
294
661
|
const queueGitHubProjection = deps.enqueueGitHubProjection || deps.queueGitHubProjection;
|
|
@@ -310,6 +677,8 @@ async function runIssueOperation(operation, rawArgs, projectRoot, deps = {}) {
|
|
|
310
677
|
module.exports = {
|
|
311
678
|
createIssueService,
|
|
312
679
|
createBeadsIssueBackend,
|
|
680
|
+
createKernelIssueBackend,
|
|
681
|
+
runBeadsOperation,
|
|
313
682
|
runIssueOperation,
|
|
314
683
|
buildBdArgs,
|
|
315
684
|
extractErrorMessage,
|
|
@@ -322,5 +691,7 @@ module.exports = {
|
|
|
322
691
|
runBdCommand,
|
|
323
692
|
getBdCommandCandidates,
|
|
324
693
|
getPathEntries,
|
|
694
|
+
resolveIssueActor,
|
|
695
|
+
resolveBeadsFallbackToKernel,
|
|
325
696
|
resolveWindowsCommandCandidates,
|
|
326
697
|
};
|