forge-workflow 0.0.10 → 0.1.0-beta.3
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 +82 -5
- package/.forge/hooks/forge-native-hook.js +431 -0
- package/.forge/protected-paths.yaml +157 -0
- package/AGENTS.md +151 -61
- package/CHANGELOG.md +709 -0
- package/CLAUDE.md +9 -118
- package/QUICKSTART.md +175 -0
- package/README.md +275 -365
- package/bin/forge-cmd.js +120 -9
- package/bin/forge-preflight.js +26 -5
- package/bin/forge.js +532 -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 +121 -0
- package/docs/guides/SUPPORT.md +190 -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 +214 -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 +155 -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/activation/ensure-forge-home.js +135 -0
- package/lib/adapter-cli.js +307 -0
- package/lib/adapters/beads-issue-adapter.js +127 -0
- package/lib/adapters/beads-kernel-compat.js +1109 -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 +139 -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-detect.js +60 -0
- package/lib/beads-nudge.js +91 -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/_aliases.js +248 -0
- package/lib/commands/_issue.js +780 -77
- package/lib/commands/_manifest.js +93 -0
- package/lib/commands/_registry.js +99 -34
- package/lib/commands/_resolve-command-opts.js +230 -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 +209 -0
- package/lib/commands/hooks.js +377 -0
- package/lib/commands/inbox.js +118 -0
- package/lib/commands/init.js +604 -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/memory.js +81 -0
- package/lib/commands/merge.js +312 -0
- package/lib/commands/migrate.js +362 -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 +481 -29
- package/lib/commands/pr.js +88 -0
- package/lib/commands/preflight.js +211 -0
- package/lib/commands/prime.js +13 -0
- package/lib/commands/push.js +135 -2
- package/lib/commands/ready.js +2 -2
- package/lib/commands/recall.js +171 -0
- package/lib/commands/recap.js +75 -0
- package/lib/commands/recommend.js +0 -1
- package/lib/commands/release.js +104 -0
- package/lib/commands/remember.js +140 -0
- package/lib/commands/role.js +99 -0
- package/lib/commands/serve.js +581 -0
- package/lib/commands/setup.js +900 -971
- package/lib/commands/shepherd.js +501 -0
- package/lib/commands/ship.js +59 -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 +362 -99
- package/lib/config-writer.js +202 -0
- package/lib/control-plane.js +236 -0
- package/lib/core/runtime-graph.js +977 -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 +192 -0
- package/lib/global-flags.js +104 -0
- package/lib/greptile-match.js +7 -63
- package/lib/grounding/context-events.js +230 -0
- package/lib/grounding/read-first.js +112 -0
- package/lib/harness-capability-matrix.js +380 -0
- package/lib/hook-global-installer.js +347 -0
- package/lib/hook-renderer.js +541 -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 +311 -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/owned-kernel.js +43 -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 +2540 -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/auto-actions.js +175 -0
- package/lib/pr-monitor/differ.js +195 -0
- package/lib/pr-monitor/digest.js +206 -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 +192 -0
- package/lib/pr-monitor/upsert-sticky.js +169 -0
- package/lib/pr-monitor/watch-lifecycle.js +95 -0
- package/lib/pr-monitor/watch.js +247 -0
- package/lib/pr-pull.js +1314 -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 +2105 -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 +251 -0
- package/lib/workflow/enforce-stage.js +351 -45
- 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-auto-actions.js +93 -0
- package/scripts/pr-coordinator.sh +3 -0
- package/scripts/pr-verdict-label.js +50 -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
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
const { execFileSync } = require('node:child_process');
|
|
2
|
+
const fs = require('node:fs');
|
|
3
|
+
const path = require('node:path');
|
|
4
|
+
|
|
5
|
+
const VERDICT_LABELS = {
|
|
6
|
+
PASS: 'good',
|
|
7
|
+
FAIL: 'bad',
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
const REVIEWER_ROLES = new Set(['spec_reviewer', 'quality_reviewer']);
|
|
11
|
+
const SECRET_KEY_PATTERN = /(api(?:[_-]|\s)?key|authorization|credential|password|private(?:[_-]|\s)?key|secret|token)/i;
|
|
12
|
+
const SECRET_TEXT_KEY_PATTERN = String.raw`(?:api(?:[_-]|\s)?key|authorization|credential|password|private(?:[_-]|\s)?key|secret|token)`;
|
|
13
|
+
const SECRET_TEXT_PATTERNS = [
|
|
14
|
+
{
|
|
15
|
+
pattern: /\bBearer\s+\S+/gi,
|
|
16
|
+
replace: () => 'Bearer [REDACTED]',
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
pattern: new RegExp(`(\\b${SECRET_TEXT_KEY_PATTERN}\\b\\s*=\\s*)([^&,\r\n]+)`, 'gi'),
|
|
20
|
+
replace: (_match, prefix) => `${prefix}[REDACTED]`,
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
pattern: new RegExp(`(\\b${SECRET_TEXT_KEY_PATTERN}\\b\\s+is\\s+)([^&,\r\n]+)`, 'gi'),
|
|
24
|
+
replace: (_match, prefix) => `${prefix}[REDACTED]`,
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
pattern: new RegExp(
|
|
28
|
+
`(\\b${SECRET_TEXT_KEY_PATTERN}\\b\\s+)(?!is\\b)([^&,\r\n]*?)(?=\\s+\\b${SECRET_TEXT_KEY_PATTERN}\\b\\s+|[&,\r\n]|$)`,
|
|
29
|
+
'gi'
|
|
30
|
+
),
|
|
31
|
+
replace: (_match, prefix) => `${prefix}[REDACTED]`,
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
pattern: new RegExp(
|
|
35
|
+
`((?:"|')?${SECRET_TEXT_KEY_PATTERN}(?:"|')?\\s*:\\s*)(?:"[^"]*"|'[^']*'|[^,}\\]\r\n]+)`,
|
|
36
|
+
'gi'
|
|
37
|
+
),
|
|
38
|
+
replace: (_match, prefix) => `${prefix}"[REDACTED]"`,
|
|
39
|
+
},
|
|
40
|
+
/\bsk-[A-Za-z0-9_-]{8,}\b/g,
|
|
41
|
+
];
|
|
42
|
+
|
|
43
|
+
function defaultRunCommand(command, args, options = {}) {
|
|
44
|
+
return execFileSync(command, args, {
|
|
45
|
+
cwd: options.cwd || process.cwd(),
|
|
46
|
+
encoding: 'utf8',
|
|
47
|
+
input: options.input,
|
|
48
|
+
timeout: options.timeout || 120000,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function redactString(value) {
|
|
53
|
+
return SECRET_TEXT_PATTERNS.reduce(
|
|
54
|
+
(current, entry) => {
|
|
55
|
+
if (entry && typeof entry === 'object' && !(entry instanceof RegExp)) {
|
|
56
|
+
return current.replace(entry.pattern, entry.replace);
|
|
57
|
+
}
|
|
58
|
+
return current.replace(entry, () => '[REDACTED]');
|
|
59
|
+
},
|
|
60
|
+
value,
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function redact(value, key = '') {
|
|
65
|
+
if (SECRET_KEY_PATTERN.test(key)) {
|
|
66
|
+
return '[REDACTED]';
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (typeof value === 'string') {
|
|
70
|
+
return redactString(value);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (Array.isArray(value)) {
|
|
74
|
+
return value.map(item => redact(item));
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (value && typeof value === 'object') {
|
|
78
|
+
return Object.fromEntries(
|
|
79
|
+
Object.entries(value).map(([entryKey, entryValue]) => [entryKey, redact(entryValue, entryKey)]),
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return value;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function asJsonString(value) {
|
|
87
|
+
if (typeof value === 'string') return redactString(value);
|
|
88
|
+
return JSON.stringify(redact(value));
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function buildSubagentAuditPayload(event) {
|
|
92
|
+
if (!event || typeof event !== 'object') {
|
|
93
|
+
throw new TypeError('Audit event must be an object');
|
|
94
|
+
}
|
|
95
|
+
if (!event.command) {
|
|
96
|
+
throw new TypeError('Audit event command is required');
|
|
97
|
+
}
|
|
98
|
+
if (!event.role) {
|
|
99
|
+
throw new TypeError('Audit event role is required');
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const metadata =
|
|
103
|
+
event.metadata && typeof event.metadata === 'object' && !Array.isArray(event.metadata)
|
|
104
|
+
? event.metadata
|
|
105
|
+
: {};
|
|
106
|
+
|
|
107
|
+
return {
|
|
108
|
+
kind: 'llm_call',
|
|
109
|
+
command: event.command,
|
|
110
|
+
issueId: event.issueId,
|
|
111
|
+
role: event.role,
|
|
112
|
+
phase: event.phase,
|
|
113
|
+
taskId: event.taskId,
|
|
114
|
+
taskTitle: event.taskTitle,
|
|
115
|
+
model: event.model || 'unknown',
|
|
116
|
+
prompt: asJsonString({
|
|
117
|
+
command: event.command,
|
|
118
|
+
role: event.role,
|
|
119
|
+
phase: event.phase,
|
|
120
|
+
taskId: event.taskId,
|
|
121
|
+
taskTitle: event.taskTitle,
|
|
122
|
+
content: event.prompt || '',
|
|
123
|
+
}),
|
|
124
|
+
response: asJsonString({
|
|
125
|
+
command: event.command,
|
|
126
|
+
role: event.role,
|
|
127
|
+
verdict: event.verdict || 'UNKNOWN',
|
|
128
|
+
content: event.response || '',
|
|
129
|
+
}),
|
|
130
|
+
metadata: redact(metadata),
|
|
131
|
+
verdict: event.verdict,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function parseRecordId(output, { requireJson = false } = {}) {
|
|
136
|
+
if (!output) return null;
|
|
137
|
+
try {
|
|
138
|
+
const parsed = JSON.parse(output);
|
|
139
|
+
return typeof parsed.id === 'string' ? parsed.id : null;
|
|
140
|
+
} catch (_error) {
|
|
141
|
+
if (requireJson) return null;
|
|
142
|
+
const match = /\bint-[A-Za-z0-9_-]+\b/.exec(String(output));
|
|
143
|
+
return match ? match[0] : null;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function hasAuditMetaJsonSupport(runCommand = defaultRunCommand, options = {}) {
|
|
148
|
+
const output = runCommand('bd', ['audit', 'record', '--help'], options);
|
|
149
|
+
return String(output).includes('--meta-json');
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function writeFallbackMetadata(payload, entryId, options) {
|
|
153
|
+
if (!entryId || !payload.metadata || Object.keys(payload.metadata).length === 0) {
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const cwd = options.cwd || process.cwd();
|
|
158
|
+
const fsImpl = options.fs || fs;
|
|
159
|
+
const forgeDir = path.join(cwd, '.forge').replace(/\\/g, '/');
|
|
160
|
+
const logPath = path.join(forgeDir, 'log.jsonl').replace(/\\/g, '/');
|
|
161
|
+
const line = {
|
|
162
|
+
kind: 'forge.auditEvidence',
|
|
163
|
+
sourceOfTruth: 'beads',
|
|
164
|
+
beadsEntryId: entryId,
|
|
165
|
+
command: redact(payload.command),
|
|
166
|
+
role: redact(payload.role),
|
|
167
|
+
phase: redact(payload.phase),
|
|
168
|
+
taskId: redact(payload.taskId),
|
|
169
|
+
taskTitle: redact(payload.taskTitle),
|
|
170
|
+
metadata: redact(payload.metadata),
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
try {
|
|
174
|
+
fsImpl.mkdirSync(forgeDir, { recursive: true });
|
|
175
|
+
fsImpl.appendFileSync(logPath, `${JSON.stringify(line)}\n`);
|
|
176
|
+
return { path: logPath, line };
|
|
177
|
+
} catch (error) {
|
|
178
|
+
return { path: logPath, line, skipped: true, error: error.message };
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function recordSubagentAuditEvent(event, options = {}) {
|
|
183
|
+
const payload = buildSubagentAuditPayload(event);
|
|
184
|
+
const runCommand = options.runCommand || defaultRunCommand;
|
|
185
|
+
const metaJsonSupported =
|
|
186
|
+
typeof options.metaJsonSupported === 'boolean'
|
|
187
|
+
? options.metaJsonSupported
|
|
188
|
+
: hasAuditMetaJsonSupport(runCommand, { cwd: options.cwd || process.cwd() });
|
|
189
|
+
const args = [
|
|
190
|
+
'audit',
|
|
191
|
+
'record',
|
|
192
|
+
'--json',
|
|
193
|
+
'--kind',
|
|
194
|
+
'llm_call',
|
|
195
|
+
];
|
|
196
|
+
|
|
197
|
+
if (payload.issueId) {
|
|
198
|
+
args.push('--issue-id', payload.issueId);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
args.push(
|
|
202
|
+
'--model',
|
|
203
|
+
payload.model,
|
|
204
|
+
'--prompt',
|
|
205
|
+
payload.prompt,
|
|
206
|
+
'--response',
|
|
207
|
+
payload.response,
|
|
208
|
+
);
|
|
209
|
+
|
|
210
|
+
if (metaJsonSupported && Object.keys(payload.metadata).length > 0) {
|
|
211
|
+
args.push('--meta-json', JSON.stringify(payload.metadata));
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const output = runCommand('bd', args, { cwd: options.cwd || process.cwd() });
|
|
215
|
+
const entryId = parseRecordId(output, { requireJson: true });
|
|
216
|
+
const fallback = metaJsonSupported ? null : writeFallbackMetadata(payload, entryId, options);
|
|
217
|
+
|
|
218
|
+
return {
|
|
219
|
+
success: Boolean(entryId),
|
|
220
|
+
entryId,
|
|
221
|
+
output,
|
|
222
|
+
payload,
|
|
223
|
+
fallback,
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function labelSubagentAuditEvent(entryId, event, options = {}) {
|
|
228
|
+
const verdict = String(event?.verdict || '').toUpperCase();
|
|
229
|
+
const role = event?.role;
|
|
230
|
+
const label = VERDICT_LABELS[verdict];
|
|
231
|
+
|
|
232
|
+
if (!entryId || !REVIEWER_ROLES.has(role) || !label) {
|
|
233
|
+
return { skipped: true };
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const runCommand = options.runCommand || defaultRunCommand;
|
|
237
|
+
const reason = `${role} verdict: ${verdict}`;
|
|
238
|
+
let output;
|
|
239
|
+
try {
|
|
240
|
+
output = runCommand('bd', [
|
|
241
|
+
'audit',
|
|
242
|
+
'label',
|
|
243
|
+
entryId,
|
|
244
|
+
'--json',
|
|
245
|
+
'--label',
|
|
246
|
+
label,
|
|
247
|
+
'--reason',
|
|
248
|
+
reason,
|
|
249
|
+
], { cwd: options.cwd || process.cwd() });
|
|
250
|
+
} catch (error) {
|
|
251
|
+
return {
|
|
252
|
+
success: false,
|
|
253
|
+
label,
|
|
254
|
+
error: error.message,
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const labeledEntryId = parseRecordId(output, { requireJson: true });
|
|
259
|
+
|
|
260
|
+
return {
|
|
261
|
+
success: labeledEntryId === entryId,
|
|
262
|
+
label,
|
|
263
|
+
entryId: labeledEntryId,
|
|
264
|
+
output,
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function recordAndLabelSubagentAuditEvent(event, options = {}) {
|
|
269
|
+
const record = recordSubagentAuditEvent(event, options);
|
|
270
|
+
const label = labelSubagentAuditEvent(record.entryId, event, options);
|
|
271
|
+
return { record, label };
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
module.exports = {
|
|
275
|
+
VERDICT_LABELS,
|
|
276
|
+
buildSubagentAuditPayload,
|
|
277
|
+
recordSubagentAuditEvent,
|
|
278
|
+
labelSubagentAuditEvent,
|
|
279
|
+
recordAndLabelSubagentAuditEvent,
|
|
280
|
+
hasAuditMetaJsonSupport,
|
|
281
|
+
redact,
|
|
282
|
+
};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('node:fs');
|
|
4
|
+
const path = require('node:path');
|
|
5
|
+
|
|
6
|
+
// Single-source, side-effect-free Beads-store detector (kernel issue a5399f3d).
|
|
7
|
+
//
|
|
8
|
+
// Both upgrade-safety surfaces reuse this ONE definition: the issue-path
|
|
9
|
+
// unmigrated-beads nudge (lib/commands/_issue.js) and the `forge upgrade`
|
|
10
|
+
// advisory (lib/upgrade-safety.js). It deliberately lives in a neutral module,
|
|
11
|
+
// NOT in lib/commands/migrate.js — re-exporting a `detectBeadsJsonlSource` name
|
|
12
|
+
// from the migrate command would revive the identifier the a7e1443c
|
|
13
|
+
// implicit-auto-migrate tombstone pins gone (test/commands/runtime-no-auto-migrate).
|
|
14
|
+
// This detector only READS the filesystem and never triggers a migration, so it
|
|
15
|
+
// honors that tombstone's spirit while giving both nudges a shared source.
|
|
16
|
+
|
|
17
|
+
function dirHasJsonl(dir) {
|
|
18
|
+
try {
|
|
19
|
+
return fs.readdirSync(dir).some(entry => entry.endsWith('.jsonl'));
|
|
20
|
+
} catch (_err) { // NOSONAR S2486 — line-scoped; must sit on the catch line
|
|
21
|
+
/* intentional: an unreadable directory has no usable jsonl */
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Return the absolute path of the directory holding a returning user's Beads
|
|
28
|
+
* JSONL under `projectRoot`, or null when none is found. Checks the top-level
|
|
29
|
+
* `.beads/` first, then the split-store `.beads/backup/` layout (jsonl there and
|
|
30
|
+
* nowhere else — a layout the migrator itself reads), so neither surface misses
|
|
31
|
+
* it. Never throws.
|
|
32
|
+
*
|
|
33
|
+
* @param {string} [projectRoot]
|
|
34
|
+
* @returns {string|null}
|
|
35
|
+
*/
|
|
36
|
+
function detectBeadsJsonlSource(projectRoot) {
|
|
37
|
+
const root = projectRoot || process.cwd();
|
|
38
|
+
const beadsDir = path.join(root, '.beads');
|
|
39
|
+
try {
|
|
40
|
+
if (!fs.existsSync(beadsDir)) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
if (dirHasJsonl(beadsDir)) {
|
|
44
|
+
return beadsDir;
|
|
45
|
+
}
|
|
46
|
+
const backupDir = path.join(beadsDir, 'backup');
|
|
47
|
+
if (fs.existsSync(backupDir) && dirHasJsonl(backupDir)) {
|
|
48
|
+
return backupDir;
|
|
49
|
+
}
|
|
50
|
+
return null;
|
|
51
|
+
} catch (_err) { // NOSONAR S2486 — line-scoped; must sit on the catch line
|
|
52
|
+
/* intentional: an unreadable project root has no detectable beads source */
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
module.exports = {
|
|
58
|
+
detectBeadsJsonlSource,
|
|
59
|
+
dirHasJsonl,
|
|
60
|
+
};
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { resolveIssueBackend } = require('./issue-backend');
|
|
4
|
+
const { detectBeadsJsonlSource } = require('./beads-detect');
|
|
5
|
+
|
|
6
|
+
// Unmigrated legacy-store nudge (kernel issue a5399f3d — upgrade-safety
|
|
7
|
+
// 0.0.10 -> current).
|
|
8
|
+
//
|
|
9
|
+
// The 0.0.10 -> current upgrade flipped the DEFAULT issue backend to the Kernel,
|
|
10
|
+
// but the legacy -> kernel migration fires ONLY from `forge setup`/`init`, never
|
|
11
|
+
// lazily on the issue path. So a user who runs `bun update` then `forge list` /
|
|
12
|
+
// `forge ready` reads an EMPTY kernel and their 0.0.10 issues APPEAR GONE — the
|
|
13
|
+
// data is safe on disk, just invisible, with no hint. This helper closes that
|
|
14
|
+
// footgun: when the resolved backend is the (default) Kernel, a read comes back
|
|
15
|
+
// empty, AND a legacy jsonl store still exists, it prints a one-time guided hint.
|
|
16
|
+
//
|
|
17
|
+
// It lives in this NEUTRAL module (not lib/commands/_issue.js) on purpose: the
|
|
18
|
+
// message text names the retired backend + its `.beads` store, and _issue.js is a
|
|
19
|
+
// D20 release-readiness hot-path surface that must stay free of those tokens (the
|
|
20
|
+
// bd-call-site audit + kernel-backed checks scan it). Keeping the strings here
|
|
21
|
+
// lets the hot path call a token-free helper.
|
|
22
|
+
//
|
|
23
|
+
// Best-effort and non-blocking: it NEVER throws and never alters the read result.
|
|
24
|
+
// Fires at most once per project root per process to avoid spam.
|
|
25
|
+
const NUDGE_READS = new Set(['list', 'ready']);
|
|
26
|
+
const warnedRoots = new Set();
|
|
27
|
+
|
|
28
|
+
// A kernel read looks empty when its contract data carries no issues. Handles the
|
|
29
|
+
// issue.list/issue.ready shape ({ issues, count }) plus a bare array / null, and
|
|
30
|
+
// is deliberately conservative: anything it cannot confirm as empty is treated as
|
|
31
|
+
// NON-empty so a user with real kernel issues is never nagged.
|
|
32
|
+
function kernelReadLooksEmpty(result) {
|
|
33
|
+
if (!result || typeof result !== 'object' || result.ok !== true) {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
const data = result.data;
|
|
37
|
+
if (data === null || data === undefined) {
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
if (Array.isArray(data)) {
|
|
41
|
+
return data.length === 0;
|
|
42
|
+
}
|
|
43
|
+
if (Array.isArray(data.issues)) {
|
|
44
|
+
return data.issues.length === 0;
|
|
45
|
+
}
|
|
46
|
+
if (Array.isArray(data.items)) {
|
|
47
|
+
return data.items.length === 0;
|
|
48
|
+
}
|
|
49
|
+
if (typeof data.count === 'number') {
|
|
50
|
+
return data.count === 0;
|
|
51
|
+
}
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function maybeWarnUnmigratedBeads(subcommand, result, projectRoot, rawOpts = {}) {
|
|
56
|
+
try {
|
|
57
|
+
if (!NUDGE_READS.has(subcommand) || !projectRoot) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
if (warnedRoots.has(projectRoot)) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
const env = rawOpts.env || process.env;
|
|
64
|
+
// Resolve the EFFECTIVE backend (default kernel). An explicit opt-in to the
|
|
65
|
+
// retired backend means the user chose it deliberately — nothing to nudge.
|
|
66
|
+
const backend = resolveIssueBackend({ deps: rawOpts, env, projectRoot, warn: () => {} });
|
|
67
|
+
if (backend !== 'kernel' || !kernelReadLooksEmpty(result)) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
if (!detectBeadsJsonlSource(projectRoot)) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
warnedRoots.add(projectRoot);
|
|
74
|
+
console.error(
|
|
75
|
+
'\n[forge] Legacy issue data detected (.beads/*.jsonl) but the Kernel issue store is empty.\n'
|
|
76
|
+
+ 'Forge now defaults to the Kernel backend (breaking change since 0.0.10). Your legacy\n'
|
|
77
|
+
+ 'issues are safe on disk but will not appear until migrated. To migrate:\n'
|
|
78
|
+
+ ' forge migrate --from beads # import your legacy issues into the Kernel\n'
|
|
79
|
+
+ ' forge setup # (re)wire hooks + provision the Kernel store\n'
|
|
80
|
+
+ 'Prefer to stay on the legacy backend? Set `issueBackend: beads` in .forge/config.yaml '
|
|
81
|
+
+ '(or FORGE_ISSUE_BACKEND=beads).\n',
|
|
82
|
+
);
|
|
83
|
+
} catch (_err) { // NOSONAR S2486 — line-scoped; must sit on the catch line
|
|
84
|
+
/* the nudge is a best-effort hint; it must never break a read */
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
module.exports = {
|
|
89
|
+
maybeWarnUnmigratedBeads,
|
|
90
|
+
kernelReadLooksEmpty,
|
|
91
|
+
};
|
package/lib/beads-setup.js
CHANGED
|
@@ -12,6 +12,13 @@ const path = require('node:path');
|
|
|
12
12
|
const { spawnSync } = require('node:child_process');
|
|
13
13
|
const { secureExecFileSync } = require('./shell-utils');
|
|
14
14
|
|
|
15
|
+
const BEADS_LOCAL_EXCLUDE_LINES = [
|
|
16
|
+
'# Forge local Beads state',
|
|
17
|
+
'.beads/',
|
|
18
|
+
'.dolt/',
|
|
19
|
+
'.beads-credential-key'
|
|
20
|
+
];
|
|
21
|
+
|
|
15
22
|
function isIgnorableFsError(error) {
|
|
16
23
|
return ['EACCES', 'ENOENT', 'ENOTDIR', 'EPERM'].includes(error?.code);
|
|
17
24
|
}
|
|
@@ -103,6 +110,10 @@ function writeBeadsGitignore(projectRoot) {
|
|
|
103
110
|
fs.mkdirSync(beadsDir, { recursive: true });
|
|
104
111
|
|
|
105
112
|
const gitignoreContent = [
|
|
113
|
+
'# Beads runtime, export, and backup state is local',
|
|
114
|
+
'*',
|
|
115
|
+
'!.gitignore',
|
|
116
|
+
'',
|
|
106
117
|
'# Dolt database files (binary, not suitable for git)',
|
|
107
118
|
'dolt/',
|
|
108
119
|
'*.db',
|
|
@@ -117,6 +128,108 @@ function writeBeadsGitignore(projectRoot) {
|
|
|
117
128
|
);
|
|
118
129
|
}
|
|
119
130
|
|
|
131
|
+
function resolveGitInfoExcludePath(projectRoot) {
|
|
132
|
+
try {
|
|
133
|
+
const gitExcludePath = secureExecFileSync(resolveCommand('git'), ['rev-parse', '--git-path', 'info/exclude'], {
|
|
134
|
+
cwd: projectRoot,
|
|
135
|
+
encoding: 'utf8',
|
|
136
|
+
stdio: 'pipe'
|
|
137
|
+
}).trim();
|
|
138
|
+
|
|
139
|
+
if (gitExcludePath) {
|
|
140
|
+
return path.isAbsolute(gitExcludePath)
|
|
141
|
+
? gitExcludePath
|
|
142
|
+
: path.resolve(projectRoot, gitExcludePath);
|
|
143
|
+
}
|
|
144
|
+
} catch (error) {
|
|
145
|
+
if (!isRecoverableCommandError(error)) {
|
|
146
|
+
throw error;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return path.join(projectRoot, '.git', 'info', 'exclude');
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function untrackExistingBeadsState(projectRoot) {
|
|
154
|
+
let tracked;
|
|
155
|
+
try {
|
|
156
|
+
tracked = secureExecFileSync(resolveCommand('git'), [
|
|
157
|
+
'ls-files',
|
|
158
|
+
'-z',
|
|
159
|
+
'--',
|
|
160
|
+
'.beads',
|
|
161
|
+
'.dolt',
|
|
162
|
+
'.beads-credential-key'
|
|
163
|
+
], {
|
|
164
|
+
cwd: projectRoot,
|
|
165
|
+
encoding: 'utf8',
|
|
166
|
+
stdio: 'pipe'
|
|
167
|
+
});
|
|
168
|
+
} catch (error) {
|
|
169
|
+
if (!isRecoverableCommandError(error)) {
|
|
170
|
+
throw error;
|
|
171
|
+
}
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (!tracked) {
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
try {
|
|
180
|
+
secureExecFileSync(resolveCommand('git'), [
|
|
181
|
+
'rm',
|
|
182
|
+
'--cached',
|
|
183
|
+
'-r',
|
|
184
|
+
'-f',
|
|
185
|
+
'--ignore-unmatch',
|
|
186
|
+
'--',
|
|
187
|
+
'.beads',
|
|
188
|
+
'.dolt',
|
|
189
|
+
'.beads-credential-key'
|
|
190
|
+
], {
|
|
191
|
+
cwd: projectRoot,
|
|
192
|
+
encoding: 'utf8',
|
|
193
|
+
stdio: 'pipe'
|
|
194
|
+
});
|
|
195
|
+
} catch (error) {
|
|
196
|
+
if (!isRecoverableCommandError(error)) {
|
|
197
|
+
throw error;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Add local-only Git exclude rules for Beads state.
|
|
204
|
+
*
|
|
205
|
+
* Uses `.git/info/exclude` instead of the tracked `.gitignore` so `forge setup`
|
|
206
|
+
* does not dirty downstream repositories just to keep Beads runtime files local.
|
|
207
|
+
* Existing tracked Beads state is removed from the Git index only, leaving local
|
|
208
|
+
* files in place for the Beads runtime.
|
|
209
|
+
*
|
|
210
|
+
* @param {string} projectRoot - Absolute path to the project root
|
|
211
|
+
*/
|
|
212
|
+
function ensureBeadsGitExclude(projectRoot) {
|
|
213
|
+
const excludePath = resolveGitInfoExcludePath(projectRoot);
|
|
214
|
+
fs.mkdirSync(path.dirname(excludePath), { recursive: true });
|
|
215
|
+
|
|
216
|
+
const existing = fs.existsSync(excludePath)
|
|
217
|
+
? fs.readFileSync(excludePath, 'utf8')
|
|
218
|
+
: '';
|
|
219
|
+
const existingLines = new Set(existing.split(/\r?\n/));
|
|
220
|
+
const missingLines = BEADS_LOCAL_EXCLUDE_LINES.filter((line) => !existingLines.has(line));
|
|
221
|
+
|
|
222
|
+
if (missingLines.length === 0) {
|
|
223
|
+
untrackExistingBeadsState(projectRoot);
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const prefix = existing && !existing.endsWith('\n') ? '\n' : '';
|
|
228
|
+
const spacer = existing.trim() ? '\n' : '';
|
|
229
|
+
fs.appendFileSync(excludePath, `${prefix}${spacer}${missingLines.join('\n')}\n`, 'utf8');
|
|
230
|
+
untrackExistingBeadsState(projectRoot);
|
|
231
|
+
}
|
|
232
|
+
|
|
120
233
|
/**
|
|
121
234
|
* Check whether Beads is properly initialized in a project.
|
|
122
235
|
*
|
|
@@ -334,6 +447,13 @@ function safeBeadsInit(projectRoot, options = {}) {
|
|
|
334
447
|
const warnings = [];
|
|
335
448
|
const errors = [];
|
|
336
449
|
|
|
450
|
+
// Keep Beads runtime/export state local even when the tracker already exists.
|
|
451
|
+
try {
|
|
452
|
+
ensureBeadsGitExclude(projectRoot);
|
|
453
|
+
} catch (err) {
|
|
454
|
+
warnings.push(`Failed to write local Beads git exclude rules: ${err.message}`);
|
|
455
|
+
}
|
|
456
|
+
|
|
337
457
|
// (a) Check if already initialized — idempotent skip
|
|
338
458
|
if (isBeadsInitialized(projectRoot)) {
|
|
339
459
|
return {
|
|
@@ -410,6 +530,7 @@ module.exports = {
|
|
|
410
530
|
sanitizePrefix,
|
|
411
531
|
writeBeadsConfig,
|
|
412
532
|
writeBeadsGitignore,
|
|
533
|
+
ensureBeadsGitExclude,
|
|
413
534
|
isBeadsInitialized,
|
|
414
535
|
readBeadsDatabaseName,
|
|
415
536
|
preSeedJsonl,
|