forge-workflow 0.0.9 → 0.1.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/rules/{greptile-review-process.md → review-process.md} +56 -41
- package/.claude/scripts/{greptile-resolve.sh → review-resolve.sh} +13 -3
- package/.cursor/rules/permissions-guidance.mdc +2 -2
- package/.forge/hooks/check-tdd.js +3 -0
- package/.forge/hooks/forge-native-hook.js +245 -0
- package/.forge/protected-paths.yaml +157 -0
- package/AGENTS.md +151 -61
- package/CHANGELOG.md +681 -0
- package/CLAUDE.md +9 -106
- package/QUICKSTART.md +171 -0
- package/README.md +271 -363
- package/bin/forge-cmd.js +120 -9
- package/bin/forge-preflight.js +26 -5
- package/bin/forge.js +466 -489
- package/docs/INDEX.md +93 -0
- package/docs/PROJECT_DESIGN.md +685 -0
- package/docs/architecture/index.md +66 -0
- package/docs/architecture/notes/README.md +35 -0
- package/docs/architecture/subsystems/README.md +46 -0
- package/docs/{TOOLCHAIN.md → forge/TOOLCHAIN.md} +56 -47
- package/docs/forge/VALIDATION.md +82 -0
- package/docs/{AGENT_INSTALL_PROMPT.md → guides/AGENT_INSTALL_PROMPT.md} +3 -3
- package/docs/guides/BEADS_GITHUB_SYNC.md +32 -0
- package/docs/{ENHANCED_ONBOARDING.md → guides/ENHANCED_ONBOARDING.md} +16 -12
- package/docs/guides/GREPTILE_SETUP.md +46 -0
- package/docs/guides/MANUAL_REVIEW_GUIDE.md +58 -0
- package/docs/guides/MIGRATION.md +56 -0
- package/docs/guides/SETUP.md +118 -0
- package/docs/guides/SUPPORT.md +185 -0
- package/docs/guides/WORKFLOW_TEMPLATES.md +74 -0
- package/docs/guides/memory-backends.md +183 -0
- package/docs/reference/ADAPTERS.md +128 -0
- package/docs/reference/AGENT_SKILL_PARITY.md +175 -0
- package/docs/reference/COMMANDS.md +205 -0
- package/docs/reference/DECISION_DRIFT_GUARDS.md +97 -0
- package/docs/{EXAMPLES.md → reference/EXAMPLES.md} +7 -5
- package/docs/reference/FORGE_KERNEL_STORAGE_MODEL.md +135 -0
- package/docs/reference/HERMES_INTEGRATION.md +118 -0
- package/docs/reference/INSIGHTS_RECAP.md +63 -0
- package/docs/reference/INSTALL.md +164 -0
- package/docs/reference/KERNEL_TAXONOMY_VALIDATION.md +161 -0
- package/docs/reference/PROTECTED_PATH_MANIFEST.md +25 -0
- package/docs/reference/RELEASE.md +68 -0
- package/docs/reference/RESEARCH_TEMPLATE.md +292 -0
- package/docs/{ROADMAP.md → reference/ROADMAP.md} +12 -9
- package/docs/reference/SKILLS.md +35 -0
- package/docs/reference/STATUS_BOARD.md +80 -0
- package/docs/reference/TEMPLATES.md +106 -0
- package/docs/reference/TOOLCHAIN.md +658 -0
- package/docs/reference/VALIDATION.md +82 -0
- package/docs/reference/agent-permissions.md +169 -0
- package/docs/reference/beads-to-kernel-migration-ux.md +61 -0
- package/docs/reference/control-plane-guarantees.md +125 -0
- package/docs/reference/dependency-chain.md +331 -0
- package/docs/reference/forge-kernel-issue-command-contract.md +161 -0
- package/docs/reference/forge-kernel-schema.md +72 -0
- package/docs/reference/kernel-conflict-evaluators.md +27 -0
- package/docs/reference/patch-md-format.md +77 -0
- package/docs/reference/protected-state-surfaces.md +59 -0
- package/docs/reference/shepherd.md +115 -0
- package/docs/reference/superpowers-analysis.md +320 -0
- package/docs/reference/superpowers-integration-options.md +404 -0
- package/docs/reference/test-environment.md +519 -0
- package/docs/reference/upgrade-safety.md +59 -0
- package/lefthook.yml +18 -0
- package/lib/adapter-cli.js +307 -0
- package/lib/adapters/beads-issue-adapter.js +127 -0
- package/lib/adapters/beads-kernel-compat.js +1042 -0
- package/lib/adapters/greptile-review-adapter.js +141 -0
- package/lib/adapters/kernel-issue-adapter.js +101 -0
- package/lib/adapters/pr-state-adapter.js +484 -0
- package/lib/adoption-profiles.js +126 -0
- package/lib/agents/README.md +2 -6
- package/lib/agents/claude.plugin.json +3 -8
- package/lib/agents/codex.plugin.json +9 -1
- package/lib/agents/cursor.plugin.json +2 -6
- package/lib/agents/hermes.plugin.json +22 -0
- package/lib/agents-config.js +39 -1236
- package/lib/audit-evidence.js +282 -0
- package/lib/beads-setup.js +225 -28
- package/lib/beads-sync-scaffold.js +36 -107
- package/lib/codex-skills.js +51 -1
- package/lib/commands/_issue.js +744 -70
- package/lib/commands/_manifest.js +91 -0
- package/lib/commands/_registry.js +85 -34
- package/lib/commands/_resolve-command-opts.js +261 -0
- package/lib/commands/_serve-security.js +270 -0
- package/lib/commands/adapter.js +12 -0
- package/lib/commands/add.js +118 -0
- package/lib/commands/audit.js +70 -0
- package/lib/commands/blocked.js +5 -0
- package/lib/commands/board.js +64 -0
- package/lib/commands/claim.js +21 -2
- package/lib/commands/claims.js +7 -0
- package/lib/commands/clean.js +485 -75
- package/lib/commands/close.js +2 -2
- package/lib/commands/comment.js +5 -0
- package/lib/commands/control.js +148 -0
- package/lib/commands/create.js +2 -2
- package/lib/commands/dev.js +185 -7
- package/lib/commands/doc-gate.js +336 -0
- package/lib/commands/doctor.js +156 -0
- package/lib/commands/explain.js +15 -0
- package/lib/commands/export.js +237 -0
- package/lib/commands/gate.js +192 -0
- package/lib/commands/hooks.js +242 -0
- package/lib/commands/inbox.js +118 -0
- package/lib/commands/init.js +598 -0
- package/lib/commands/insights.js +79 -0
- package/lib/commands/issue.js +12 -1
- package/lib/commands/issues.js +66 -0
- package/lib/commands/lint.js +5 -0
- package/lib/commands/list.js +2 -2
- package/lib/commands/merge.js +312 -0
- package/lib/commands/migrate.js +523 -0
- package/lib/commands/new.js +12 -0
- package/lib/commands/options.js +241 -0
- package/lib/commands/orient.js +13 -0
- package/lib/commands/orphans.js +5 -0
- package/lib/commands/patch.js +67 -0
- package/lib/commands/plan.js +436 -24
- package/lib/commands/preflight.js +211 -0
- package/lib/commands/prime.js +13 -0
- package/lib/commands/push.js +69 -2
- package/lib/commands/ready.js +2 -2
- package/lib/commands/recall.js +116 -0
- package/lib/commands/recap.js +61 -0
- package/lib/commands/recommend.js +22 -2
- package/lib/commands/release.js +91 -0
- package/lib/commands/remember.js +74 -0
- package/lib/commands/role.js +99 -0
- package/lib/commands/serve.js +581 -0
- package/lib/commands/setup.js +851 -979
- package/lib/commands/shepherd.js +436 -0
- package/lib/commands/ship.js +23 -1
- package/lib/commands/show.js +2 -2
- package/lib/commands/stage.js +192 -0
- package/lib/commands/stale.js +5 -0
- package/lib/commands/status.js +329 -11
- package/lib/commands/sync.js +34 -46
- package/lib/commands/team.js +15 -2
- package/lib/commands/test.js +58 -7
- package/lib/commands/update.js +2 -2
- package/lib/commands/upgrade.js +47 -0
- package/lib/commands/validate.js +56 -25
- package/lib/commands/worktree.js +308 -128
- package/lib/config-writer.js +202 -0
- package/lib/control-plane.js +236 -0
- package/lib/core/runtime-graph.js +946 -0
- package/lib/dep-guard/keyword-ripple.js +184 -0
- package/lib/deprecated-sync-cleanup.js +362 -0
- package/lib/detect-agent.js +2 -28
- package/lib/detect-worktree.js +42 -17
- package/lib/doc-gate/declaration.js +177 -0
- package/lib/doc-gate/detect.js +289 -0
- package/lib/doc-gate/gate.js +375 -0
- package/lib/doc-gate/okf-config.js +128 -0
- package/lib/doc-gate/okf.js +429 -0
- package/lib/docs-command.js +1161 -6
- package/lib/forge-issues.js +697 -0
- package/lib/forge-lock.js +262 -0
- package/lib/gate-events.js +193 -0
- package/lib/global-flags.js +74 -0
- package/lib/greptile-match.js +7 -63
- package/lib/harness-capability-matrix.js +380 -0
- package/lib/hook-global-installer.js +347 -0
- package/lib/hook-renderer.js +451 -0
- package/lib/inbox.js +391 -0
- package/lib/insights.js +397 -0
- package/lib/issue-adapter.js +156 -0
- package/lib/issue-backend.js +145 -0
- package/lib/issue-render.js +220 -0
- package/lib/issue-sync/authority.js +100 -0
- package/lib/issue-sync/github-pull.js +184 -0
- package/lib/issue-sync/import-primitives.js +98 -0
- package/lib/issue-sync/legacy-link-bridge.js +436 -0
- package/lib/issue-sync/link-store.js +292 -0
- package/lib/issue-sync/project-github.js +123 -0
- package/lib/issue-sync/reconcile.js +195 -0
- package/lib/issue-sync/schema.js +126 -0
- package/lib/kernel/backing-issue.js +305 -0
- package/lib/kernel/broker.js +1218 -0
- package/lib/kernel/cli-broker-factory.js +130 -0
- package/lib/kernel/conflict-signal.js +82 -0
- package/lib/kernel/evaluators.js +195 -0
- package/lib/kernel/fs-class.js +495 -0
- package/lib/kernel/issue-command-contract.js +559 -0
- package/lib/kernel/issue-id-resolver.js +186 -0
- package/lib/kernel/lease-enforcer.js +158 -0
- package/lib/kernel/migrations.js +333 -0
- package/lib/kernel/planning-buckets-schema.js +109 -0
- package/lib/kernel/projection-jsonl-writer.js +450 -0
- package/lib/kernel/readiness-model.js +329 -0
- package/lib/kernel/schema.js +356 -0
- package/lib/kernel/sqlite-driver.js +2504 -0
- package/lib/kernel/taxonomy-validator.js +394 -0
- package/lib/lefthook-check.js +8 -4
- package/lib/lefthook-wiring.js +413 -0
- package/lib/mcp-config-renderer.js +288 -0
- package/lib/memory/graphiti-mcp.js +106 -0
- package/lib/memory/router.js +387 -0
- package/lib/memory/typed-api.js +102 -0
- package/lib/memory-digest.js +195 -0
- package/lib/merge-rules.js +395 -0
- package/lib/migrate-dry-run.js +466 -0
- package/lib/orientation.js +863 -0
- package/lib/package-manager-remediation.js +103 -0
- package/lib/package-root.js +381 -0
- package/lib/patch-intent.js +890 -0
- package/lib/plugin-catalog.js +3 -4
- package/lib/plugin-manager.js +0 -5
- package/lib/pr-bundle.js +186 -0
- package/lib/pr-monitor/differ.js +195 -0
- package/lib/pr-monitor/events.js +0 -0
- package/lib/pr-monitor/gather.js +124 -0
- package/lib/pr-monitor/journal.js +299 -0
- package/lib/pr-monitor/monitor.js +146 -0
- package/lib/pr-monitor/render-sticky.js +157 -0
- package/lib/pr-monitor/watch-lifecycle.js +95 -0
- package/lib/pr-monitor/watch.js +247 -0
- package/lib/pr-pull.js +1273 -0
- package/lib/pr-shepherd.js +494 -0
- package/lib/pr-state-validator.js +59 -0
- package/lib/preflight/gates.js +237 -0
- package/lib/preflight/runner.js +116 -0
- package/lib/project-discovery.js +0 -53
- package/lib/project-memory.js +166 -0
- package/lib/protected-path-manifest.js +281 -0
- package/lib/protected-state-surfaces.js +387 -0
- package/lib/release-readiness.js +2089 -0
- package/lib/reset.js +59 -45
- package/lib/review-adapter.js +68 -0
- package/lib/rules-sync.js +260 -0
- package/lib/runtime-health.js +332 -23
- package/lib/safety-config-renderer.js +268 -0
- package/lib/setup-action-log.js +1 -7
- package/lib/setup.js +27 -65
- package/lib/shell-utils.js +76 -6
- package/lib/skills-sync.js +330 -0
- package/lib/smart-status/conflicts.js +205 -0
- package/lib/smart-status/scoring.js +191 -0
- package/lib/status/beads-snapshot.js +145 -0
- package/lib/status/presenter.js +216 -0
- package/lib/status/snapshot.js +186 -0
- package/lib/sync-backend.js +202 -0
- package/lib/untrusted-content.js +52 -0
- package/lib/upgrade-safety.js +199 -0
- package/lib/workflow/enforce-stage.js +298 -47
- package/lib/workflow/stage-transition.js +115 -0
- package/lib/workflow/stages.js +30 -6
- package/lib/workflow/state-manager.js +159 -14
- package/lib/workflow/state.js +23 -1
- package/lib/workflow-profiles.js +17 -5
- package/package.json +46 -36
- package/rules/documentation.md +19 -0
- package/rules/kernel-tracking.md +26 -0
- package/rules/security.md +22 -0
- package/rules/tdd.md +20 -0
- package/rules/workflow.md +27 -0
- package/scripts/auto-backing-issue.js +47 -0
- package/scripts/beads-context.sh +165 -22
- package/scripts/beads-migrate-to-dolt.sh +7 -0
- package/scripts/beads-upgrade-smoke.sh +284 -0
- package/scripts/behavioral-judge.sh +115 -11
- package/scripts/benchmark.js +349 -63
- package/scripts/bootstrap-windows-tools.sh +78 -0
- package/scripts/branch-protection.js +2 -3
- package/scripts/check-agents.js +34 -137
- package/scripts/commitlint.js +3 -1
- package/scripts/conflict-detect.sh +3 -0
- package/scripts/dep-guard-analyze.js +52 -17
- package/scripts/dep-guard-keyword-ripple.js +29 -0
- package/scripts/dep-guard-render-review.js +86 -0
- package/scripts/dep-guard.sh +64 -232
- package/scripts/file-index.sh +3 -0
- package/scripts/forge-team/lib/claim.sh +34 -18
- package/scripts/forge-team/lib/dashboard.sh +61 -86
- package/scripts/forge-team/lib/epic.sh +99 -263
- package/scripts/forge-team/lib/hooks.sh +26 -28
- package/scripts/forge-team/lib/identity.sh +4 -4
- package/scripts/forge-team/lib/sync-github.sh +144 -47
- package/scripts/forge-team/lib/verify.sh +93 -83
- package/scripts/forge-team/lib/workload.sh +41 -65
- package/scripts/forge-team/tests/claim.test.sh +25 -19
- package/scripts/forge-team/tests/dashboard.test.sh +31 -46
- package/scripts/forge-team/tests/epic.test.sh +52 -71
- package/scripts/forge-team/tests/hooks.test.sh +38 -50
- package/scripts/forge-team/tests/identity.test.sh +3 -3
- package/scripts/forge-team/tests/integration.test.sh +44 -66
- package/scripts/forge-team/tests/sync-github.test.sh +183 -79
- package/scripts/forge-team/tests/verify.test.sh +37 -46
- package/scripts/forge-team/tests/workflow-integration.test.sh +4 -4
- package/scripts/forge-team/tests/workload.test.sh +32 -66
- package/scripts/gen-command-manifest.js +153 -0
- package/scripts/gen-embedded-assets.mjs +129 -0
- package/scripts/install.ps1 +139 -0
- package/scripts/install.sh +268 -0
- package/scripts/lib/beads-migrate-to-dolt.mjs +503 -0
- package/scripts/lib/release-asset.mjs +84 -0
- package/scripts/parity-check.mjs +145 -0
- package/scripts/parity-check.test.mjs +58 -0
- package/scripts/pin-agentic-workflow-images.js +112 -0
- package/scripts/pr-coordinator.sh +3 -0
- package/scripts/preflight-sonar.eslint.config.mjs +44 -0
- package/scripts/preflight.sh +108 -0
- package/scripts/protected-state-check.js +104 -0
- package/scripts/smart-status-score.js +31 -0
- package/scripts/smart-status-sessions.js +51 -0
- package/scripts/smart-status.sh +117 -369
- package/scripts/spikes/config-race-bench.js +111 -0
- package/scripts/spikes/harness-capability-matrix.js +13 -0
- package/scripts/spikes/patch-anchor-stability-bench.js +125 -0
- package/scripts/spikes/protected-path-manifest.js +20 -0
- package/scripts/spikes/skill-auto-invoke-parity.js +292 -0
- package/scripts/sync-agent-skills.js +62 -0
- package/scripts/sync-agentic-workflow.js +48 -0
- package/scripts/sync-utils.sh +3 -0
- package/scripts/test-ci-shard.js +251 -0
- package/scripts/test-dashboard.js +188 -52
- package/scripts/test-full-suite.js +186 -0
- package/scripts/test-profile.js +278 -0
- package/scripts/test.js +302 -28
- package/scripts/validate.js +143 -0
- package/scripts/validate.sh +18 -1
- package/skills/claim-safety/SKILL.md +102 -0
- package/skills/claim-safety/evals/evals.json +46 -0
- package/{.github/prompts/dev.prompt.md → skills/dev/SKILL.md} +46 -52
- package/skills/dev/evals/evals.json +50 -0
- package/skills/hermes-forge/SKILL.md +185 -0
- package/skills/hermes-forge/evals/evals.json +46 -0
- package/skills/issue-basics/SKILL.md +111 -0
- package/skills/issue-basics/evals/evals.json +46 -0
- package/skills/kernel/SKILL.md +166 -0
- package/skills/kernel/evals/evals.json +50 -0
- package/skills/memory/SKILL.md +102 -0
- package/skills/parallel-deep-research/SKILL.md +14 -11
- package/skills/parallel-deep-research/evals/evals.json +11 -27
- package/{.github/prompts/plan.prompt.md → skills/plan/SKILL.md} +134 -159
- package/skills/plan/evals/evals.json +42 -0
- package/skills/research/SKILL.md +195 -0
- package/skills/research/evals/evals.json +42 -0
- package/{.github/prompts/review.prompt.md → skills/review/SKILL.md} +98 -62
- package/skills/review/evals/evals.json +42 -0
- package/skills/rollback/SKILL.md +110 -0
- package/skills/rollback/evals/evals.json +46 -0
- package/skills/rollback/references/methods.md +204 -0
- package/{.cursor/commands/rollback.md → skills/rollback/references/workflow-integration.md} +10 -284
- package/skills/shepherd/SKILL.md +66 -0
- package/skills/shepherd/evals/evals.json +42 -0
- package/skills/ship/SKILL.md +251 -0
- package/skills/ship/evals/evals.json +42 -0
- package/skills/smith/SKILL.md +142 -0
- package/skills/smith/evals/evals.json +46 -0
- package/skills/smith/references/autonomy-and-gates.md +94 -0
- package/{.github/prompts/sonarcloud.prompt.md → skills/sonarcloud/SKILL.md} +14 -3
- package/skills/sonarcloud/evals/evals.json +46 -0
- package/skills/sonarcloud-analysis/SKILL.md +18 -13
- package/skills/sonarcloud-analysis/evals/evals.json +11 -15
- package/skills/status/SKILL.md +102 -0
- package/skills/status/evals/evals.json +50 -0
- package/skills/triage-ready/SKILL.md +121 -0
- package/skills/triage-ready/evals/evals.json +42 -0
- package/{.github/prompts/validate.prompt.md → skills/validate/SKILL.md} +52 -29
- package/skills/validate/evals/evals.json +42 -0
- package/skills/verify/SKILL.md +299 -0
- package/skills/verify/evals/evals.json +50 -0
- package/.claude/commands/dev.md +0 -345
- package/.claude/commands/plan.md +0 -566
- package/.claude/commands/premerge.md +0 -186
- package/.claude/commands/research.md +0 -42
- package/.claude/commands/review.md +0 -451
- package/.claude/commands/rollback.md +0 -721
- package/.claude/commands/ship.md +0 -213
- package/.claude/commands/sonarcloud.md +0 -152
- package/.claude/commands/status.md +0 -90
- package/.claude/commands/validate.md +0 -288
- package/.claude/commands/verify.md +0 -269
- package/.claude/rules/workflow.md +0 -121
- package/.cline/workflows/dev.md +0 -342
- package/.cline/workflows/plan.md +0 -563
- package/.cline/workflows/premerge.md +0 -183
- package/.cline/workflows/research.md +0 -39
- package/.cline/workflows/review.md +0 -448
- package/.cline/workflows/rollback.md +0 -718
- package/.cline/workflows/ship.md +0 -210
- package/.cline/workflows/sonarcloud.md +0 -146
- package/.cline/workflows/status.md +0 -87
- package/.cline/workflows/validate.md +0 -285
- package/.cline/workflows/verify.md +0 -266
- package/.codex/config.toml +0 -11
- package/.codex/skills/dev/SKILL.md +0 -345
- package/.codex/skills/plan/SKILL.md +0 -566
- package/.codex/skills/premerge/SKILL.md +0 -186
- package/.codex/skills/research/SKILL.md +0 -42
- package/.codex/skills/review/SKILL.md +0 -451
- package/.codex/skills/rollback/SKILL.md +0 -721
- package/.codex/skills/ship/SKILL.md +0 -213
- package/.codex/skills/sonarcloud/SKILL.md +0 -149
- package/.codex/skills/status/SKILL.md +0 -90
- package/.codex/skills/validate/SKILL.md +0 -288
- package/.codex/skills/verify/SKILL.md +0 -269
- package/.cursor/commands/dev.md +0 -342
- package/.cursor/commands/plan.md +0 -563
- package/.cursor/commands/premerge.md +0 -183
- package/.cursor/commands/research.md +0 -39
- package/.cursor/commands/review.md +0 -448
- package/.cursor/commands/ship.md +0 -210
- package/.cursor/commands/sonarcloud.md +0 -146
- package/.cursor/commands/status.md +0 -87
- package/.cursor/commands/validate.md +0 -285
- package/.cursor/commands/verify.md +0 -266
- package/.cursorrules +0 -149
- package/.github/prompts/premerge.prompt.md +0 -188
- package/.github/prompts/research.prompt.md +0 -44
- package/.github/prompts/rollback.prompt.md +0 -723
- package/.github/prompts/ship.prompt.md +0 -215
- package/.github/prompts/status.prompt.md +0 -92
- package/.github/prompts/verify.prompt.md +0 -271
- package/.github/workflows/beads-to-github.yml +0 -56
- package/.github/workflows/github-to-beads.yml +0 -97
- package/.kilocode/workflows/dev.md +0 -346
- package/.kilocode/workflows/plan.md +0 -567
- package/.kilocode/workflows/premerge.md +0 -187
- package/.kilocode/workflows/research.md +0 -43
- package/.kilocode/workflows/review.md +0 -452
- package/.kilocode/workflows/rollback.md +0 -722
- package/.kilocode/workflows/ship.md +0 -214
- package/.kilocode/workflows/sonarcloud.md +0 -150
- package/.kilocode/workflows/status.md +0 -91
- package/.kilocode/workflows/validate.md +0 -289
- package/.kilocode/workflows/verify.md +0 -270
- package/.opencode/commands/dev.md +0 -345
- package/.opencode/commands/plan.md +0 -566
- package/.opencode/commands/premerge.md +0 -186
- package/.opencode/commands/research.md +0 -42
- package/.opencode/commands/review.md +0 -451
- package/.opencode/commands/rollback.md +0 -721
- package/.opencode/commands/ship.md +0 -213
- package/.opencode/commands/sonarcloud.md +0 -149
- package/.opencode/commands/status.md +0 -90
- package/.opencode/commands/validate.md +0 -288
- package/.opencode/commands/verify.md +0 -269
- package/.roo/commands/dev.md +0 -346
- package/.roo/commands/plan.md +0 -567
- package/.roo/commands/premerge.md +0 -187
- package/.roo/commands/research.md +0 -43
- package/.roo/commands/review.md +0 -452
- package/.roo/commands/rollback.md +0 -722
- package/.roo/commands/ship.md +0 -214
- package/.roo/commands/sonarcloud.md +0 -150
- package/.roo/commands/status.md +0 -91
- package/.roo/commands/validate.md +0 -289
- package/.roo/commands/verify.md +0 -270
- package/docs/BEADS_GITHUB_SYNC.md +0 -255
- package/docs/GREPTILE_SETUP.md +0 -400
- package/docs/MANUAL_REVIEW_GUIDE.md +0 -106
- package/docs/SETUP.md +0 -663
- package/docs/VALIDATION.md +0 -363
- package/lib/agents/cline.plugin.json +0 -29
- package/lib/agents/copilot.plugin.json +0 -24
- package/lib/agents/kilocode.plugin.json +0 -22
- package/lib/agents/opencode.plugin.json +0 -23
- package/lib/agents/roo.plugin.json +0 -30
- package/lib/beads-health-check.js +0 -143
- package/lib/commands/commands-reset.js +0 -147
- package/opencode.json +0 -67
- package/scripts/beads-context.test.js +0 -567
- package/scripts/github-beads-sync/comment.mjs +0 -64
- package/scripts/github-beads-sync/config.mjs +0 -148
- package/scripts/github-beads-sync/github-api.mjs +0 -131
- package/scripts/github-beads-sync/index.mjs +0 -332
- package/scripts/github-beads-sync/label-mapper.mjs +0 -54
- package/scripts/github-beads-sync/mapping.mjs +0 -78
- package/scripts/github-beads-sync/reverse-sync-cli.mjs +0 -31
- package/scripts/github-beads-sync/reverse-sync.mjs +0 -138
- package/scripts/github-beads-sync/run-bd.mjs +0 -161
- package/scripts/github-beads-sync/sanitize.mjs +0 -121
- package/scripts/github-beads-sync.config.json +0 -26
- package/scripts/sync-commands.js +0 -600
|
@@ -0,0 +1,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
|
+
};
|
package/lib/beads-setup.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Utilities for configuring Beads during `forge setup`.
|
|
3
3
|
*
|
|
4
4
|
* Handles prefix sanitization, config/gitignore generation,
|
|
5
|
-
* initialization detection, and
|
|
5
|
+
* initialization detection, and legacy JSONL compatibility shims.
|
|
6
6
|
*
|
|
7
7
|
* @module beads-setup
|
|
8
8
|
*/
|
|
@@ -10,6 +10,22 @@
|
|
|
10
10
|
const fs = require('node:fs');
|
|
11
11
|
const path = require('node:path');
|
|
12
12
|
const { spawnSync } = require('node:child_process');
|
|
13
|
+
const { secureExecFileSync } = require('./shell-utils');
|
|
14
|
+
|
|
15
|
+
const BEADS_LOCAL_EXCLUDE_LINES = [
|
|
16
|
+
'# Forge local Beads state',
|
|
17
|
+
'.beads/',
|
|
18
|
+
'.dolt/',
|
|
19
|
+
'.beads-credential-key'
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
function isIgnorableFsError(error) {
|
|
23
|
+
return ['EACCES', 'ENOENT', 'ENOTDIR', 'EPERM'].includes(error?.code);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function isRecoverableCommandError(error) {
|
|
27
|
+
return error?.code === 'ENOENT' || typeof error?.status === 'number';
|
|
28
|
+
}
|
|
13
29
|
|
|
14
30
|
/**
|
|
15
31
|
* Resolve a command to its full path to avoid relying on inherited PATH.
|
|
@@ -24,7 +40,10 @@ function resolveCommand(command) {
|
|
|
24
40
|
if (result.status === 0 && result.stdout) {
|
|
25
41
|
return result.stdout.trim().split(/\r?\n/)[0].trim();
|
|
26
42
|
}
|
|
27
|
-
} catch (
|
|
43
|
+
} catch (error) {
|
|
44
|
+
if (!isRecoverableCommandError(error)) {
|
|
45
|
+
throw error;
|
|
46
|
+
}
|
|
28
47
|
// Expected: command resolution may fail if 'which'/'where.exe' is unavailable — fall back to bare command name
|
|
29
48
|
}
|
|
30
49
|
return command;
|
|
@@ -91,6 +110,10 @@ function writeBeadsGitignore(projectRoot) {
|
|
|
91
110
|
fs.mkdirSync(beadsDir, { recursive: true });
|
|
92
111
|
|
|
93
112
|
const gitignoreContent = [
|
|
113
|
+
'# Beads runtime, export, and backup state is local',
|
|
114
|
+
'*',
|
|
115
|
+
'!.gitignore',
|
|
116
|
+
'',
|
|
94
117
|
'# Dolt database files (binary, not suitable for git)',
|
|
95
118
|
'dolt/',
|
|
96
119
|
'*.db',
|
|
@@ -105,13 +128,120 @@ function writeBeadsGitignore(projectRoot) {
|
|
|
105
128
|
);
|
|
106
129
|
}
|
|
107
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
|
+
|
|
108
233
|
/**
|
|
109
234
|
* Check whether Beads is properly initialized in a project.
|
|
110
235
|
*
|
|
111
|
-
* Returns `true`
|
|
236
|
+
* Returns `true` when all of the following are present:
|
|
112
237
|
* - `.beads/` directory exists
|
|
113
238
|
* - `.beads/config.yaml` exists and contains an `issue-prefix` key
|
|
114
|
-
* -
|
|
239
|
+
* - the config points to either:
|
|
240
|
+
* - a legacy SQLite tracker with legacy data markers present, or
|
|
241
|
+
* - a Dolt-backed tracker with at least one post-`bd init` marker present
|
|
242
|
+
*
|
|
243
|
+
* Legacy SQLite-backed Beads installs also count as initialized so `forge setup`
|
|
244
|
+
* does not run `bd init` over an existing tracker before migration.
|
|
115
245
|
*
|
|
116
246
|
* @param {string} projectRoot - Absolute path to the project root
|
|
117
247
|
* @returns {boolean} `true` if Beads is fully initialized
|
|
@@ -119,34 +249,74 @@ function writeBeadsGitignore(projectRoot) {
|
|
|
119
249
|
function isBeadsInitialized(projectRoot) {
|
|
120
250
|
const beadsDir = path.join(projectRoot, '.beads');
|
|
121
251
|
const configPath = path.join(beadsDir, 'config.yaml');
|
|
122
|
-
const jsonlPath = path.join(beadsDir, 'issues.jsonl');
|
|
123
252
|
|
|
124
253
|
if (!fs.existsSync(beadsDir)) return false;
|
|
125
254
|
if (!fs.existsSync(configPath)) return false;
|
|
126
|
-
if (!fs.existsSync(jsonlPath)) return false;
|
|
127
255
|
|
|
128
256
|
const configContent = fs.readFileSync(configPath, 'utf8');
|
|
129
257
|
if (!configContent.includes('issue-prefix:')) return false;
|
|
130
258
|
|
|
131
|
-
|
|
259
|
+
const hasLegacyState = [
|
|
260
|
+
'issues.jsonl',
|
|
261
|
+
'backup',
|
|
262
|
+
'bd.db',
|
|
263
|
+
'beads.db',
|
|
264
|
+
'db.sqlite'
|
|
265
|
+
].some((entry) => fs.existsSync(path.join(beadsDir, entry)));
|
|
266
|
+
const hasDoltMetadata = fs.existsSync(path.join(beadsDir, 'metadata.json'));
|
|
267
|
+
const hasDoltCompanionState = [
|
|
268
|
+
'README.md',
|
|
269
|
+
'hooks',
|
|
270
|
+
'redirect'
|
|
271
|
+
].some((entry) => fs.existsSync(path.join(beadsDir, entry)));
|
|
272
|
+
const hasDoltState = hasDoltMetadata && hasDoltCompanionState;
|
|
273
|
+
|
|
274
|
+
if (configContent.includes('backend: sqlite')) {
|
|
275
|
+
return hasLegacyState;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
if (configContent.includes('backend: dolt')) {
|
|
279
|
+
return hasDoltState;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
return hasLegacyState || hasDoltState;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function readBeadsDatabaseName(projectRoot) {
|
|
286
|
+
const metadataPath = path.join(projectRoot, '.beads', 'metadata.json');
|
|
287
|
+
if (!fs.existsSync(metadataPath)) {
|
|
288
|
+
return null;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
try {
|
|
292
|
+
const metadata = JSON.parse(fs.readFileSync(metadataPath, 'utf8'));
|
|
293
|
+
if (typeof metadata.dolt_database === 'string' && metadata.dolt_database.trim()) {
|
|
294
|
+
return metadata.dolt_database.trim();
|
|
295
|
+
}
|
|
296
|
+
if (typeof metadata.database === 'string' && metadata.database.trim()) {
|
|
297
|
+
return metadata.database.trim();
|
|
298
|
+
}
|
|
299
|
+
} catch (error) {
|
|
300
|
+
if (error instanceof SyntaxError || isIgnorableFsError(error)) {
|
|
301
|
+
return null;
|
|
302
|
+
}
|
|
303
|
+
throw error;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
return null;
|
|
132
307
|
}
|
|
133
308
|
|
|
134
309
|
/**
|
|
135
|
-
*
|
|
310
|
+
* Legacy compatibility shim for the removed JSONL pre-seed workaround.
|
|
136
311
|
*
|
|
137
|
-
*
|
|
138
|
-
*
|
|
312
|
+
* Beads latest no longer requires `.beads/issues.jsonl` during init, so
|
|
313
|
+
* this helper intentionally does nothing. It remains exported because some
|
|
314
|
+
* older tests and setup paths still import it.
|
|
139
315
|
*
|
|
140
|
-
* @param {string}
|
|
316
|
+
* @param {string} _projectRoot - Absolute path to the project root
|
|
141
317
|
*/
|
|
142
|
-
function preSeedJsonl(
|
|
143
|
-
|
|
144
|
-
fs.mkdirSync(beadsDir, { recursive: true });
|
|
145
|
-
|
|
146
|
-
const jsonlPath = path.join(beadsDir, 'issues.jsonl');
|
|
147
|
-
if (!fs.existsSync(jsonlPath)) {
|
|
148
|
-
fs.writeFileSync(jsonlPath, '', 'utf8');
|
|
149
|
-
}
|
|
318
|
+
function preSeedJsonl(_projectRoot) {
|
|
319
|
+
// Legacy no-op: Dolt-backed Beads no longer requires JSONL pre-seeding.
|
|
150
320
|
}
|
|
151
321
|
|
|
152
322
|
// ---------------------------------------------------------------------------
|
|
@@ -212,6 +382,31 @@ function restoreDirectory(dirPath, snapshot) {
|
|
|
212
382
|
}
|
|
213
383
|
}
|
|
214
384
|
|
|
385
|
+
function resolveHooksDir(projectRoot) {
|
|
386
|
+
const fallbackHooksDir = path.join(projectRoot, '.git', 'hooks');
|
|
387
|
+
|
|
388
|
+
try {
|
|
389
|
+
const gitHooksPath = secureExecFileSync(resolveCommand('git'), ['rev-parse', '--git-path', 'hooks'], {
|
|
390
|
+
cwd: projectRoot,
|
|
391
|
+
encoding: 'utf8',
|
|
392
|
+
stdio: 'pipe'
|
|
393
|
+
}).trim();
|
|
394
|
+
|
|
395
|
+
if (!gitHooksPath) {
|
|
396
|
+
return fallbackHooksDir;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
return path.isAbsolute(gitHooksPath)
|
|
400
|
+
? gitHooksPath
|
|
401
|
+
: path.resolve(projectRoot, gitHooksPath);
|
|
402
|
+
} catch (error) {
|
|
403
|
+
if (isRecoverableCommandError(error)) {
|
|
404
|
+
return fallbackHooksDir;
|
|
405
|
+
}
|
|
406
|
+
throw error;
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
|
|
215
410
|
/**
|
|
216
411
|
* Default bd init executor — calls `execFileSync('bd', ['init'])`.
|
|
217
412
|
*
|
|
@@ -237,7 +432,7 @@ function defaultExecBdInit(projectRoot) {
|
|
|
237
432
|
* e. Run bd init (via execBdInit callback or default execFileSync)
|
|
238
433
|
* f. Restore .git/hooks/ from snapshot
|
|
239
434
|
* g. Restore lefthook hooks if callback provided
|
|
240
|
-
* h.
|
|
435
|
+
* h. Skip the removed issues.jsonl pre-seed workaround
|
|
241
436
|
*
|
|
242
437
|
* @param {string} projectRoot - Absolute path to the project root
|
|
243
438
|
* @param {Object} [options={}] - Configuration options
|
|
@@ -252,6 +447,13 @@ function safeBeadsInit(projectRoot, options = {}) {
|
|
|
252
447
|
const warnings = [];
|
|
253
448
|
const errors = [];
|
|
254
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
|
+
|
|
255
457
|
// (a) Check if already initialized — idempotent skip
|
|
256
458
|
if (isBeadsInitialized(projectRoot)) {
|
|
257
459
|
return {
|
|
@@ -279,7 +481,7 @@ function safeBeadsInit(projectRoot, options = {}) {
|
|
|
279
481
|
}
|
|
280
482
|
|
|
281
483
|
// (d) Snapshot current .git/hooks/ directory
|
|
282
|
-
const hooksDir =
|
|
484
|
+
const hooksDir = resolveHooksDir(projectRoot);
|
|
283
485
|
const hooksSnapshot = snapshotDirectory(hooksDir);
|
|
284
486
|
|
|
285
487
|
// (e) Run bd init — wrapped in try/catch
|
|
@@ -316,13 +518,6 @@ function safeBeadsInit(projectRoot, options = {}) {
|
|
|
316
518
|
}
|
|
317
519
|
}
|
|
318
520
|
|
|
319
|
-
// (h) Pre-seed JSONL if empty
|
|
320
|
-
try {
|
|
321
|
-
preSeedJsonl(projectRoot);
|
|
322
|
-
} catch (err) {
|
|
323
|
-
warnings.push(`Failed to pre-seed issues.jsonl: ${err.message}`);
|
|
324
|
-
}
|
|
325
|
-
|
|
326
521
|
return {
|
|
327
522
|
success: true,
|
|
328
523
|
skipped: false,
|
|
@@ -335,7 +530,9 @@ module.exports = {
|
|
|
335
530
|
sanitizePrefix,
|
|
336
531
|
writeBeadsConfig,
|
|
337
532
|
writeBeadsGitignore,
|
|
533
|
+
ensureBeadsGitExclude,
|
|
338
534
|
isBeadsInitialized,
|
|
535
|
+
readBeadsDatabaseName,
|
|
339
536
|
preSeedJsonl,
|
|
340
537
|
safeBeadsInit
|
|
341
538
|
};
|