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
package/lib/commands/status.js
CHANGED
|
@@ -8,12 +8,26 @@ const {
|
|
|
8
8
|
getAllowedTransitionsForWorkflowState,
|
|
9
9
|
} = require('../workflow/state');
|
|
10
10
|
const { STAGE_IDS } = require('../workflow/stages');
|
|
11
|
+
const { detectWorktree } = require('../detect-worktree');
|
|
12
|
+
const { readStatusSnapshot } = require('../status/snapshot');
|
|
13
|
+
const {
|
|
14
|
+
buildPersonalStatusJson,
|
|
15
|
+
formatRunNextLines,
|
|
16
|
+
formatZeroArgStatus,
|
|
17
|
+
} = require('../status/presenter');
|
|
11
18
|
const {
|
|
12
19
|
loadState,
|
|
13
20
|
extractWorkflowStateFromComments,
|
|
14
21
|
readWorkflowStateFromBeads,
|
|
15
22
|
} = require('../workflow/state-manager');
|
|
16
|
-
|
|
23
|
+
const { secureExecFileSync } = require('../shell-utils.js');
|
|
24
|
+
|
|
25
|
+
// Lifecycle progression used for status display. Stage 8 'Merge' is the merge
|
|
26
|
+
// EVENT (a human GitHub action — Forge exposes no `merge` command) that lands after
|
|
27
|
+
// the review stage and before post-merge /verify. Pre-merge doc/CI/handoff checks
|
|
28
|
+
// are an embedded gate inside /ship and /review, not a standalone stage, so this
|
|
29
|
+
// ladder never gains a premerge stage. (AUTHORITATIVE_STAGE_NAMES below still maps
|
|
30
|
+
// the legacy 'premerge' id purely so historical workflow state round-trips.)
|
|
17
31
|
const WORKFLOW_STAGES = {
|
|
18
32
|
1: { name: 'Fresh Start', nextCommand: 'research' },
|
|
19
33
|
2: { name: 'Research', nextCommand: 'research' },
|
|
@@ -26,6 +40,10 @@ const WORKFLOW_STAGES = {
|
|
|
26
40
|
9: { name: 'Verification', nextCommand: 'verify' },
|
|
27
41
|
};
|
|
28
42
|
|
|
43
|
+
// Legacy read-path name map. `premerge` is retired as a standalone stage (it is now
|
|
44
|
+
// an embedded gate in /ship and /review) but is intentionally KEPT here so a stored
|
|
45
|
+
// `currentStage: 'premerge'` from a schemaVersion 1 workflow still resolves a name
|
|
46
|
+
// on read — deleting it would break upgraders (see test/status-command.test.js).
|
|
29
47
|
const AUTHORITATIVE_STAGE_NAMES = {
|
|
30
48
|
plan: 'Planning',
|
|
31
49
|
dev: 'Development',
|
|
@@ -325,10 +343,15 @@ function detectStage(context) {
|
|
|
325
343
|
|
|
326
344
|
function parseStatusInputs(args = [], flags = {}) {
|
|
327
345
|
const statusArgs = Array.isArray(args) ? args : [];
|
|
346
|
+
const firstDefined = (...values) => values.find(value => value !== undefined && value !== null);
|
|
328
347
|
const getNextValue = (flagName) => {
|
|
329
348
|
const index = statusArgs.indexOf(flagName);
|
|
330
349
|
if (index !== -1 && index + 1 < statusArgs.length) {
|
|
331
|
-
|
|
350
|
+
const candidate = statusArgs[index + 1];
|
|
351
|
+
if (typeof candidate === 'string' && candidate.startsWith('--')) {
|
|
352
|
+
return null;
|
|
353
|
+
}
|
|
354
|
+
return candidate;
|
|
332
355
|
}
|
|
333
356
|
return null;
|
|
334
357
|
};
|
|
@@ -339,13 +362,246 @@ function parseStatusInputs(args = [], flags = {}) {
|
|
|
339
362
|
};
|
|
340
363
|
|
|
341
364
|
return {
|
|
342
|
-
issueId: flags.issueId
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
365
|
+
issueId: firstDefined(flags.issueId, flags['--issue-id'], getInlineValue('--issue-id'), getNextValue('--issue-id')),
|
|
366
|
+
issueBackend: firstDefined(flags.issueBackend, flags['--issue-backend'], getInlineValue('--issue-backend'), getNextValue('--issue-backend')) || null,
|
|
367
|
+
workflowState: firstDefined(flags.workflowState, flags['--workflow-state'], getInlineValue('--workflow-state'), getNextValue('--workflow-state')),
|
|
368
|
+
bdComments: firstDefined(flags.bdComments, flags['--bd-comments'], getInlineValue('--bd-comments'), getNextValue('--bd-comments')),
|
|
369
|
+
projectRoot: firstDefined(flags.projectRoot, flags['--project-root'], getInlineValue('--project-root'), getNextValue('--project-root')) || null,
|
|
370
|
+
json: flags.json === true || flags['--json'] === true || statusArgs.includes('--json'),
|
|
371
|
+
full: flags.full === true || flags['--full'] === true || statusArgs.includes('--full'),
|
|
372
|
+
now: firstDefined(flags.now, flags['--now'], getInlineValue('--now'), getNextValue('--now')),
|
|
373
|
+
staleAfterDays: firstDefined(
|
|
374
|
+
flags.staleAfterDays,
|
|
375
|
+
flags['--stale-after-days'],
|
|
376
|
+
flags['--staleAfterDays'],
|
|
377
|
+
getInlineValue('--stale-after-days'),
|
|
378
|
+
getInlineValue('--staleAfterDays'),
|
|
379
|
+
getNextValue('--stale-after-days'),
|
|
380
|
+
getNextValue('--staleAfterDays')
|
|
381
|
+
),
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
function runGitCommand(projectRoot, args) {
|
|
386
|
+
if (!projectRoot) {
|
|
387
|
+
return '';
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
try {
|
|
391
|
+
return secureExecFileSync('git', args, {
|
|
392
|
+
encoding: 'utf8',
|
|
393
|
+
cwd: projectRoot,
|
|
394
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
395
|
+
}).trim();
|
|
396
|
+
} catch (_error) {
|
|
397
|
+
return '';
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
function detectRepoContext(projectRoot = process.cwd()) {
|
|
402
|
+
const worktree = detectWorktree(projectRoot);
|
|
403
|
+
const branch = worktree.branch || runGitCommand(projectRoot, ['branch', '--show-current']) || 'unknown';
|
|
404
|
+
const statusOutput = runGitCommand(projectRoot, ['status', '--short']);
|
|
405
|
+
const changedEntries = statusOutput ? statusOutput.split(/\r?\n/).filter(Boolean) : [];
|
|
406
|
+
const clean = changedEntries.length === 0;
|
|
407
|
+
const summary = clean ? 'clean' : `${changedEntries.length} uncommitted change${changedEntries.length === 1 ? '' : 's'}`;
|
|
408
|
+
|
|
409
|
+
return {
|
|
410
|
+
branch,
|
|
411
|
+
inWorktree: worktree.inWorktree === true,
|
|
412
|
+
worktreePath: worktree.currentWorktree || projectRoot,
|
|
413
|
+
mainWorktree: worktree.mainWorktree || projectRoot,
|
|
414
|
+
workingTree: {
|
|
415
|
+
clean,
|
|
416
|
+
summary,
|
|
417
|
+
},
|
|
346
418
|
};
|
|
347
419
|
}
|
|
348
420
|
|
|
421
|
+
function readIssueDetails(issueId, projectRoot = process.cwd()) {
|
|
422
|
+
if (!issueId) {
|
|
423
|
+
return null;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
try {
|
|
427
|
+
const raw = secureExecFileSync('bd', ['show', issueId, '--json'], {
|
|
428
|
+
encoding: 'utf8',
|
|
429
|
+
cwd: projectRoot,
|
|
430
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
431
|
+
}).trim();
|
|
432
|
+
if (!raw) {
|
|
433
|
+
return null;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
const parsed = JSON.parse(raw);
|
|
437
|
+
return Array.isArray(parsed) ? parsed[0] || null : parsed;
|
|
438
|
+
} catch (_error) {
|
|
439
|
+
return null;
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
function stripDatePrefix(value) {
|
|
444
|
+
const datePrefixLength = 11;
|
|
445
|
+
const datePrefix =
|
|
446
|
+
value.length > datePrefixLength &&
|
|
447
|
+
value[4] === '-' &&
|
|
448
|
+
value[7] === '-' &&
|
|
449
|
+
value[10] === '-';
|
|
450
|
+
return datePrefix ? value.slice(datePrefixLength) : value;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
function trimTrailingPathPunctuation(value) {
|
|
454
|
+
const trailingPunctuation = '.,;:';
|
|
455
|
+
let end = value.length;
|
|
456
|
+
while (end > 0 && trailingPunctuation.includes(value[end - 1])) {
|
|
457
|
+
end -= 1;
|
|
458
|
+
}
|
|
459
|
+
return value.slice(0, end);
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
function stripMarkdownSuffixes(value) {
|
|
463
|
+
let stripped = value.endsWith('.md') ? value.slice(0, -3) : value;
|
|
464
|
+
for (const suffix of ['-design', '-plan', '-tasks', '-decisions']) {
|
|
465
|
+
if (stripped.endsWith(suffix)) {
|
|
466
|
+
stripped = stripped.slice(0, -suffix.length);
|
|
467
|
+
break;
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
return stripped;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
function extractSlugFromDesignPath(candidatePath) {
|
|
474
|
+
const parts = trimTrailingPathPunctuation(candidatePath).replaceAll('\\', '/').split('/').filter(Boolean);
|
|
475
|
+
const docsIndex = parts.indexOf('docs');
|
|
476
|
+
if (docsIndex === -1) {
|
|
477
|
+
return '';
|
|
478
|
+
}
|
|
479
|
+
const root = parts[docsIndex + 1];
|
|
480
|
+
|
|
481
|
+
if (root === 'plans') {
|
|
482
|
+
const fileName = parts[docsIndex + 2] || '';
|
|
483
|
+
return stripDatePrefix(stripMarkdownSuffixes(fileName));
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
if (root === 'work') {
|
|
487
|
+
return stripDatePrefix(parts[docsIndex + 2] || '');
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
return '';
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
function isPathCandidateChar(char) {
|
|
494
|
+
return (
|
|
495
|
+
(char >= 'a' && char <= 'z') ||
|
|
496
|
+
(char >= 'A' && char <= 'Z') ||
|
|
497
|
+
(char >= '0' && char <= '9') ||
|
|
498
|
+
char === '/' ||
|
|
499
|
+
char === '\\' ||
|
|
500
|
+
char === '.' ||
|
|
501
|
+
char === '-' ||
|
|
502
|
+
char === '_'
|
|
503
|
+
);
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
function extractPathCandidates(text) {
|
|
507
|
+
const candidates = [];
|
|
508
|
+
let index = 0;
|
|
509
|
+
|
|
510
|
+
while (index < text.length) {
|
|
511
|
+
const hasForwardPath = text.startsWith('docs/', index);
|
|
512
|
+
const hasBackwardPath = text.startsWith('docs\\', index);
|
|
513
|
+
|
|
514
|
+
if (!hasForwardPath && !hasBackwardPath) {
|
|
515
|
+
index += 1;
|
|
516
|
+
continue;
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
let end = index;
|
|
520
|
+
while (end < text.length && isPathCandidateChar(text[end])) {
|
|
521
|
+
end += 1;
|
|
522
|
+
}
|
|
523
|
+
candidates.push(text.slice(index, end));
|
|
524
|
+
index = end;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
return candidates;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
function extractDesignSlugs(design) {
|
|
531
|
+
if (typeof design !== 'string' || design.trim() === '') {
|
|
532
|
+
return [];
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
const slugs = new Set();
|
|
536
|
+
for (const candidate of extractPathCandidates(design)) {
|
|
537
|
+
const slug = extractSlugFromDesignPath(candidate);
|
|
538
|
+
if (slug) {
|
|
539
|
+
slugs.add(slug);
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
return [...slugs];
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
function designMatchesFeatureSlug(issue, featureSlug) {
|
|
547
|
+
if (!issue || !featureSlug) {
|
|
548
|
+
return false;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
return extractDesignSlugs(issue.design).includes(featureSlug);
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
function discoverCurrentIssue(snapshot, context, projectRoot = process.cwd()) {
|
|
555
|
+
const activeAssigned = snapshot.activeAssigned || [];
|
|
556
|
+
if (activeAssigned.length === 0) {
|
|
557
|
+
return null;
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
const branchInfo = analyzeBranch(context.branch || 'master');
|
|
561
|
+
const hydratedIssues = new Map();
|
|
562
|
+
|
|
563
|
+
function hydrateIssue(issue) {
|
|
564
|
+
if (!issue || !issue.id) {
|
|
565
|
+
return issue;
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
if (hydratedIssues.has(issue.id)) {
|
|
569
|
+
return hydratedIssues.get(issue.id);
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
const hasSufficientDetails = Boolean(issue.comments) && Boolean(issue.design);
|
|
573
|
+
const details = hasSufficientDetails ? issue : readIssueDetails(issue.id, projectRoot);
|
|
574
|
+
const hydratedIssue = details ? { ...issue, ...details } : issue;
|
|
575
|
+
hydratedIssues.set(issue.id, hydratedIssue);
|
|
576
|
+
return hydratedIssue;
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
if (branchInfo.featureSlug) {
|
|
580
|
+
const slugMatches = [];
|
|
581
|
+
|
|
582
|
+
for (const issue of activeAssigned) {
|
|
583
|
+
const candidate = issue.design ? issue : hydrateIssue(issue);
|
|
584
|
+
if (designMatchesFeatureSlug(candidate, branchInfo.featureSlug)) {
|
|
585
|
+
slugMatches.push(candidate);
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
if (slugMatches.length === 1) {
|
|
590
|
+
return hydrateIssue(slugMatches[0]);
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
if (slugMatches.length > 1) {
|
|
594
|
+
return null;
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
if (activeAssigned.length === 1) {
|
|
599
|
+
return hydrateIssue(activeAssigned[0]);
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
return null;
|
|
603
|
+
}
|
|
604
|
+
|
|
349
605
|
function resolveWorkflowState(inputs) {
|
|
350
606
|
try {
|
|
351
607
|
if (inputs.workflowState) {
|
|
@@ -354,7 +610,9 @@ function resolveWorkflowState(inputs) {
|
|
|
354
610
|
|
|
355
611
|
const { state } = loadState(inputs.projectRoot, {
|
|
356
612
|
issueId: inputs.issueId,
|
|
613
|
+
issue: inputs.issue,
|
|
357
614
|
comments: inputs.bdComments,
|
|
615
|
+
preferBeads: inputs.preferBeads,
|
|
358
616
|
});
|
|
359
617
|
|
|
360
618
|
return { workflowState: state, fallbackReason: null };
|
|
@@ -440,11 +698,8 @@ function collectCompletedChecks(result) {
|
|
|
440
698
|
function formatAuthoritativeStatus(result) {
|
|
441
699
|
const completedStages = result.workflowState.completedStages.map(stageId => stageId);
|
|
442
700
|
const allowedCommands = result.nextStages.map(stageId => '/' + stageId).join(', ');
|
|
443
|
-
const nextLines =
|
|
701
|
+
const nextLines = formatRunNextLines(result);
|
|
444
702
|
|
|
445
|
-
if (result.nextCommand) {
|
|
446
|
-
nextLines.push(`Next after this: /${result.nextCommand}`);
|
|
447
|
-
}
|
|
448
703
|
if (allowedCommands) {
|
|
449
704
|
nextLines.push(`Allowed after this: ${allowedCommands}`);
|
|
450
705
|
}
|
|
@@ -495,14 +750,74 @@ function formatStatus(result) {
|
|
|
495
750
|
|
|
496
751
|
module.exports = {
|
|
497
752
|
name: 'status',
|
|
498
|
-
description: '
|
|
753
|
+
description: 'One-glance orientation: where you are, what to run next, and your work',
|
|
754
|
+
usage: 'forge status [--full] [--json]',
|
|
755
|
+
flags: {
|
|
756
|
+
'--full': 'Also show blocked, stale, and recently completed issues',
|
|
757
|
+
'--json': 'Emit the machine-readable status envelope',
|
|
758
|
+
},
|
|
499
759
|
handler: async (args, flags, projectRoot) => {
|
|
500
760
|
const inputs = parseStatusInputs(args, flags);
|
|
761
|
+
const isZeroArgStatus = !inputs.issueId && !inputs.workflowState && !inputs.bdComments;
|
|
762
|
+
inputs.preferBeads = !isZeroArgStatus && Boolean(inputs.issueId || inputs.bdComments);
|
|
501
763
|
if (!inputs.projectRoot && projectRoot) {
|
|
502
764
|
inputs.projectRoot = projectRoot;
|
|
503
765
|
}
|
|
766
|
+
const effectiveProjectRoot = inputs.projectRoot || process.cwd();
|
|
767
|
+
let context = null;
|
|
768
|
+
let snapshot = null;
|
|
769
|
+
|
|
770
|
+
if (isZeroArgStatus) {
|
|
771
|
+
context = detectRepoContext(effectiveProjectRoot);
|
|
772
|
+
// Read from the active issue backend (Kernel by default; Beads only when
|
|
773
|
+
// explicitly selected). Previously hard-wired to the retired Beads
|
|
774
|
+
// issues.jsonl, which is empty on a kernel-default repo — so the flagship
|
|
775
|
+
// view showed "Ready: none" despite a populated kernel (bug 40f35797).
|
|
776
|
+
snapshot = await readStatusSnapshot(effectiveProjectRoot, {
|
|
777
|
+
now: inputs.now,
|
|
778
|
+
staleAfterDays: inputs.staleAfterDays,
|
|
779
|
+
issueBackend: inputs.issueBackend,
|
|
780
|
+
env: process.env,
|
|
781
|
+
});
|
|
782
|
+
const discoveredIssue = discoverCurrentIssue(snapshot, context, effectiveProjectRoot);
|
|
783
|
+
if (discoveredIssue) {
|
|
784
|
+
inputs.issueId = discoveredIssue.id;
|
|
785
|
+
inputs.issue = discoveredIssue;
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
|
|
504
789
|
const { workflowState, fallbackReason } = resolveWorkflowState(inputs);
|
|
505
790
|
|
|
791
|
+
if (isZeroArgStatus) {
|
|
792
|
+
if (workflowState) {
|
|
793
|
+
const result = buildAuthoritativeStatus(workflowState);
|
|
794
|
+
const output = inputs.json
|
|
795
|
+
? JSON.stringify(buildPersonalStatusJson({ context, snapshot, workflowResult: result }), null, 2)
|
|
796
|
+
: formatZeroArgStatus({ context, snapshot, workflowResult: result, full: inputs.full });
|
|
797
|
+
return {
|
|
798
|
+
success: true,
|
|
799
|
+
context,
|
|
800
|
+
snapshot,
|
|
801
|
+
issueId: inputs.issueId,
|
|
802
|
+
output,
|
|
803
|
+
...result,
|
|
804
|
+
};
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
const output = inputs.json
|
|
808
|
+
? JSON.stringify(buildPersonalStatusJson({ context, snapshot }), null, 2)
|
|
809
|
+
: formatZeroArgStatus({ context, snapshot, full: inputs.full });
|
|
810
|
+
return {
|
|
811
|
+
success: true,
|
|
812
|
+
context,
|
|
813
|
+
snapshot,
|
|
814
|
+
issueId: inputs.issueId,
|
|
815
|
+
missingWorkflowState: true,
|
|
816
|
+
output,
|
|
817
|
+
...(fallbackReason ? { fallbackReason } : {}),
|
|
818
|
+
};
|
|
819
|
+
}
|
|
820
|
+
|
|
506
821
|
if (workflowState) {
|
|
507
822
|
const result = buildAuthoritativeStatus(workflowState);
|
|
508
823
|
return { success: true, output: formatStatus(result), ...result };
|
|
@@ -521,6 +836,8 @@ module.exports = {
|
|
|
521
836
|
parseStatusInputs,
|
|
522
837
|
readWorkflowStateFromBeads,
|
|
523
838
|
detectStage,
|
|
839
|
+
detectRepoContext,
|
|
840
|
+
discoverCurrentIssue,
|
|
524
841
|
analyzeBranch,
|
|
525
842
|
analyzeFiles,
|
|
526
843
|
analyzePR,
|
|
@@ -528,4 +845,5 @@ module.exports = {
|
|
|
528
845
|
analyzeBeads,
|
|
529
846
|
calculateConfidence,
|
|
530
847
|
formatStatus,
|
|
848
|
+
extractDesignSlugs,
|
|
531
849
|
};
|
package/lib/commands/sync.js
CHANGED
|
@@ -1,73 +1,61 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const {
|
|
4
|
-
|
|
5
|
-
function isRecoverableBeadsSyncError(error) {
|
|
6
|
-
const message = error?.message ?? String(error ?? '');
|
|
7
|
-
return (
|
|
8
|
-
message.includes('failed to open database') ||
|
|
9
|
-
message.includes('database not found') ||
|
|
10
|
-
message.includes('no beads configuration found') ||
|
|
11
|
-
message.includes('remote \'origin\' not found') ||
|
|
12
|
-
message.includes('remote "origin" not found')
|
|
13
|
-
);
|
|
14
|
-
}
|
|
3
|
+
const { createSyncBackend } = require('../sync-backend');
|
|
15
4
|
|
|
16
5
|
/**
|
|
17
6
|
* Forge Sync Command
|
|
18
|
-
* Syncs
|
|
19
|
-
*
|
|
7
|
+
* Syncs Kernel issue state with the configured sync backend.
|
|
8
|
+
*
|
|
9
|
+
* The local kernel is single-machine authority, so the default `local-noop`
|
|
10
|
+
* backend is a graceful no-op that names the model instead of moving data.
|
|
11
|
+
* Selecting `git-jsonl`/`server` (a future swap) is a backend change with zero
|
|
12
|
+
* call-site churn — see docs/work/2026-06-26-sync-authority/plan.md.
|
|
20
13
|
*
|
|
21
14
|
* @module commands/sync
|
|
22
15
|
*/
|
|
23
16
|
module.exports = {
|
|
24
17
|
name: 'sync',
|
|
25
|
-
description: 'Sync
|
|
18
|
+
description: 'Sync Kernel issue state with the configured sync backend (local-noop until a server/remote is configured)',
|
|
26
19
|
usage: 'forge sync',
|
|
27
20
|
flags: {},
|
|
28
21
|
|
|
29
22
|
/**
|
|
30
|
-
*
|
|
23
|
+
* Resolve a SyncBackend (default `local-noop`) and run its one-shot sync.
|
|
31
24
|
* @param {string[]} _args - Positional arguments (unused)
|
|
32
25
|
* @param {object} _flags - CLI flags (unused)
|
|
33
|
-
* @param {string}
|
|
26
|
+
* @param {string} projectRoot - Project root path (used for backend resolution)
|
|
34
27
|
* @param {object} [opts] - Options for dependency injection
|
|
35
|
-
* @param {
|
|
28
|
+
* @param {object} [opts._backend] - Override for the resolved SyncBackend (testing)
|
|
29
|
+
* @param {object} [opts.deps] - Explicit backend selection (precedence over env/config)
|
|
30
|
+
* @param {object} [opts.env] - Environment override (testing)
|
|
36
31
|
* @returns {Promise<{success: boolean, synced: boolean, message?: string, error?: string}>}
|
|
37
32
|
*/
|
|
38
|
-
handler: async (_args, _flags,
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
// Step 1: Check if bd binary exists
|
|
33
|
+
handler: async (_args, _flags, projectRoot, opts = {}) => {
|
|
34
|
+
let backend;
|
|
42
35
|
try {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
};
|
|
36
|
+
backend = opts._backend || createSyncBackend({
|
|
37
|
+
projectRoot,
|
|
38
|
+
deps: opts.deps,
|
|
39
|
+
env: opts.env,
|
|
40
|
+
});
|
|
41
|
+
} catch (resolveErr) {
|
|
42
|
+
return { success: false, synced: false, error: resolveErr.message };
|
|
50
43
|
}
|
|
51
44
|
|
|
52
|
-
|
|
45
|
+
let result;
|
|
53
46
|
try {
|
|
54
|
-
|
|
55
|
-
exec('bd', ['dolt', 'push'], { stdio: 'pipe' });
|
|
47
|
+
result = await backend.sync({ ...opts, projectRoot });
|
|
56
48
|
} catch (syncErr) {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
success: true,
|
|
60
|
-
synced: false,
|
|
61
|
-
message: 'Beads is installed but not initialized for sync in this worktree — skipping sync',
|
|
62
|
-
};
|
|
63
|
-
}
|
|
64
|
-
return {
|
|
65
|
-
success: false,
|
|
66
|
-
synced: false,
|
|
67
|
-
error: syncErr.message,
|
|
68
|
-
};
|
|
49
|
+
console.error(`forge sync failed: ${syncErr.message}`);
|
|
50
|
+
return { success: false, synced: false, error: syncErr.message };
|
|
69
51
|
}
|
|
70
|
-
|
|
71
|
-
|
|
52
|
+
// Surface the outcome to the user — otherwise `forge sync` runs silently and
|
|
53
|
+
// a user can't tell whether it synced, no-op'd (local-noop), or failed.
|
|
54
|
+
if (result && result.message) {
|
|
55
|
+
console.log(result.message);
|
|
56
|
+
} else if (result && !result.success && result.error) {
|
|
57
|
+
console.error(`forge sync failed: ${result.error}`);
|
|
58
|
+
}
|
|
59
|
+
return result;
|
|
72
60
|
},
|
|
73
61
|
};
|
package/lib/commands/team.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const { existsSync } = require('node:fs');
|
|
2
2
|
const { execFileSync } = require('node:child_process');
|
|
3
3
|
const path = require('node:path');
|
|
4
|
+
const { getPackageRoot } = require('../package-root');
|
|
4
5
|
|
|
5
6
|
// Resolve bash binary from fixed, known locations only (no PATH search).
|
|
6
7
|
// SonarCloud S4036: PATH must not be searched for OS commands.
|
|
@@ -22,7 +23,9 @@ function _resolveBash() {
|
|
|
22
23
|
}
|
|
23
24
|
|
|
24
25
|
function handleTeam(args) {
|
|
25
|
-
|
|
26
|
+
// Route through the asset root so `forge team` finds the script in BOTH channels:
|
|
27
|
+
// the on-disk package (npm/npx) and a compiled binary (extracted embed dir).
|
|
28
|
+
const scriptPath = path.join(getPackageRoot(), 'scripts', 'forge-team', 'index.sh');
|
|
26
29
|
const bashPath = _resolveBash();
|
|
27
30
|
|
|
28
31
|
try {
|
|
@@ -34,4 +37,14 @@ function handleTeam(args) {
|
|
|
34
37
|
}
|
|
35
38
|
}
|
|
36
39
|
|
|
37
|
-
|
|
40
|
+
async function handler(args = []) {
|
|
41
|
+
handleTeam(args);
|
|
42
|
+
return { success: true };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
module.exports = {
|
|
46
|
+
name: 'team',
|
|
47
|
+
description: 'Team coordination workflows and sync utilities',
|
|
48
|
+
handler,
|
|
49
|
+
handleTeam
|
|
50
|
+
};
|
package/lib/commands/test.js
CHANGED
|
@@ -27,6 +27,18 @@ const LOCKFILE_MAP = [
|
|
|
27
27
|
|
|
28
28
|
const DEFAULT_TIMEOUT = 120000;
|
|
29
29
|
const BEADS_CHECK_TIMEOUT = 3000;
|
|
30
|
+
const DIRECT_TEST_CANDIDATES = Object.freeze({
|
|
31
|
+
'bin/forge.js': [
|
|
32
|
+
'test/cli-flags.test.js',
|
|
33
|
+
'test/forge-cli-registry.test.js',
|
|
34
|
+
'test/setup-github-sync.test.js',
|
|
35
|
+
'test/setup-runtime-flags.test.js',
|
|
36
|
+
],
|
|
37
|
+
'lib/lefthook-check.js': ['test/lefthook-check.test.js', 'test/runtime-health.test.js'],
|
|
38
|
+
'lib/runtime-health.js': ['test/runtime-health.test.js'],
|
|
39
|
+
'lib/upgrade-safety.js': ['test/commands/upgrade.test.js'],
|
|
40
|
+
'scripts/test.js': ['test/scripts/test-runner.test.js'],
|
|
41
|
+
});
|
|
30
42
|
|
|
31
43
|
/**
|
|
32
44
|
* Detect the package manager by checking which lockfile exists.
|
|
@@ -115,7 +127,17 @@ function getChangedFiles(execFileSync, options = {}) {
|
|
|
115
127
|
encoding: 'utf8',
|
|
116
128
|
timeout: 5000,
|
|
117
129
|
}).trim();
|
|
118
|
-
} catch (
|
|
130
|
+
} catch (err) { // NOSONAR S2486
|
|
131
|
+
// A git-diff FAILURE is NOT the same as "no files changed": we could not
|
|
132
|
+
// determine the change set at all. Callers that must fail closed (preflight)
|
|
133
|
+
// pass { strict: true } so the error surfaces instead of masquerading as an
|
|
134
|
+
// empty list. The default path keeps the historical empty-list fallback that
|
|
135
|
+
// `forge test --affected` and the pre-push mapping rely on to run the full
|
|
136
|
+
// suite, so their semantics are unchanged.
|
|
137
|
+
if (options.strict) {
|
|
138
|
+
const reason = err && err.message ? err.message : String(err);
|
|
139
|
+
throw new Error(`git diff failed while computing changed files: ${reason}`);
|
|
140
|
+
}
|
|
119
141
|
/* intentional: git diff failed, no affected files to report */
|
|
120
142
|
return [];
|
|
121
143
|
}
|
|
@@ -152,17 +174,36 @@ function getAffectedTestFiles(projectRoot, execFileSync, fs = defaultFs, options
|
|
|
152
174
|
function getTestCandidatesForChangedFile(file) {
|
|
153
175
|
if (!file) return [];
|
|
154
176
|
|
|
177
|
+
const directCandidates = DIRECT_TEST_CANDIDATES[file];
|
|
178
|
+
if (directCandidates) {
|
|
179
|
+
return directCandidates;
|
|
180
|
+
}
|
|
181
|
+
|
|
155
182
|
if (file.startsWith('test/') && file.endsWith('.test.js')) {
|
|
156
183
|
return [file];
|
|
157
184
|
}
|
|
158
185
|
|
|
186
|
+
if (file === 'README.md'
|
|
187
|
+
|| file === 'QUICKSTART.md'
|
|
188
|
+
|| file === 'CHANGELOG.md'
|
|
189
|
+
|| file === 'AGENTS.md'
|
|
190
|
+
|| file === 'DEVELOPMENT.md'
|
|
191
|
+
|| file === 'docs/INDEX.md'
|
|
192
|
+
|| file === 'docs/PROJECT_DESIGN.md'
|
|
193
|
+
|| file.startsWith('docs/forge/')
|
|
194
|
+
|| file.startsWith('docs/guides/')
|
|
195
|
+
|| file.startsWith('docs/reference/')
|
|
196
|
+
|| file.startsWith('docs/work/')) {
|
|
197
|
+
return ['test/docs-consistency.test.js'];
|
|
198
|
+
}
|
|
199
|
+
|
|
159
200
|
if (file.startsWith('lib/') && file.endsWith('.js')) {
|
|
160
201
|
const relative = file.slice('lib/'.length);
|
|
161
202
|
return [`test/${relative.replace(/\.js$/, '.test.js')}`];
|
|
162
203
|
}
|
|
163
204
|
|
|
164
|
-
if (file.startsWith('scripts/') && file.endsWith('.js')) {
|
|
165
|
-
const relative = file.slice('scripts/'.length).replace(/\.js$/, '.test.js');
|
|
205
|
+
if (file.startsWith('scripts/') && (file.endsWith('.js') || file.endsWith('.sh'))) {
|
|
206
|
+
const relative = file.slice('scripts/'.length).replace(/\.(js|sh)$/, '.test.js');
|
|
166
207
|
return [
|
|
167
208
|
`test/scripts/${relative}`,
|
|
168
209
|
`test/${relative}`,
|
|
@@ -177,10 +218,20 @@ function getTestCandidatesForChangedFile(file) {
|
|
|
177
218
|
];
|
|
178
219
|
}
|
|
179
220
|
|
|
180
|
-
if (file.startsWith('.
|
|
221
|
+
if (file.startsWith('.github/agentic-workflows/') || file === '.github/behavioral-test-scores.json') {
|
|
222
|
+
return [
|
|
223
|
+
'test/scripts/behavioral-judge.test.js',
|
|
224
|
+
'test/structural/agentic-workflow-sync.test.js',
|
|
225
|
+
];
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (file.startsWith('.forge/')
|
|
229
|
+
|| file.startsWith('.cursor/')
|
|
230
|
+
|| file.startsWith('.codex/')) {
|
|
181
231
|
return [
|
|
182
|
-
'test/
|
|
183
|
-
'test/
|
|
232
|
+
'test/agent-gaps.test.js',
|
|
233
|
+
'test/scripts/check-agents.test.js',
|
|
234
|
+
'test/structural/skills-sync-drift.test.js',
|
|
184
235
|
];
|
|
185
236
|
}
|
|
186
237
|
|
|
@@ -192,7 +243,7 @@ module.exports = {
|
|
|
192
243
|
getAffectedTestFiles,
|
|
193
244
|
getTestCandidatesForChangedFile,
|
|
194
245
|
name: 'test',
|
|
195
|
-
description: 'Run tests with smart defaults (timeout,
|
|
246
|
+
description: 'Run tests with smart defaults (timeout, affected-only)',
|
|
196
247
|
usage: 'forge test [--affected]',
|
|
197
248
|
flags: {
|
|
198
249
|
'--affected': 'Run only tests for changed files',
|
package/lib/commands/update.js
CHANGED