forge-workflow 0.0.10 → 0.1.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/rules/{greptile-review-process.md → review-process.md} +56 -41
- package/.claude/scripts/{greptile-resolve.sh → review-resolve.sh} +13 -3
- package/.cursor/rules/permissions-guidance.mdc +2 -2
- package/.forge/hooks/check-tdd.js +3 -0
- package/.forge/hooks/forge-native-hook.js +245 -0
- package/.forge/protected-paths.yaml +157 -0
- package/AGENTS.md +150 -61
- package/CHANGELOG.md +681 -0
- package/CLAUDE.md +9 -118
- package/QUICKSTART.md +171 -0
- package/README.md +271 -363
- package/bin/forge-cmd.js +120 -9
- package/bin/forge-preflight.js +26 -5
- package/bin/forge.js +461 -489
- package/docs/INDEX.md +93 -0
- package/docs/PROJECT_DESIGN.md +685 -0
- package/docs/architecture/index.md +66 -0
- package/docs/architecture/notes/README.md +35 -0
- package/docs/architecture/subsystems/README.md +46 -0
- package/docs/forge/TOOLCHAIN.md +670 -0
- package/docs/forge/VALIDATION.md +82 -0
- package/docs/{AGENT_INSTALL_PROMPT.md → guides/AGENT_INSTALL_PROMPT.md} +3 -3
- package/docs/guides/BEADS_GITHUB_SYNC.md +32 -0
- package/docs/{ENHANCED_ONBOARDING.md → guides/ENHANCED_ONBOARDING.md} +16 -12
- package/docs/guides/GREPTILE_SETUP.md +46 -0
- package/docs/guides/MANUAL_REVIEW_GUIDE.md +58 -0
- package/docs/guides/MIGRATION.md +56 -0
- package/docs/guides/SETUP.md +118 -0
- package/docs/guides/SUPPORT.md +185 -0
- package/docs/guides/WORKFLOW_TEMPLATES.md +74 -0
- package/docs/guides/memory-backends.md +183 -0
- package/docs/reference/ADAPTERS.md +128 -0
- package/docs/reference/AGENT_SKILL_PARITY.md +175 -0
- package/docs/reference/COMMANDS.md +205 -0
- package/docs/reference/DECISION_DRIFT_GUARDS.md +97 -0
- package/docs/{EXAMPLES.md → reference/EXAMPLES.md} +7 -5
- package/docs/reference/FORGE_KERNEL_STORAGE_MODEL.md +135 -0
- package/docs/reference/HERMES_INTEGRATION.md +118 -0
- package/docs/reference/INSIGHTS_RECAP.md +63 -0
- package/docs/reference/INSTALL.md +164 -0
- package/docs/reference/KERNEL_TAXONOMY_VALIDATION.md +161 -0
- package/docs/reference/PROTECTED_PATH_MANIFEST.md +25 -0
- package/docs/reference/RELEASE.md +68 -0
- package/docs/reference/RESEARCH_TEMPLATE.md +292 -0
- package/docs/{ROADMAP.md → reference/ROADMAP.md} +12 -9
- package/docs/reference/SKILLS.md +35 -0
- package/docs/reference/STATUS_BOARD.md +80 -0
- package/docs/reference/TEMPLATES.md +106 -0
- package/docs/{TOOLCHAIN.md → reference/TOOLCHAIN.md} +62 -47
- package/docs/reference/VALIDATION.md +82 -0
- package/docs/reference/agent-permissions.md +169 -0
- package/docs/reference/beads-to-kernel-migration-ux.md +61 -0
- package/docs/reference/control-plane-guarantees.md +125 -0
- package/docs/reference/dependency-chain.md +331 -0
- package/docs/reference/forge-kernel-issue-command-contract.md +161 -0
- package/docs/reference/forge-kernel-schema.md +72 -0
- package/docs/reference/kernel-conflict-evaluators.md +27 -0
- package/docs/reference/patch-md-format.md +77 -0
- package/docs/reference/protected-state-surfaces.md +59 -0
- package/docs/reference/shepherd.md +115 -0
- package/docs/reference/superpowers-analysis.md +320 -0
- package/docs/reference/superpowers-integration-options.md +404 -0
- package/docs/reference/test-environment.md +519 -0
- package/docs/reference/upgrade-safety.md +59 -0
- package/lefthook.yml +18 -0
- package/lib/adapter-cli.js +307 -0
- package/lib/adapters/beads-issue-adapter.js +127 -0
- package/lib/adapters/beads-kernel-compat.js +1042 -0
- package/lib/adapters/greptile-review-adapter.js +141 -0
- package/lib/adapters/kernel-issue-adapter.js +101 -0
- package/lib/adapters/pr-state-adapter.js +484 -0
- package/lib/adoption-profiles.js +126 -0
- package/lib/agents/README.md +2 -6
- package/lib/agents/claude.plugin.json +3 -8
- package/lib/agents/codex.plugin.json +9 -1
- package/lib/agents/cursor.plugin.json +2 -6
- package/lib/agents/hermes.plugin.json +22 -0
- package/lib/agents-config.js +39 -1236
- package/lib/audit-evidence.js +282 -0
- package/lib/beads-setup.js +121 -0
- package/lib/beads-sync-scaffold.js +25 -101
- package/lib/codex-skills.js +51 -1
- package/lib/commands/_issue.js +741 -77
- package/lib/commands/_manifest.js +91 -0
- package/lib/commands/_registry.js +85 -34
- package/lib/commands/_resolve-command-opts.js +261 -0
- package/lib/commands/_serve-security.js +270 -0
- package/lib/commands/adapter.js +12 -0
- package/lib/commands/add.js +118 -0
- package/lib/commands/audit.js +70 -0
- package/lib/commands/blocked.js +5 -0
- package/lib/commands/board.js +64 -0
- package/lib/commands/claim.js +21 -2
- package/lib/commands/claims.js +7 -0
- package/lib/commands/clean.js +485 -75
- package/lib/commands/close.js +2 -2
- package/lib/commands/comment.js +5 -0
- package/lib/commands/control.js +148 -0
- package/lib/commands/create.js +2 -2
- package/lib/commands/dev.js +185 -7
- package/lib/commands/doc-gate.js +336 -0
- package/lib/commands/doctor.js +156 -0
- package/lib/commands/explain.js +15 -0
- package/lib/commands/export.js +237 -0
- package/lib/commands/gate.js +192 -0
- package/lib/commands/hooks.js +242 -0
- package/lib/commands/inbox.js +118 -0
- package/lib/commands/init.js +598 -0
- package/lib/commands/insights.js +79 -0
- package/lib/commands/issue.js +12 -1
- package/lib/commands/issues.js +17 -0
- package/lib/commands/lint.js +5 -0
- package/lib/commands/list.js +2 -2
- package/lib/commands/merge.js +312 -0
- package/lib/commands/migrate.js +523 -0
- package/lib/commands/new.js +12 -0
- package/lib/commands/options.js +241 -0
- package/lib/commands/orient.js +13 -0
- package/lib/commands/orphans.js +5 -0
- package/lib/commands/patch.js +67 -0
- package/lib/commands/plan.js +436 -24
- package/lib/commands/preflight.js +211 -0
- package/lib/commands/prime.js +13 -0
- package/lib/commands/push.js +69 -2
- package/lib/commands/ready.js +2 -2
- package/lib/commands/recall.js +116 -0
- package/lib/commands/recap.js +61 -0
- package/lib/commands/recommend.js +0 -1
- package/lib/commands/release.js +91 -0
- package/lib/commands/remember.js +74 -0
- package/lib/commands/role.js +99 -0
- package/lib/commands/serve.js +581 -0
- package/lib/commands/setup.js +838 -972
- package/lib/commands/shepherd.js +436 -0
- package/lib/commands/ship.js +23 -1
- package/lib/commands/show.js +2 -2
- package/lib/commands/stage.js +192 -0
- package/lib/commands/stale.js +5 -0
- package/lib/commands/status.js +158 -21
- package/lib/commands/sync.js +34 -46
- package/lib/commands/team.js +4 -1
- package/lib/commands/test.js +43 -27
- package/lib/commands/update.js +2 -2
- package/lib/commands/upgrade.js +47 -0
- package/lib/commands/validate.js +43 -18
- package/lib/commands/worktree.js +307 -100
- package/lib/config-writer.js +202 -0
- package/lib/control-plane.js +236 -0
- package/lib/core/runtime-graph.js +946 -0
- package/lib/dep-guard/keyword-ripple.js +2 -2
- package/lib/deprecated-sync-cleanup.js +362 -0
- package/lib/detect-agent.js +2 -28
- package/lib/detect-worktree.js +35 -9
- package/lib/doc-gate/declaration.js +177 -0
- package/lib/doc-gate/detect.js +289 -0
- package/lib/doc-gate/gate.js +375 -0
- package/lib/doc-gate/okf-config.js +128 -0
- package/lib/doc-gate/okf.js +429 -0
- package/lib/docs-command.js +1161 -6
- package/lib/forge-issues.js +382 -11
- package/lib/forge-lock.js +262 -0
- package/lib/gate-events.js +193 -0
- package/lib/global-flags.js +74 -0
- package/lib/greptile-match.js +7 -63
- package/lib/harness-capability-matrix.js +380 -0
- package/lib/hook-global-installer.js +347 -0
- package/lib/hook-renderer.js +451 -0
- package/lib/inbox.js +391 -0
- package/lib/insights.js +397 -0
- package/lib/issue-adapter.js +156 -0
- package/lib/issue-backend.js +145 -0
- package/lib/issue-render.js +220 -0
- package/lib/kernel/backing-issue.js +305 -0
- package/lib/kernel/broker.js +1218 -0
- package/lib/kernel/cli-broker-factory.js +130 -0
- package/lib/kernel/conflict-signal.js +82 -0
- package/lib/kernel/evaluators.js +195 -0
- package/lib/kernel/fs-class.js +495 -0
- package/lib/kernel/issue-command-contract.js +559 -0
- package/lib/kernel/issue-id-resolver.js +186 -0
- package/lib/kernel/lease-enforcer.js +158 -0
- package/lib/kernel/migrations.js +333 -0
- package/lib/kernel/planning-buckets-schema.js +109 -0
- package/lib/kernel/projection-jsonl-writer.js +450 -0
- package/lib/kernel/readiness-model.js +329 -0
- package/lib/kernel/schema.js +356 -0
- package/lib/kernel/sqlite-driver.js +2504 -0
- package/lib/kernel/taxonomy-validator.js +394 -0
- package/lib/lefthook-check.js +3 -2
- package/lib/lefthook-wiring.js +413 -0
- package/lib/mcp-config-renderer.js +288 -0
- package/lib/memory/graphiti-mcp.js +106 -0
- package/lib/memory/router.js +387 -0
- package/lib/memory/typed-api.js +102 -0
- package/lib/memory-digest.js +195 -0
- package/lib/merge-rules.js +395 -0
- package/lib/migrate-dry-run.js +466 -0
- package/lib/orientation.js +863 -0
- package/lib/package-manager-remediation.js +103 -0
- package/lib/package-root.js +381 -0
- package/lib/patch-intent.js +890 -0
- package/lib/plugin-catalog.js +3 -4
- package/lib/plugin-manager.js +0 -5
- package/lib/pr-bundle.js +186 -0
- package/lib/pr-monitor/differ.js +195 -0
- package/lib/pr-monitor/events.js +0 -0
- package/lib/pr-monitor/gather.js +124 -0
- package/lib/pr-monitor/journal.js +299 -0
- package/lib/pr-monitor/monitor.js +146 -0
- package/lib/pr-monitor/render-sticky.js +157 -0
- package/lib/pr-monitor/watch-lifecycle.js +95 -0
- package/lib/pr-monitor/watch.js +247 -0
- package/lib/pr-pull.js +1273 -0
- package/lib/pr-shepherd.js +494 -0
- package/lib/pr-state-validator.js +59 -0
- package/lib/preflight/gates.js +237 -0
- package/lib/preflight/runner.js +116 -0
- package/lib/project-discovery.js +0 -53
- package/lib/project-memory.js +99 -497
- package/lib/protected-path-manifest.js +281 -0
- package/lib/protected-state-surfaces.js +387 -0
- package/lib/release-readiness.js +2089 -0
- package/lib/reset.js +59 -45
- package/lib/review-adapter.js +68 -0
- package/lib/rules-sync.js +260 -0
- package/lib/runtime-health.js +241 -20
- package/lib/safety-config-renderer.js +268 -0
- package/lib/setup-action-log.js +1 -7
- package/lib/setup.js +27 -65
- package/lib/shell-utils.js +76 -6
- package/lib/skills-sync.js +330 -0
- package/lib/smart-status/scoring.js +17 -3
- package/lib/status/beads-snapshot.js +45 -2
- package/lib/status/presenter.js +169 -18
- package/lib/status/snapshot.js +186 -0
- package/lib/sync-backend.js +202 -0
- package/lib/untrusted-content.js +52 -0
- package/lib/upgrade-safety.js +199 -0
- package/lib/workflow/enforce-stage.js +296 -47
- package/lib/workflow/stage-transition.js +115 -0
- package/lib/workflow/stages.js +30 -6
- package/lib/workflow/state-manager.js +11 -22
- package/lib/workflow/state.js +23 -1
- package/lib/workflow-profiles.js +17 -5
- package/package.json +37 -35
- package/rules/documentation.md +19 -0
- package/rules/kernel-tracking.md +26 -0
- package/rules/security.md +22 -0
- package/rules/tdd.md +20 -0
- package/rules/workflow.md +27 -0
- package/scripts/auto-backing-issue.js +47 -0
- package/scripts/beads-context.sh +81 -57
- package/scripts/beads-upgrade-smoke.sh +24 -3
- package/scripts/bootstrap-windows-tools.sh +78 -0
- package/scripts/branch-protection.js +2 -3
- package/scripts/check-agents.js +34 -137
- package/scripts/commitlint.js +3 -1
- package/scripts/conflict-detect.sh +3 -0
- package/scripts/dep-guard.sh +22 -3
- package/scripts/file-index.sh +3 -0
- package/scripts/forge-team/lib/claim.sh +34 -18
- package/scripts/forge-team/lib/dashboard.sh +61 -86
- package/scripts/forge-team/lib/epic.sh +99 -263
- package/scripts/forge-team/lib/hooks.sh +26 -28
- package/scripts/forge-team/lib/identity.sh +4 -4
- package/scripts/forge-team/lib/sync-github.sh +49 -84
- package/scripts/forge-team/lib/verify.sh +93 -83
- package/scripts/forge-team/lib/workload.sh +41 -65
- package/scripts/forge-team/tests/claim.test.sh +25 -19
- package/scripts/forge-team/tests/dashboard.test.sh +31 -46
- package/scripts/forge-team/tests/epic.test.sh +52 -71
- package/scripts/forge-team/tests/hooks.test.sh +38 -50
- package/scripts/forge-team/tests/identity.test.sh +3 -3
- package/scripts/forge-team/tests/integration.test.sh +44 -66
- package/scripts/forge-team/tests/sync-github.test.sh +50 -83
- package/scripts/forge-team/tests/verify.test.sh +37 -46
- package/scripts/forge-team/tests/workflow-integration.test.sh +4 -4
- package/scripts/forge-team/tests/workload.test.sh +32 -66
- package/scripts/gen-command-manifest.js +153 -0
- package/scripts/gen-embedded-assets.mjs +129 -0
- package/scripts/install.ps1 +139 -0
- package/scripts/install.sh +268 -0
- package/scripts/lib/release-asset.mjs +84 -0
- package/scripts/parity-check.mjs +145 -0
- package/scripts/parity-check.test.mjs +58 -0
- package/scripts/pin-agentic-workflow-images.js +112 -0
- package/scripts/pr-coordinator.sh +3 -0
- package/scripts/preflight-sonar.eslint.config.mjs +44 -0
- package/scripts/preflight.sh +21 -94
- package/scripts/protected-state-check.js +104 -0
- package/scripts/smart-status.sh +60 -57
- package/scripts/spikes/config-race-bench.js +111 -0
- package/scripts/spikes/harness-capability-matrix.js +13 -0
- package/scripts/spikes/patch-anchor-stability-bench.js +125 -0
- package/scripts/spikes/protected-path-manifest.js +20 -0
- package/scripts/spikes/skill-auto-invoke-parity.js +292 -0
- package/scripts/sync-agent-skills.js +62 -0
- package/scripts/sync-utils.sh +3 -0
- package/scripts/test-ci-shard.js +13 -6
- package/scripts/test.js +95 -12
- package/skills/claim-safety/SKILL.md +102 -0
- package/skills/claim-safety/evals/evals.json +46 -0
- package/{.github/prompts/dev.prompt.md → skills/dev/SKILL.md} +44 -50
- package/skills/dev/evals/evals.json +50 -0
- package/skills/hermes-forge/SKILL.md +185 -0
- package/skills/hermes-forge/evals/evals.json +46 -0
- package/skills/issue-basics/SKILL.md +111 -0
- package/skills/issue-basics/evals/evals.json +46 -0
- package/skills/kernel/SKILL.md +166 -0
- package/skills/kernel/evals/evals.json +50 -0
- package/skills/memory/SKILL.md +102 -0
- package/skills/parallel-deep-research/SKILL.md +14 -11
- package/skills/parallel-deep-research/evals/evals.json +11 -27
- package/{.github/prompts/plan.prompt.md → skills/plan/SKILL.md} +132 -157
- package/skills/plan/evals/evals.json +42 -0
- package/skills/research/SKILL.md +195 -0
- package/skills/research/evals/evals.json +42 -0
- package/{.github/prompts/review.prompt.md → skills/review/SKILL.md} +98 -62
- package/skills/review/evals/evals.json +42 -0
- package/skills/rollback/SKILL.md +110 -0
- package/skills/rollback/evals/evals.json +46 -0
- package/skills/rollback/references/methods.md +204 -0
- package/{.cursor/commands/rollback.md → skills/rollback/references/workflow-integration.md} +10 -284
- package/skills/shepherd/SKILL.md +66 -0
- package/skills/shepherd/evals/evals.json +42 -0
- package/{.github/prompts/ship.prompt.md → skills/ship/SKILL.md} +81 -45
- package/skills/ship/evals/evals.json +42 -0
- package/skills/smith/SKILL.md +142 -0
- package/skills/smith/evals/evals.json +46 -0
- package/skills/smith/references/autonomy-and-gates.md +94 -0
- package/{.github/prompts/sonarcloud.prompt.md → skills/sonarcloud/SKILL.md} +14 -3
- package/skills/sonarcloud/evals/evals.json +46 -0
- package/skills/sonarcloud-analysis/SKILL.md +18 -13
- package/skills/sonarcloud-analysis/evals/evals.json +11 -15
- package/{.github/prompts/status.prompt.md → skills/status/SKILL.md} +20 -10
- package/skills/status/evals/evals.json +50 -0
- package/skills/triage-ready/SKILL.md +121 -0
- package/skills/triage-ready/evals/evals.json +42 -0
- package/{.github/prompts/validate.prompt.md → skills/validate/SKILL.md} +52 -29
- package/skills/validate/evals/evals.json +42 -0
- package/skills/verify/SKILL.md +299 -0
- package/skills/verify/evals/evals.json +50 -0
- package/.claude/commands/dev.md +0 -345
- package/.claude/commands/plan.md +0 -566
- package/.claude/commands/premerge.md +0 -186
- package/.claude/commands/research.md +0 -42
- package/.claude/commands/review.md +0 -451
- package/.claude/commands/rollback.md +0 -721
- package/.claude/commands/ship.md +0 -213
- package/.claude/commands/sonarcloud.md +0 -152
- package/.claude/commands/status.md +0 -90
- package/.claude/commands/validate.md +0 -288
- package/.claude/commands/verify.md +0 -269
- package/.claude/rules/workflow.md +0 -121
- package/.cline/workflows/dev.md +0 -342
- package/.cline/workflows/plan.md +0 -563
- package/.cline/workflows/premerge.md +0 -183
- package/.cline/workflows/research.md +0 -39
- package/.cline/workflows/review.md +0 -448
- package/.cline/workflows/rollback.md +0 -718
- package/.cline/workflows/ship.md +0 -210
- package/.cline/workflows/sonarcloud.md +0 -146
- package/.cline/workflows/status.md +0 -87
- package/.cline/workflows/validate.md +0 -285
- package/.cline/workflows/verify.md +0 -266
- package/.codex/config.toml +0 -11
- package/.codex/skills/dev/SKILL.md +0 -345
- package/.codex/skills/plan/SKILL.md +0 -566
- package/.codex/skills/premerge/SKILL.md +0 -186
- package/.codex/skills/research/SKILL.md +0 -42
- package/.codex/skills/review/SKILL.md +0 -451
- package/.codex/skills/rollback/SKILL.md +0 -721
- package/.codex/skills/ship/SKILL.md +0 -213
- package/.codex/skills/sonarcloud/SKILL.md +0 -149
- package/.codex/skills/status/SKILL.md +0 -90
- package/.codex/skills/validate/SKILL.md +0 -288
- package/.codex/skills/verify/SKILL.md +0 -269
- package/.cursor/commands/dev.md +0 -342
- package/.cursor/commands/plan.md +0 -563
- package/.cursor/commands/premerge.md +0 -183
- package/.cursor/commands/research.md +0 -39
- package/.cursor/commands/review.md +0 -448
- package/.cursor/commands/ship.md +0 -210
- package/.cursor/commands/sonarcloud.md +0 -146
- package/.cursor/commands/status.md +0 -87
- package/.cursor/commands/validate.md +0 -285
- package/.cursor/commands/verify.md +0 -266
- package/.cursorrules +0 -149
- package/.github/prompts/premerge.prompt.md +0 -188
- package/.github/prompts/research.prompt.md +0 -44
- package/.github/prompts/rollback.prompt.md +0 -723
- package/.github/prompts/verify.prompt.md +0 -271
- package/.github/workflows/beads-to-github.yml +0 -89
- package/.github/workflows/github-to-beads.yml +0 -100
- package/.kilocode/workflows/dev.md +0 -346
- package/.kilocode/workflows/plan.md +0 -567
- package/.kilocode/workflows/premerge.md +0 -187
- package/.kilocode/workflows/research.md +0 -43
- package/.kilocode/workflows/review.md +0 -452
- package/.kilocode/workflows/rollback.md +0 -722
- package/.kilocode/workflows/ship.md +0 -214
- package/.kilocode/workflows/sonarcloud.md +0 -150
- package/.kilocode/workflows/status.md +0 -91
- package/.kilocode/workflows/validate.md +0 -289
- package/.kilocode/workflows/verify.md +0 -270
- package/.opencode/commands/dev.md +0 -345
- package/.opencode/commands/plan.md +0 -566
- package/.opencode/commands/premerge.md +0 -186
- package/.opencode/commands/research.md +0 -42
- package/.opencode/commands/review.md +0 -451
- package/.opencode/commands/rollback.md +0 -721
- package/.opencode/commands/ship.md +0 -213
- package/.opencode/commands/sonarcloud.md +0 -149
- package/.opencode/commands/status.md +0 -90
- package/.opencode/commands/validate.md +0 -288
- package/.opencode/commands/verify.md +0 -269
- package/.roo/commands/dev.md +0 -346
- package/.roo/commands/plan.md +0 -567
- package/.roo/commands/premerge.md +0 -187
- package/.roo/commands/research.md +0 -43
- package/.roo/commands/review.md +0 -452
- package/.roo/commands/rollback.md +0 -722
- package/.roo/commands/ship.md +0 -214
- package/.roo/commands/sonarcloud.md +0 -150
- package/.roo/commands/status.md +0 -91
- package/.roo/commands/validate.md +0 -289
- package/.roo/commands/verify.md +0 -270
- package/docs/BEADS_GITHUB_SYNC.md +0 -281
- package/docs/GREPTILE_SETUP.md +0 -400
- package/docs/MANUAL_REVIEW_GUIDE.md +0 -106
- package/docs/SETUP.md +0 -663
- package/docs/VALIDATION.md +0 -363
- package/lib/agents/cline.plugin.json +0 -29
- package/lib/agents/copilot.plugin.json +0 -24
- package/lib/agents/kilocode.plugin.json +0 -22
- package/lib/agents/opencode.plugin.json +0 -23
- package/lib/agents/roo.plugin.json +0 -30
- package/lib/beads-bootstrap.js +0 -225
- package/lib/beads-health-check.js +0 -188
- package/lib/commands/commands-reset.js +0 -147
- package/opencode.json +0 -67
- package/scripts/beads-context.test.js +0 -584
- package/scripts/github-beads-sync/comment.mjs +0 -64
- package/scripts/github-beads-sync/config.mjs +0 -148
- package/scripts/github-beads-sync/github-api.mjs +0 -131
- package/scripts/github-beads-sync/index.mjs +0 -356
- package/scripts/github-beads-sync/label-mapper.mjs +0 -54
- package/scripts/github-beads-sync/mapping.mjs +0 -132
- package/scripts/github-beads-sync/reverse-sync-cli.mjs +0 -31
- package/scripts/github-beads-sync/reverse-sync.mjs +0 -162
- package/scripts/github-beads-sync/run-bd.mjs +0 -161
- package/scripts/github-beads-sync/sanitize.mjs +0 -121
- package/scripts/github-beads-sync.config.json +0 -26
- package/scripts/sync-commands.js +0 -600
package/lib/commands/status.js
CHANGED
|
@@ -9,8 +9,12 @@ const {
|
|
|
9
9
|
} = require('../workflow/state');
|
|
10
10
|
const { STAGE_IDS } = require('../workflow/stages');
|
|
11
11
|
const { detectWorktree } = require('../detect-worktree');
|
|
12
|
-
const {
|
|
13
|
-
const {
|
|
12
|
+
const { readStatusSnapshot } = require('../status/snapshot');
|
|
13
|
+
const {
|
|
14
|
+
buildPersonalStatusJson,
|
|
15
|
+
formatRunNextLines,
|
|
16
|
+
formatZeroArgStatus,
|
|
17
|
+
} = require('../status/presenter');
|
|
14
18
|
const {
|
|
15
19
|
loadState,
|
|
16
20
|
extractWorkflowStateFromComments,
|
|
@@ -18,6 +22,12 @@ const {
|
|
|
18
22
|
} = require('../workflow/state-manager');
|
|
19
23
|
const { secureExecFileSync } = require('../shell-utils.js');
|
|
20
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.)
|
|
21
31
|
const WORKFLOW_STAGES = {
|
|
22
32
|
1: { name: 'Fresh Start', nextCommand: 'research' },
|
|
23
33
|
2: { name: 'Research', nextCommand: 'research' },
|
|
@@ -30,6 +40,10 @@ const WORKFLOW_STAGES = {
|
|
|
30
40
|
9: { name: 'Verification', nextCommand: 'verify' },
|
|
31
41
|
};
|
|
32
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).
|
|
33
47
|
const AUTHORITATIVE_STAGE_NAMES = {
|
|
34
48
|
plan: 'Planning',
|
|
35
49
|
dev: 'Development',
|
|
@@ -329,10 +343,15 @@ function detectStage(context) {
|
|
|
329
343
|
|
|
330
344
|
function parseStatusInputs(args = [], flags = {}) {
|
|
331
345
|
const statusArgs = Array.isArray(args) ? args : [];
|
|
346
|
+
const firstDefined = (...values) => values.find(value => value !== undefined && value !== null);
|
|
332
347
|
const getNextValue = (flagName) => {
|
|
333
348
|
const index = statusArgs.indexOf(flagName);
|
|
334
349
|
if (index !== -1 && index + 1 < statusArgs.length) {
|
|
335
|
-
|
|
350
|
+
const candidate = statusArgs[index + 1];
|
|
351
|
+
if (typeof candidate === 'string' && candidate.startsWith('--')) {
|
|
352
|
+
return null;
|
|
353
|
+
}
|
|
354
|
+
return candidate;
|
|
336
355
|
}
|
|
337
356
|
return null;
|
|
338
357
|
};
|
|
@@ -343,10 +362,23 @@ function parseStatusInputs(args = [], flags = {}) {
|
|
|
343
362
|
};
|
|
344
363
|
|
|
345
364
|
return {
|
|
346
|
-
issueId: flags.issueId
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
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
|
+
),
|
|
350
382
|
};
|
|
351
383
|
}
|
|
352
384
|
|
|
@@ -408,17 +440,104 @@ function readIssueDetails(issueId, projectRoot = process.cwd()) {
|
|
|
408
440
|
}
|
|
409
441
|
}
|
|
410
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
|
+
|
|
411
530
|
function extractDesignSlugs(design) {
|
|
412
531
|
if (typeof design !== 'string' || design.trim() === '') {
|
|
413
532
|
return [];
|
|
414
533
|
}
|
|
415
534
|
|
|
416
535
|
const slugs = new Set();
|
|
417
|
-
const
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
536
|
+
for (const candidate of extractPathCandidates(design)) {
|
|
537
|
+
const slug = extractSlugFromDesignPath(candidate);
|
|
538
|
+
if (slug) {
|
|
539
|
+
slugs.add(slug);
|
|
540
|
+
}
|
|
422
541
|
}
|
|
423
542
|
|
|
424
543
|
return [...slugs];
|
|
@@ -450,7 +569,8 @@ function discoverCurrentIssue(snapshot, context, projectRoot = process.cwd()) {
|
|
|
450
569
|
return hydratedIssues.get(issue.id);
|
|
451
570
|
}
|
|
452
571
|
|
|
453
|
-
const
|
|
572
|
+
const hasSufficientDetails = Boolean(issue.comments) && Boolean(issue.design);
|
|
573
|
+
const details = hasSufficientDetails ? issue : readIssueDetails(issue.id, projectRoot);
|
|
454
574
|
const hydratedIssue = details ? { ...issue, ...details } : issue;
|
|
455
575
|
hydratedIssues.set(issue.id, hydratedIssue);
|
|
456
576
|
return hydratedIssue;
|
|
@@ -578,11 +698,8 @@ function collectCompletedChecks(result) {
|
|
|
578
698
|
function formatAuthoritativeStatus(result) {
|
|
579
699
|
const completedStages = result.workflowState.completedStages.map(stageId => stageId);
|
|
580
700
|
const allowedCommands = result.nextStages.map(stageId => '/' + stageId).join(', ');
|
|
581
|
-
const nextLines =
|
|
701
|
+
const nextLines = formatRunNextLines(result);
|
|
582
702
|
|
|
583
|
-
if (result.nextCommand) {
|
|
584
|
-
nextLines.push(`Next after this: /${result.nextCommand}`);
|
|
585
|
-
}
|
|
586
703
|
if (allowedCommands) {
|
|
587
704
|
nextLines.push(`Allowed after this: ${allowedCommands}`);
|
|
588
705
|
}
|
|
@@ -633,7 +750,12 @@ function formatStatus(result) {
|
|
|
633
750
|
|
|
634
751
|
module.exports = {
|
|
635
752
|
name: 'status',
|
|
636
|
-
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
|
+
},
|
|
637
759
|
handler: async (args, flags, projectRoot) => {
|
|
638
760
|
const inputs = parseStatusInputs(args, flags);
|
|
639
761
|
const isZeroArgStatus = !inputs.issueId && !inputs.workflowState && !inputs.bdComments;
|
|
@@ -647,7 +769,16 @@ module.exports = {
|
|
|
647
769
|
|
|
648
770
|
if (isZeroArgStatus) {
|
|
649
771
|
context = detectRepoContext(effectiveProjectRoot);
|
|
650
|
-
|
|
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
|
+
});
|
|
651
782
|
const discoveredIssue = discoverCurrentIssue(snapshot, context, effectiveProjectRoot);
|
|
652
783
|
if (discoveredIssue) {
|
|
653
784
|
inputs.issueId = discoveredIssue.id;
|
|
@@ -660,23 +791,29 @@ module.exports = {
|
|
|
660
791
|
if (isZeroArgStatus) {
|
|
661
792
|
if (workflowState) {
|
|
662
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 });
|
|
663
797
|
return {
|
|
664
798
|
success: true,
|
|
665
799
|
context,
|
|
666
800
|
snapshot,
|
|
667
801
|
issueId: inputs.issueId,
|
|
668
|
-
output
|
|
802
|
+
output,
|
|
669
803
|
...result,
|
|
670
804
|
};
|
|
671
805
|
}
|
|
672
806
|
|
|
807
|
+
const output = inputs.json
|
|
808
|
+
? JSON.stringify(buildPersonalStatusJson({ context, snapshot }), null, 2)
|
|
809
|
+
: formatZeroArgStatus({ context, snapshot, full: inputs.full });
|
|
673
810
|
return {
|
|
674
811
|
success: true,
|
|
675
812
|
context,
|
|
676
813
|
snapshot,
|
|
677
814
|
issueId: inputs.issueId,
|
|
678
815
|
missingWorkflowState: true,
|
|
679
|
-
output
|
|
816
|
+
output,
|
|
680
817
|
...(fallbackReason ? { fallbackReason } : {}),
|
|
681
818
|
};
|
|
682
819
|
}
|
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 {
|
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,22 +174,29 @@ function getAffectedTestFiles(projectRoot, execFileSync, fs = defaultFs, options
|
|
|
152
174
|
function getTestCandidatesForChangedFile(file) {
|
|
153
175
|
if (!file) return [];
|
|
154
176
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
if (file === 'lib/runtime-health.js') {
|
|
160
|
-
return ['test/runtime-health.test.js'];
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
if (file === 'scripts/test.js') {
|
|
164
|
-
return ['test/scripts/test-runner.test.js'];
|
|
177
|
+
const directCandidates = DIRECT_TEST_CANDIDATES[file];
|
|
178
|
+
if (directCandidates) {
|
|
179
|
+
return directCandidates;
|
|
165
180
|
}
|
|
166
181
|
|
|
167
182
|
if (file.startsWith('test/') && file.endsWith('.test.js')) {
|
|
168
183
|
return [file];
|
|
169
184
|
}
|
|
170
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
|
+
|
|
171
200
|
if (file.startsWith('lib/') && file.endsWith('.js')) {
|
|
172
201
|
const relative = file.slice('lib/'.length);
|
|
173
202
|
return [`test/${relative.replace(/\.js$/, '.test.js')}`];
|
|
@@ -196,26 +225,13 @@ function getTestCandidatesForChangedFile(file) {
|
|
|
196
225
|
];
|
|
197
226
|
}
|
|
198
227
|
|
|
199
|
-
if (file.startsWith('.claude/commands/') && file.endsWith('.md')) {
|
|
200
|
-
return [
|
|
201
|
-
'test/command-sync-check.test.js',
|
|
202
|
-
'test/structural/command-sync.test.js',
|
|
203
|
-
];
|
|
204
|
-
}
|
|
205
|
-
|
|
206
228
|
if (file.startsWith('.forge/')
|
|
207
229
|
|| file.startsWith('.cursor/')
|
|
208
|
-
|| file.startsWith('.
|
|
209
|
-
|| file.startsWith('.roo/')
|
|
210
|
-
|| file.startsWith('.kilocode/')
|
|
211
|
-
|| file.startsWith('.opencode/')
|
|
212
|
-
|| file.startsWith('.codex/skills/')
|
|
213
|
-
|| file.startsWith('.github/prompts/')) {
|
|
230
|
+
|| file.startsWith('.codex/')) {
|
|
214
231
|
return [
|
|
215
232
|
'test/agent-gaps.test.js',
|
|
216
|
-
'test/command-sync-check.test.js',
|
|
217
233
|
'test/scripts/check-agents.test.js',
|
|
218
|
-
'test/structural/
|
|
234
|
+
'test/structural/skills-sync-drift.test.js',
|
|
219
235
|
];
|
|
220
236
|
}
|
|
221
237
|
|
|
@@ -227,7 +243,7 @@ module.exports = {
|
|
|
227
243
|
getAffectedTestFiles,
|
|
228
244
|
getTestCandidatesForChangedFile,
|
|
229
245
|
name: 'test',
|
|
230
|
-
description: 'Run tests with smart defaults (timeout,
|
|
246
|
+
description: 'Run tests with smart defaults (timeout, affected-only)',
|
|
231
247
|
usage: 'forge test [--affected]',
|
|
232
248
|
flags: {
|
|
233
249
|
'--affected': 'Run only tests for changed files',
|
package/lib/commands/update.js
CHANGED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
applySelfHeal,
|
|
5
|
+
buildUpgradeDryRunReport,
|
|
6
|
+
renderUpgradeDryRunReport,
|
|
7
|
+
} = require('../upgrade-safety');
|
|
8
|
+
|
|
9
|
+
function hasArg(args, name) {
|
|
10
|
+
return Array.isArray(args) && args.includes(name);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const usage = 'Usage: forge upgrade [--dry-run] [--self-heal]';
|
|
14
|
+
|
|
15
|
+
async function handler(args, flags, projectRoot) {
|
|
16
|
+
const dryRun = flags?.dryRun === true || flags?.['dry-run'] === true || hasArg(args, '--dry-run');
|
|
17
|
+
const selfHeal = flags?.selfHeal === true || flags?.['self-heal'] === true || hasArg(args, '--self-heal');
|
|
18
|
+
|
|
19
|
+
if (!dryRun && !selfHeal) {
|
|
20
|
+
return {
|
|
21
|
+
success: false,
|
|
22
|
+
error: usage,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const report = buildUpgradeDryRunReport(projectRoot);
|
|
27
|
+
const selfHealResult = selfHeal ? applySelfHeal(report.projectRoot, report) : null;
|
|
28
|
+
const output = renderUpgradeDryRunReport(report, selfHealResult);
|
|
29
|
+
const success = report.ok && !selfHealResult?.refused;
|
|
30
|
+
|
|
31
|
+
return {
|
|
32
|
+
success,
|
|
33
|
+
output,
|
|
34
|
+
error: success ? undefined : output,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
module.exports = {
|
|
39
|
+
name: 'upgrade',
|
|
40
|
+
description: 'Preview and self-heal safe Forge upgrade readiness',
|
|
41
|
+
usage,
|
|
42
|
+
flags: {
|
|
43
|
+
'--dry-run': 'Preview upgrade readiness without writing files',
|
|
44
|
+
'--self-heal': 'Apply safe metadata repairs only; does not snapshot or restore',
|
|
45
|
+
},
|
|
46
|
+
handler,
|
|
47
|
+
};
|