@opengsd/gsd-core 1.15.0 → 1.17.0
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-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -2
- package/.opencode/plugins/gsd-core.js +336 -6
- package/.opencode/plugins/gsd-statusline/tui.tsx +55 -0
- package/GEMINI.md +1 -1
- package/agents/gsd-code-fixer.compact.md +1 -1
- package/agents/gsd-code-fixer.md +1 -1
- package/agents/gsd-code-reviewer.compact.md +5 -3
- package/agents/gsd-code-reviewer.md +8 -6
- package/agents/gsd-debug-session-manager.compact.md +7 -1
- package/agents/gsd-debug-session-manager.md +7 -1
- package/agents/gsd-debugger.md +2 -2
- package/agents/gsd-eval-auditor.compact.md +1 -1
- package/agents/gsd-eval-auditor.md +1 -1
- package/agents/gsd-executor.md +4 -4
- package/agents/gsd-intel-updater.compact.md +1 -1
- package/agents/gsd-intel-updater.md +1 -1
- package/agents/gsd-mempalace-curator.md +2 -2
- package/agents/gsd-phase-researcher.md +1 -1
- package/agents/gsd-plan-checker.md +7 -2
- package/agents/gsd-planner.md +3 -3
- package/agents/gsd-project-researcher.compact.md +1 -1
- package/agents/gsd-project-researcher.md +1 -1
- package/agents/gsd-research-synthesizer.compact.md +1 -1
- package/agents/gsd-research-synthesizer.md +1 -1
- package/agents/gsd-ui-auditor.compact.md +21 -30
- package/agents/gsd-ui-auditor.md +42 -51
- package/agents/gsd-ui-researcher.compact.md +1 -1
- package/agents/gsd-ui-researcher.md +1 -1
- package/agents/gsd-verifier.md +31 -9
- package/bin/gsd-mcp-server.js +3 -1
- package/bin/install.js +487 -290
- package/commands/gsd/add-tests.md +6 -1
- package/commands/gsd/ai-integration-phase.md +6 -1
- package/commands/gsd/audit-fix.md +5 -0
- package/commands/gsd/audit-milestone.md +6 -1
- package/commands/gsd/autonomous.md +5 -0
- package/commands/gsd/capture.md +8 -4
- package/commands/gsd/code-review.md +7 -2
- package/commands/gsd/complete-milestone.md +4 -0
- package/commands/gsd/config.md +7 -3
- package/commands/gsd/debug.md +11 -7
- package/commands/gsd/discuss-phase.md +7 -3
- package/commands/gsd/docs-update.md +12 -7
- package/commands/gsd/eval-review.md +6 -1
- package/commands/gsd/execute-phase.md +12 -7
- package/commands/gsd/extract-learnings.md +5 -0
- package/commands/gsd/fast.md +4 -0
- package/commands/gsd/forensics.md +5 -1
- package/commands/gsd/graphify.md +10 -6
- package/commands/gsd/health.md +5 -0
- package/commands/gsd/help.md +7 -2
- package/commands/gsd/import.md +7 -3
- package/commands/gsd/inbox.md +5 -0
- package/commands/gsd/ingest-docs.md +5 -1
- package/commands/gsd/manager.md +6 -1
- package/commands/gsd/map-codebase.md +7 -3
- package/commands/gsd/mempalace-capture.md +5 -1
- package/commands/gsd/mempalace-recall.md +5 -1
- package/commands/gsd/milestone-summary.md +5 -1
- package/commands/gsd/mvp-phase.md +8 -3
- package/commands/gsd/new-milestone.md +6 -1
- package/commands/gsd/new-project.md +5 -0
- package/commands/gsd/next.md +6 -1
- package/commands/gsd/ns-context.md +4 -0
- package/commands/gsd/ns-ideate.md +4 -0
- package/commands/gsd/ns-manage.md +4 -0
- package/commands/gsd/ns-project.md +4 -0
- package/commands/gsd/ns-review.md +4 -0
- package/commands/gsd/ns-workflow.md +4 -0
- package/commands/gsd/onboard.md +6 -1
- package/commands/gsd/pause-work.md +5 -1
- package/commands/gsd/phase.md +8 -4
- package/commands/gsd/plan-phase.md +6 -1
- package/commands/gsd/plan-review-convergence.md +5 -1
- package/commands/gsd/pr-branch.md +4 -0
- package/commands/gsd/profile-user.md +5 -1
- package/commands/gsd/progress.md +6 -1
- package/commands/gsd/quick-batch.md +20 -8
- package/commands/gsd/quick.md +12 -7
- package/commands/gsd/review.md +5 -1
- package/commands/gsd/secure-phase.md +6 -1
- package/commands/gsd/ship.md +5 -0
- package/commands/gsd/sketch.md +7 -2
- package/commands/gsd/spec-phase.md +5 -1
- package/commands/gsd/spike.md +8 -3
- package/commands/gsd/surface.md +5 -1
- package/commands/gsd/thread.md +4 -0
- package/commands/gsd/ui-phase.md +6 -1
- package/commands/gsd/ui-review.md +6 -1
- package/commands/gsd/ultraplan-phase.md +5 -1
- package/commands/gsd/undo.md +5 -1
- package/commands/gsd/update.md +6 -2
- package/commands/gsd/validate-phase.md +6 -1
- package/commands/gsd/verify-work.md +6 -1
- package/commands/gsd/workspace.md +7 -3
- package/gsd-core/bin/gsd-tools.cjs +159 -58
- package/gsd-core/bin/lib/active-workstream-store.cjs +15 -0
- package/gsd-core/bin/lib/adr-parser.cjs +37 -5
- package/gsd-core/bin/lib/agent-install-check.cjs +4 -1
- package/gsd-core/bin/lib/audit.cjs +78 -44
- package/gsd-core/bin/lib/broken-windows.cjs +13 -13
- package/gsd-core/bin/lib/capability-activation.cjs +9 -4
- package/gsd-core/bin/lib/capability-registry.cjs +249 -117
- package/gsd-core/bin/lib/capability-validator.cjs +18 -1
- package/gsd-core/bin/lib/capability-writer.cjs +2 -1
- package/gsd-core/bin/lib/check-auto-mode.cjs +35 -0
- package/gsd-core/bin/lib/check-command-router.cjs +164 -1712
- package/gsd-core/bin/lib/cjs-command-router-adapter.cjs +13 -2
- package/gsd-core/bin/lib/cli-exit.cjs +12 -0
- package/gsd-core/bin/lib/codex-agent-toml.cjs +11 -8
- package/gsd-core/bin/lib/command-aliases.cjs +7 -0
- package/gsd-core/bin/lib/command-routing-hub.cjs +48 -1
- package/gsd-core/bin/lib/commands.cjs +201 -178
- package/gsd-core/bin/lib/complexity-trigger.cjs +8 -7
- package/gsd-core/bin/lib/config.cjs +43 -12
- package/gsd-core/bin/lib/core-utils.cjs +6 -1
- package/gsd-core/bin/lib/coverage.cjs +4 -8
- package/gsd-core/bin/lib/decision-coverage-support.cjs +259 -0
- package/gsd-core/bin/lib/drift.cjs +177 -42
- package/gsd-core/bin/lib/frontmatter-fence.cjs +90 -0
- package/gsd-core/bin/lib/frontmatter-splice.cjs +494 -0
- package/gsd-core/bin/lib/frontmatter.cjs +413 -234
- package/gsd-core/bin/lib/gap-checker.cjs +72 -29
- package/gsd-core/bin/lib/gate-api-coverage-verify-pre.cjs +381 -0
- package/gsd-core/bin/lib/gate-args.cjs +53 -0
- package/gsd-core/bin/lib/gate-codebase-drift.cjs +285 -0
- package/gsd-core/bin/lib/gate-config.cjs +46 -0
- package/gsd-core/bin/lib/gate-context-drift.cjs +142 -0
- package/gsd-core/bin/lib/gate-decision-coverage-plan.cjs +169 -0
- package/gsd-core/bin/lib/gate-decision-coverage-verify.cjs +126 -0
- package/gsd-core/bin/lib/gate-evaluation-scope.cjs +555 -0
- package/gsd-core/bin/lib/gate-evidence.cjs +138 -0
- package/gsd-core/bin/lib/gate-exit.cjs +27 -0
- package/gsd-core/bin/lib/gate-gap-analysis-plan-post.cjs +61 -0
- package/gsd-core/bin/lib/gate-phase-context.cjs +170 -0
- package/gsd-core/bin/lib/gate-predicate-evaluator.cjs +1 -1
- package/gsd-core/bin/lib/gate-predicate.cjs +165 -0
- package/gsd-core/bin/lib/gate-prohibition-enforcement.cjs +94 -0
- package/gsd-core/bin/lib/gate-schema-drift.cjs +165 -0
- package/gsd-core/bin/lib/gate-tdd-red-evidence.cjs +100 -0
- package/gsd-core/bin/lib/gate-tdd-review-checkpoint.cjs +182 -0
- package/gsd-core/bin/lib/gate-ui-plan.cjs +86 -0
- package/gsd-core/bin/lib/gate-ui-safety.cjs +81 -0
- package/gsd-core/bin/lib/gate-verdict.cjs +64 -0
- package/gsd-core/bin/lib/gate-verify-command-paths.cjs +78 -0
- package/gsd-core/bin/lib/gate-verify-failure-directions.cjs +41 -0
- package/gsd-core/bin/lib/graphify.cjs +3 -2
- package/gsd-core/bin/lib/health-diagnostic-rules/phase-structure.cjs +43 -47
- package/gsd-core/bin/lib/health-diagnostic.cjs +45 -8
- package/gsd-core/bin/lib/init.cjs +194 -108
- package/gsd-core/bin/lib/install-engine.cjs +30 -35
- package/gsd-core/bin/lib/install-profiles.cjs +7 -4
- package/gsd-core/bin/lib/install-shadow-report.cjs +12 -0
- package/gsd-core/bin/lib/installer-migrations/011-opencode-mcp-no-prompts.cjs +99 -0
- package/gsd-core/bin/lib/installer-migrations.cjs +54 -1
- package/gsd-core/bin/lib/io.cjs +122 -3
- package/gsd-core/bin/lib/jsonc-edit.cjs +418 -0
- package/gsd-core/bin/lib/loop-host-contract.cjs +2 -1
- package/gsd-core/bin/lib/loop-resolver.cjs +95 -0
- package/gsd-core/bin/lib/markdown-sectionizer.cjs +75 -1
- package/gsd-core/bin/lib/mcp-server.cjs +5 -1
- package/gsd-core/bin/lib/milestone.cjs +19 -1
- package/gsd-core/bin/lib/model-resolver.cjs +18 -18
- package/gsd-core/bin/lib/observability/event.cjs +1 -1
- package/gsd-core/bin/lib/observability/logger.cjs +46 -1
- package/gsd-core/bin/lib/opencode-mcp-entry.cjs +39 -0
- package/gsd-core/bin/lib/opencode-version.cjs +486 -0
- package/gsd-core/bin/lib/pattern.cjs +10 -0
- package/gsd-core/bin/lib/phase-command-router.cjs +11 -4
- package/gsd-core/bin/lib/phase-estimation.cjs +5 -4
- package/gsd-core/bin/lib/phase-id.cjs +1 -1
- package/gsd-core/bin/lib/phase-lifecycle.cjs +9 -2
- package/gsd-core/bin/lib/phase-status.cjs +360 -0
- package/gsd-core/bin/lib/phase.cjs +301 -80
- package/gsd-core/bin/lib/plan-document.cjs +93 -19
- package/gsd-core/bin/lib/plan-drift-guard.cjs +5 -0
- package/gsd-core/bin/lib/planning-document.cjs +273 -40
- package/gsd-core/bin/lib/planning-inspect.cjs +42 -8
- package/gsd-core/bin/lib/planning-snapshot.cjs +18 -0
- package/gsd-core/bin/lib/planning-workspace.cjs +76 -53
- package/gsd-core/bin/lib/pristine-baseline.cjs +10 -0
- package/gsd-core/bin/lib/profile-output.cjs +6 -3
- package/gsd-core/bin/lib/prohibition-enforcement.cjs +0 -55
- package/gsd-core/bin/lib/quick-batch-command-router.cjs +35 -9
- package/gsd-core/bin/lib/quick-batch-dispatch.cjs +13 -9
- package/gsd-core/bin/lib/real-home-guard.cjs +9 -1
- package/gsd-core/bin/lib/report-parser.cjs +269 -0
- package/gsd-core/bin/lib/roadmap-command-router.cjs +13 -15
- package/gsd-core/bin/lib/roadmap-parser.cjs +82 -15
- package/gsd-core/bin/lib/roadmap-upgrade.cjs +127 -65
- package/gsd-core/bin/lib/roadmap.cjs +169 -72
- package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +186 -161
- package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +2 -1
- package/gsd-core/bin/lib/runtime-artifact-layout.cjs +57 -1
- package/gsd-core/bin/lib/runtime-homes.cjs +13 -10
- package/gsd-core/bin/lib/runtime-hooks-surface.cjs +365 -594
- package/gsd-core/bin/lib/runtime-name-policy.cjs +142 -27
- package/gsd-core/bin/lib/runtime-slash.cjs +60 -30
- package/gsd-core/bin/lib/shell-command-projection.cjs +37 -2
- package/gsd-core/bin/lib/smart-entry.cjs +111 -56
- package/gsd-core/bin/lib/stale-bake-guard.cjs +32 -48
- package/gsd-core/bin/lib/state-contract.cjs +15 -18
- package/gsd-core/bin/lib/state-document.cjs +100 -22
- package/gsd-core/bin/lib/state-transition.cjs +67 -9
- package/gsd-core/bin/lib/state.cjs +668 -171
- package/gsd-core/bin/lib/surface.cjs +2 -1
- package/gsd-core/bin/lib/tdd-red-evidence.cjs +48 -152
- package/gsd-core/bin/lib/uat-predicate.cjs +313 -35
- package/gsd-core/bin/lib/uat.cjs +424 -7
- package/gsd-core/bin/lib/ui-frontend-evidence.cjs +94 -58
- package/gsd-core/bin/lib/update-context.cjs +18 -1
- package/gsd-core/bin/lib/vendor/README.md +31 -9
- package/gsd-core/bin/lib/vendor/saxes.cjs +1934 -0
- package/gsd-core/bin/lib/vendor/saxes.cjs.LICENSE.txt +92 -0
- package/gsd-core/bin/lib/vendor/tap-parser.cjs +8927 -0
- package/gsd-core/bin/lib/vendor/tap-parser.cjs.LICENSE.txt +152 -0
- package/gsd-core/bin/lib/verification-command-router.cjs +2 -1
- package/gsd-core/bin/lib/verification.cjs +1265 -324
- package/gsd-core/bin/lib/verify-command-router.cjs +18 -7
- package/gsd-core/bin/lib/verify.cjs +286 -567
- package/gsd-core/bin/lib/workstream-inventory-builder.cjs +14 -6
- package/gsd-core/bin/lib/workstream-inventory.cjs +31 -21
- package/gsd-core/bin/lib/workstream-name-policy.cjs +31 -1
- package/gsd-core/bin/lib/workstream.cjs +11 -2
- package/gsd-core/bin/shared/config-defaults.manifest.json +5 -0
- package/gsd-core/bin/shared/config-schema.manifest.json +4 -0
- package/gsd-core/references/autonomous-smart-discuss.md +2 -1
- package/gsd-core/references/autonomous-ui-design-contract.md +3 -3
- package/gsd-core/references/execute-mvp-tdd.md +5 -10
- package/gsd-core/references/execute-phase-between-wave-reset.md +3 -0
- package/gsd-core/references/execute-phase-response-language.md +1 -1
- package/gsd-core/references/gsd-run-resolver.md +1 -1
- package/gsd-core/references/loop-hook-dispatch.md +7 -1
- package/gsd-core/references/offer-next.md +1 -1
- package/gsd-core/references/planning-config.md +1 -1
- package/gsd-core/references/spidr-splitting.md +1 -1
- package/gsd-core/references/tdd.md +10 -4
- package/gsd-core/references/verifier-phase-gates.md +5 -2
- package/gsd-core/references/verify-mvp-mode.md +2 -2
- package/gsd-core/references/workstream-flag.md +33 -3
- package/gsd-core/templates/README.md +1 -1
- package/gsd-core/templates/UAT.md +17 -1
- package/gsd-core/templates/config.json +2 -11
- package/gsd-core/templates/verification-report.md +1 -1
- package/gsd-core/workflows/_runtime-launcher.snippet.sh +1 -1
- package/gsd-core/workflows/add-backlog.md +1 -1
- package/gsd-core/workflows/add-phase.md +8 -7
- package/gsd-core/workflows/add-tests.md +3 -2
- package/gsd-core/workflows/add-todo.md +4 -3
- package/gsd-core/workflows/ai-integration-phase.md +3 -2
- package/gsd-core/workflows/audit-fix.md +1 -1
- package/gsd-core/workflows/audit-milestone.md +4 -3
- package/gsd-core/workflows/audit-uat.md +1 -1
- package/gsd-core/workflows/autonomous.md +28 -18
- package/gsd-core/workflows/check-todos.md +6 -5
- package/gsd-core/workflows/cleanup.md +1 -1
- package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +20 -13
- package/gsd-core/workflows/code-review-fix.md +5 -4
- package/gsd-core/workflows/code-review.md +100 -91
- package/gsd-core/workflows/complete-milestone/detail/elaboration.md +4 -3
- package/gsd-core/workflows/complete-milestone.md +12 -7
- package/gsd-core/workflows/debug.md +30 -5
- package/gsd-core/workflows/diagnose-issues.md +3 -2
- package/gsd-core/workflows/discuss-phase/modes/advisor.md +1 -1
- package/gsd-core/workflows/discuss-phase/modes/chain.md +1 -1
- package/gsd-core/workflows/discuss-phase-assumptions.md +4 -3
- package/gsd-core/workflows/discuss-phase.md +4 -3
- package/gsd-core/workflows/do.md +1 -1
- package/gsd-core/workflows/docs-update.md +4 -3
- package/gsd-core/workflows/edit-phase.md +4 -3
- package/gsd-core/workflows/eval-review.md +5 -3
- package/gsd-core/workflows/execute-phase/detail/elaboration.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/code-review-disposition.md +4 -2
- package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +15 -4
- package/gsd-core/workflows/execute-phase/steps/completion-reconciliation.md +10 -7
- package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +2 -2
- package/gsd-core/workflows/execute-phase/steps/gap-closure-artifacts.md +3 -1
- package/gsd-core/workflows/execute-phase/steps/partial-wave.md +2 -2
- package/gsd-core/workflows/execute-phase/steps/per-plan-executor-routing.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/protected-branch.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/regression-gate.md +2 -0
- package/gsd-core/workflows/execute-phase/steps/tdd-applicability-resolution.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/verify-phase-goal.md +187 -0
- package/gsd-core/workflows/execute-phase/steps/wave-post-gate-hooks.md +2 -3
- package/gsd-core/workflows/execute-phase.md +81 -171
- package/gsd-core/workflows/execute-plan.md +13 -19
- package/gsd-core/workflows/explore.md +2 -2
- package/gsd-core/workflows/extract-learnings.md +3 -2
- package/gsd-core/workflows/fast.md +1 -1
- package/gsd-core/workflows/forensics.md +1 -1
- package/gsd-core/workflows/graduation.md +1 -1
- package/gsd-core/workflows/health.md +2 -1
- package/gsd-core/workflows/import.md +3 -2
- package/gsd-core/workflows/inbox.md +1 -1
- package/gsd-core/workflows/ingest-docs.md +1 -1
- package/gsd-core/workflows/insert-phase.md +4 -3
- package/gsd-core/workflows/list-seeds.md +1 -1
- package/gsd-core/workflows/list-workspaces.md +1 -1
- package/gsd-core/workflows/manager.md +5 -3
- package/gsd-core/workflows/map-codebase.md +4 -3
- package/gsd-core/workflows/milestone-summary.md +3 -2
- package/gsd-core/workflows/mvp-phase.md +14 -14
- package/gsd-core/workflows/new-milestone.md +10 -10
- package/gsd-core/workflows/new-project/steps/auto-mode-config.md +1 -1
- package/gsd-core/workflows/new-project.md +1 -1
- package/gsd-core/workflows/new-workspace.md +1 -1
- package/gsd-core/workflows/next.md +1 -1
- package/gsd-core/workflows/pause-work.md +2 -2
- package/gsd-core/workflows/plan-phase/detail/elaboration.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/closed-phase-gate.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +1 -1
- package/gsd-core/workflows/plan-phase.md +27 -18
- package/gsd-core/workflows/plan-review-convergence.md +1 -1
- package/gsd-core/workflows/plant-seed.md +1 -1
- package/gsd-core/workflows/pr-branch.md +1 -1
- package/gsd-core/workflows/profile-user.md +1 -1
- package/gsd-core/workflows/progress.md +19 -49
- package/gsd-core/workflows/quick/steps/plan-checker-loop.md +4 -2
- package/gsd-core/workflows/quick/steps/quick-verification.md +4 -4
- package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +30 -11
- package/gsd-core/workflows/quick-batch/steps/batch-init.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/completion.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/merge-wave.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/planner-wave.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/research-phase.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/resume-mode.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/verification-wave.md +9 -3
- package/gsd-core/workflows/quick-batch/steps/worktree-dispatch.md +1 -1
- package/gsd-core/workflows/quick-batch.md +15 -10
- package/gsd-core/workflows/quick.md +42 -31
- package/gsd-core/workflows/remove-phase.md +3 -2
- package/gsd-core/workflows/remove-workspace.md +1 -1
- package/gsd-core/workflows/resume-project.md +17 -12
- package/gsd-core/workflows/review.md +3 -2
- package/gsd-core/workflows/scan.md +3 -2
- package/gsd-core/workflows/secure-phase.md +11 -12
- package/gsd-core/workflows/settings-advanced.md +1 -1
- package/gsd-core/workflows/settings-integrations.md +1 -1
- package/gsd-core/workflows/settings.md +1 -1
- package/gsd-core/workflows/ship.md +12 -12
- package/gsd-core/workflows/sketch-wrap-up.md +1 -1
- package/gsd-core/workflows/sketch.md +1 -1
- package/gsd-core/workflows/smart-entry.md +1 -1
- package/gsd-core/workflows/spec-phase.md +1 -1
- package/gsd-core/workflows/spike-wrap-up.md +1 -1
- package/gsd-core/workflows/spike.md +1 -1
- package/gsd-core/workflows/stats.md +1 -1
- package/gsd-core/workflows/sync-skills.md +1 -1
- package/gsd-core/workflows/thread.md +2 -2
- package/gsd-core/workflows/transition.md +13 -23
- package/gsd-core/workflows/ui-phase.md +5 -4
- package/gsd-core/workflows/ui-review.md +4 -3
- package/gsd-core/workflows/ultraplan-phase.md +3 -2
- package/gsd-core/workflows/undo.md +1 -1
- package/gsd-core/workflows/validate-phase.md +10 -12
- package/gsd-core/workflows/verify-work/detail/elaboration.md +43 -3
- package/gsd-core/workflows/verify-work/steps/automated-ui-verification.md +1 -1
- package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +5 -3
- package/gsd-core/workflows/verify-work.md +68 -59
- package/hooks/dist/gsd-agent-isolation-guard.js +8 -0
- package/hooks/dist/gsd-check-update-worker.js +8 -0
- package/hooks/dist/gsd-check-update.js +34 -20
- package/hooks/dist/gsd-context-monitor.js +45 -13
- package/hooks/dist/gsd-cursor-subagent-start.js +8 -0
- package/hooks/dist/gsd-secret-read-guard.js +263 -16
- package/hooks/dist/gsd-statusline.js +336 -29
- package/hooks/dist/gsd-update-banner.js +8 -0
- package/hooks/dist/gsd-windsurf-pre-write.js +11 -2
- package/hooks/dist/gsd-workflow-guard.js +5 -4
- package/hooks/dist/gsd-worktree-path-guard.js +6 -2
- package/hooks/dist/lib/cli-exit.js +12 -0
- package/hooks/dist/lib/git-probe.js +17 -1
- package/hooks/dist/lib/isolation-sentinel.js +2 -2
- package/hooks/gsd-agent-isolation-guard.js +8 -0
- package/hooks/gsd-check-update-worker.js +8 -0
- package/hooks/gsd-check-update.js +34 -20
- package/hooks/gsd-context-monitor.js +45 -13
- package/hooks/gsd-cursor-subagent-start.js +8 -0
- package/hooks/gsd-secret-read-guard.js +263 -16
- package/hooks/gsd-statusline.js +336 -29
- package/hooks/gsd-update-banner.js +8 -0
- package/hooks/gsd-windsurf-pre-write.js +11 -2
- package/hooks/gsd-workflow-guard.js +5 -4
- package/hooks/gsd-worktree-path-guard.js +6 -2
- package/hooks/hooks.json +5 -5
- package/hooks/lib/cli-exit.js +12 -0
- package/hooks/lib/git-probe.js +17 -1
- package/hooks/lib/isolation-sentinel.js +2 -2
- package/package.json +21 -4
- package/scripts/changeset/parse.cjs +52 -4
- package/scripts/ci-timeout-report.cjs +770 -4
- package/scripts/command-contract-helpers.cjs +12 -8
- package/scripts/docs-guard-registry.cjs +6 -0
- package/scripts/gen-capability-registry.cjs +7 -1
- package/scripts/gen-features.cjs +13 -8
- package/scripts/gen-hooks-cli-exit.cjs +12 -28
- package/scripts/gen-loop-host-contract.cjs +14 -2
- package/scripts/gen-platform-conformance-tier.cjs +187 -1
- package/scripts/gen-plugin-skills.cjs +87 -1
- package/scripts/gen-research-agents.cjs +24 -31
- package/scripts/gen-scripts-cli-exit.cjs +30 -3
- package/scripts/gen-test-timings.cjs +32 -7
- package/scripts/lib/cli-exit.cjs +12 -0
- package/scripts/lib/macos-conformance-tier.generated.cjs +24 -2
- package/scripts/lib/ndjson-reporter.cjs +28 -3
- package/scripts/lib/platform-conformance-tier.generated.cjs +44 -5
- package/scripts/lib/registration-ledger-preload.cjs +155 -0
- package/scripts/lib/vendor-bundle.cjs +59 -0
- package/scripts/lib/vendor-licenses/saxes-6.0.0.txt +64 -0
- package/scripts/lint-allow-test-rule-refs.effective-ceiling.json +1 -1
- package/scripts/lint-allow-test-rule-refs.unverified-ceiling.json +1 -1
- package/scripts/lint-completion-predicate-drift.cjs +18 -19
- package/scripts/lint-descriptions.cjs +7 -3
- package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +10 -0
- package/scripts/lint-eslint-glob-coverage.allowlist.json +20 -0
- package/scripts/lint-frontmatter-fence-drift.cjs +313 -0
- package/scripts/lint-frontmatter-scalar-broad-grep.cjs +122 -16
- package/scripts/lint-phase-enumeration-drift.cjs +12 -8
- package/scripts/lint-phase-id-drift.cjs +29 -0
- package/scripts/lint-planning-document-positive-control.cjs +329 -0
- package/scripts/lint-response-language-coverage.cjs +3 -0
- package/scripts/lint-skill-deps.cjs +7 -3
- package/scripts/lint-test-file-count.allowlist.json +11 -1
- package/scripts/lint-test-file-count.cjs +34 -1
- package/scripts/lint-vendored-deps.cjs +41 -5
- package/scripts/lint-workflow-shellcheck-baseline.json +10 -10
- package/scripts/mutation-matrix.cjs +53 -6
- package/scripts/release-tarball-smoke.cjs +14 -7
- package/scripts/require-issue-link-policy.cjs +6 -2
- package/scripts/sync-runtime-launcher.cjs +184 -2
- package/scripts/verify-npm-publish.cjs +76 -20
- package/skills/gsd-add-tests/SKILL.md +6 -1
- package/skills/gsd-ai-integration-phase/SKILL.md +6 -1
- package/skills/gsd-audit-fix/SKILL.md +5 -0
- package/skills/gsd-audit-milestone/SKILL.md +6 -1
- package/skills/gsd-autonomous/SKILL.md +5 -0
- package/skills/gsd-capture/SKILL.md +8 -4
- package/skills/gsd-code-review/SKILL.md +7 -2
- package/skills/gsd-complete-milestone/SKILL.md +4 -0
- package/skills/gsd-config/SKILL.md +8 -4
- package/skills/gsd-debug/SKILL.md +11 -7
- package/skills/gsd-discuss-phase/SKILL.md +7 -3
- package/skills/gsd-docs-update/SKILL.md +12 -7
- package/skills/gsd-eval-review/SKILL.md +6 -1
- package/skills/gsd-execute-phase/SKILL.md +12 -7
- package/skills/gsd-extract-learnings/SKILL.md +5 -0
- package/skills/gsd-fast/SKILL.md +4 -0
- package/skills/gsd-forensics/SKILL.md +5 -1
- package/skills/gsd-graphify/SKILL.md +10 -6
- package/skills/gsd-health/SKILL.md +5 -0
- package/skills/gsd-help/SKILL.md +7 -2
- package/skills/gsd-import/SKILL.md +7 -3
- package/skills/gsd-inbox/SKILL.md +5 -0
- package/skills/gsd-ingest-docs/SKILL.md +5 -1
- package/skills/gsd-manager/SKILL.md +6 -1
- package/skills/gsd-map-codebase/SKILL.md +7 -3
- package/skills/gsd-mempalace-capture/SKILL.md +5 -1
- package/skills/gsd-mempalace-recall/SKILL.md +5 -1
- package/skills/gsd-milestone-summary/SKILL.md +5 -1
- package/skills/gsd-mvp-phase/SKILL.md +8 -3
- package/skills/gsd-new-milestone/SKILL.md +6 -1
- package/skills/gsd-new-project/SKILL.md +5 -0
- package/skills/gsd-next/SKILL.md +6 -1
- package/skills/gsd-ns-context/SKILL.md +4 -0
- package/skills/gsd-ns-ideate/SKILL.md +4 -0
- package/skills/gsd-ns-manage/SKILL.md +4 -0
- package/skills/gsd-ns-project/SKILL.md +4 -0
- package/skills/gsd-ns-review/SKILL.md +4 -0
- package/skills/gsd-ns-workflow/SKILL.md +4 -0
- package/skills/gsd-onboard/SKILL.md +6 -1
- package/skills/gsd-pause-work/SKILL.md +5 -1
- package/skills/gsd-phase/SKILL.md +8 -4
- package/skills/gsd-plan-phase/SKILL.md +6 -1
- package/skills/gsd-plan-review-convergence/SKILL.md +5 -1
- package/skills/gsd-pr-branch/SKILL.md +4 -0
- package/skills/gsd-profile-user/SKILL.md +5 -1
- package/skills/gsd-progress/SKILL.md +6 -1
- package/skills/gsd-quick/SKILL.md +16 -10
- package/skills/gsd-quick-batch/SKILL.md +20 -8
- package/skills/gsd-review/SKILL.md +5 -1
- package/skills/gsd-review-backlog/SKILL.md +3 -2
- package/skills/gsd-secure-phase/SKILL.md +6 -1
- package/skills/gsd-ship/SKILL.md +5 -0
- package/skills/gsd-sketch/SKILL.md +7 -2
- package/skills/gsd-spec-phase/SKILL.md +5 -1
- package/skills/gsd-spike/SKILL.md +8 -3
- package/skills/gsd-surface/SKILL.md +5 -1
- package/skills/gsd-thread/SKILL.md +4 -0
- package/skills/gsd-ui-phase/SKILL.md +6 -1
- package/skills/gsd-ui-review/SKILL.md +6 -1
- package/skills/gsd-ultraplan-phase/SKILL.md +5 -1
- package/skills/gsd-undo/SKILL.md +5 -1
- package/skills/gsd-update/SKILL.md +6 -2
- package/skills/gsd-validate-phase/SKILL.md +6 -1
- package/skills/gsd-verify-work/SKILL.md +6 -1
- package/skills/gsd-workspace/SKILL.md +7 -3
- package/skills/gsd-workstreams/SKILL.md +6 -6
- package/vscode/package.json +1 -1
- package/gsd-core/workflows/execute-phase/steps/stale-reverification.md +0 -24
|
@@ -0,0 +1,486 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* OpenCode generation detection (ADR-5331 D2, D5; epic #5331, Phase 1 #5337).
|
|
4
|
+
*
|
|
5
|
+
* OpenCode 1.x and 2.x share one command name (`opencode`), one set of config
|
|
6
|
+
* locations and cannot be installed side by side, so GSD's `opencode` runtime
|
|
7
|
+
* cannot tell them apart from the descriptor alone. This module answers one
|
|
8
|
+
* question — "which generation is installed?" — and adapts the two spawn-time
|
|
9
|
+
* argument shapes that differ between generations.
|
|
10
|
+
*
|
|
11
|
+
* Evidence (ADR-5331 evidence register; reproduced against the real binaries):
|
|
12
|
+
* E2 `opencode --version` prints `1.19.0` on 1.x and `opencode v2.0.27` on 2.x.
|
|
13
|
+
* E3 2.x `opencode run` has no `--dir` and no `--variant`; both exit 1 with usage.
|
|
14
|
+
* Effort travels as a `#variant` suffix on `--model provider/model#variant`.
|
|
15
|
+
*
|
|
16
|
+
* What this module deliberately does NOT do:
|
|
17
|
+
* - It does not infer 1.x from config. 2.x reads GSD's 1.x-shaped keys in place
|
|
18
|
+
* (E7), so a 1.x-shaped config proves nothing about the binary. Only 2.x-shaped
|
|
19
|
+
* keys (or `cli.json`) are evidence, and only for generation 2.
|
|
20
|
+
* - It does not guess. An unresolvable generation is `null`, and callers keep
|
|
21
|
+
* today's behavior.
|
|
22
|
+
* - It does not apply to Kilo. Kilo is a fork on the OpenCode 1.x line with its
|
|
23
|
+
* own binary (ADR-5331 D7); callers gate on the `opencode` runtime / lane slug.
|
|
24
|
+
*/
|
|
25
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
|
+
};
|
|
28
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
+
exports.OPENCODE_V1_FREEZE_DATE = exports.OPENCODE_GENERATION_ENV = void 0;
|
|
30
|
+
exports.parseOpencodeVersion = parseOpencodeVersion;
|
|
31
|
+
exports.generationForMajor = generationForMajor;
|
|
32
|
+
exports.probeOpencodeVersion = probeOpencodeVersion;
|
|
33
|
+
exports.detectGenerationSignals = detectGenerationSignals;
|
|
34
|
+
exports.parseGenerationOverride = parseGenerationOverride;
|
|
35
|
+
exports.resolveOpencodeGeneration = resolveOpencodeGeneration;
|
|
36
|
+
exports.resetOpencodeGenerationMemo = resetOpencodeGenerationMemo;
|
|
37
|
+
exports.detectOpencodeGeneration = detectOpencodeGeneration;
|
|
38
|
+
exports.adaptOrchestratorExecForGeneration = adaptOrchestratorExecForGeneration;
|
|
39
|
+
exports.adaptLaneInputForGeneration = adaptLaneInputForGeneration;
|
|
40
|
+
exports.adaptLanePlanForGeneration = adaptLanePlanForGeneration;
|
|
41
|
+
exports.classifyOpencodeDatabase = classifyOpencodeDatabase;
|
|
42
|
+
exports.resolveOpencodeDataDir = resolveOpencodeDataDir;
|
|
43
|
+
exports.describeDataDirNotice = describeDataDirNotice;
|
|
44
|
+
exports.describeDeprecationNotice = describeDeprecationNotice;
|
|
45
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
46
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
47
|
+
const node_child_process_1 = require("node:child_process");
|
|
48
|
+
const review_lane_invocation_cjs_1 = require("./review-lane-invocation.cjs");
|
|
49
|
+
const shell_command_projection_cjs_1 = require("./shell-command-projection.cjs");
|
|
50
|
+
/** Environment variable that forces a generation (`1` or `2`), bypassing the probe. */
|
|
51
|
+
exports.OPENCODE_GENERATION_ENV = 'GSD_OPENCODE_GENERATION';
|
|
52
|
+
// One whole line: optional `opencode` word, optional `v`, semver with optional
|
|
53
|
+
// pre-release/build. Both observed outputs (`1.19.0`, `opencode v2.0.27`) match;
|
|
54
|
+
// log noise, banners and multi-token lines do not.
|
|
55
|
+
const VERSION_LINE = /^(?:opencode\s+)?v?(\d+)\.(\d+)\.(\d+)((?:[-+][0-9A-Za-z.+-]+)?)$/i;
|
|
56
|
+
/**
|
|
57
|
+
* Parse the stdout of `opencode --version`.
|
|
58
|
+
*
|
|
59
|
+
* Only the first non-empty line is considered, and it must be a version and
|
|
60
|
+
* nothing else. Returns `null` for anything unparseable rather than guessing.
|
|
61
|
+
*/
|
|
62
|
+
function parseOpencodeVersion(raw) {
|
|
63
|
+
if (typeof raw !== 'string')
|
|
64
|
+
return null;
|
|
65
|
+
const first = raw.split(/\r?\n/).map((l) => l.trim()).find((l) => l.length > 0);
|
|
66
|
+
if (first === undefined)
|
|
67
|
+
return null;
|
|
68
|
+
const m = VERSION_LINE.exec(first);
|
|
69
|
+
if (!m)
|
|
70
|
+
return null;
|
|
71
|
+
const major = Number(m[1]);
|
|
72
|
+
if (!Number.isSafeInteger(major))
|
|
73
|
+
return null;
|
|
74
|
+
return { version: `${m[1]}.${m[2]}.${m[3]}${m[4]}`, major };
|
|
75
|
+
}
|
|
76
|
+
/** Map a parsed major to a generation. Major 0 is not a shipped OpenCode line → `null`. */
|
|
77
|
+
function generationForMajor(major) {
|
|
78
|
+
if (!Number.isInteger(major) || major < 1)
|
|
79
|
+
return null;
|
|
80
|
+
return major === 1 ? 1 : 2;
|
|
81
|
+
}
|
|
82
|
+
const DEFAULT_PROBE_TIMEOUT_MS = 5000;
|
|
83
|
+
const PROBE_OUTPUT_LIMIT = 1 << 16;
|
|
84
|
+
function text(v) {
|
|
85
|
+
if (v === null || v === undefined)
|
|
86
|
+
return '';
|
|
87
|
+
return typeof v === 'string' ? v : v.toString('utf8');
|
|
88
|
+
}
|
|
89
|
+
/** Single-line, length-bounded excerpt for diagnostics; never echoes control characters. */
|
|
90
|
+
function excerpt(s) {
|
|
91
|
+
return s.replace(/[\u0000-\u001f\u007f]+/g, ' ').trim().slice(0, 120);
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Run `<binary> --version` and parse the result.
|
|
95
|
+
*
|
|
96
|
+
* Argv only, never a shell: the binary name is the sole variable and is not
|
|
97
|
+
* interpolated into a command string. Never throws.
|
|
98
|
+
*/
|
|
99
|
+
function probeOpencodeVersion(opts = {}) {
|
|
100
|
+
const binary = typeof opts.binary === 'string' && opts.binary.length > 0 ? opts.binary : 'opencode';
|
|
101
|
+
const timeout = typeof opts.timeoutMs === 'number' && Number.isFinite(opts.timeoutMs) && opts.timeoutMs > 0
|
|
102
|
+
? opts.timeoutMs
|
|
103
|
+
: DEFAULT_PROBE_TIMEOUT_MS;
|
|
104
|
+
const spawn = opts.spawn ?? node_child_process_1.spawnSync;
|
|
105
|
+
let result;
|
|
106
|
+
try {
|
|
107
|
+
result = spawn(binary, ['--version'], {
|
|
108
|
+
encoding: 'utf8',
|
|
109
|
+
timeout,
|
|
110
|
+
shell: false,
|
|
111
|
+
windowsHide: true,
|
|
112
|
+
maxBuffer: PROBE_OUTPUT_LIMIT,
|
|
113
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
114
|
+
...(opts.env ? { env: opts.env } : {}),
|
|
115
|
+
...(opts.cwd ? { cwd: opts.cwd } : {}),
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
catch (e) {
|
|
119
|
+
return { ok: false, reason: 'error', detail: excerpt(e instanceof Error ? e.message : String(e)) };
|
|
120
|
+
}
|
|
121
|
+
if (result.error) {
|
|
122
|
+
const code = result.error.code;
|
|
123
|
+
if (code === 'ENOENT' || code === 'EACCES') {
|
|
124
|
+
return { ok: false, reason: 'missing', detail: `${binary}: ${code}` };
|
|
125
|
+
}
|
|
126
|
+
if (code === 'ETIMEDOUT') {
|
|
127
|
+
return { ok: false, reason: 'timeout', detail: `${binary} --version exceeded ${timeout}ms` };
|
|
128
|
+
}
|
|
129
|
+
return { ok: false, reason: 'error', detail: excerpt(result.error.message) };
|
|
130
|
+
}
|
|
131
|
+
if (result.signal === 'SIGTERM' || result.signal === 'SIGKILL') {
|
|
132
|
+
return { ok: false, reason: 'timeout', detail: `${binary} --version exceeded ${timeout}ms` };
|
|
133
|
+
}
|
|
134
|
+
if (result.status !== 0) {
|
|
135
|
+
return { ok: false, reason: 'nonzero', detail: `${binary} --version exited ${String(result.status)}` };
|
|
136
|
+
}
|
|
137
|
+
const raw = text(result.stdout);
|
|
138
|
+
const parsed = parseOpencodeVersion(raw);
|
|
139
|
+
if (!parsed) {
|
|
140
|
+
return { ok: false, reason: 'unparseable', detail: `unrecognized output: ${excerpt(raw)}` };
|
|
141
|
+
}
|
|
142
|
+
return { ok: true, version: parsed.version, major: parsed.major, raw: raw.trim() };
|
|
143
|
+
}
|
|
144
|
+
function isPlainObject(v) {
|
|
145
|
+
return v !== null && typeof v === 'object' && !Array.isArray(v);
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Look for evidence that a 2.x binary has been (or is being) used with this config.
|
|
149
|
+
*
|
|
150
|
+
* Positive evidence only: `cli.json` is created by 2.x on first start, and the
|
|
151
|
+
* following keys exist only in the native 2.x shape ([migrate-v1] renames):
|
|
152
|
+
* `plugins[]`, `agents{}`, `commands{}`, `permissions[]`, `skills[]`,
|
|
153
|
+
* `providers{}`, `snapshots`, `media{}` and `mcp.servers{}`. 1.x-shaped keys are
|
|
154
|
+
* deliberately not evidence of generation 1: 2.x reads them unchanged (E7).
|
|
155
|
+
*/
|
|
156
|
+
function detectGenerationSignals(opts) {
|
|
157
|
+
const fs = opts.fs ?? { exists: (p) => node_fs_1.default.existsSync(p) };
|
|
158
|
+
const evidence = [];
|
|
159
|
+
if (typeof opts.configDir === 'string' && opts.configDir.length > 0) {
|
|
160
|
+
try {
|
|
161
|
+
if (fs.exists(node_path_1.default.join(opts.configDir, 'cli.json')))
|
|
162
|
+
evidence.push('cli.json');
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
/* an unreadable directory is simply no evidence */
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
const cfg = opts.config;
|
|
169
|
+
if (isPlainObject(cfg)) {
|
|
170
|
+
if (Array.isArray(cfg['plugins']))
|
|
171
|
+
evidence.push('plugins[]');
|
|
172
|
+
if (isPlainObject(cfg['agents']))
|
|
173
|
+
evidence.push('agents{}');
|
|
174
|
+
if (isPlainObject(cfg['commands']))
|
|
175
|
+
evidence.push('commands{}');
|
|
176
|
+
if (Array.isArray(cfg['permissions']))
|
|
177
|
+
evidence.push('permissions[]');
|
|
178
|
+
if (Array.isArray(cfg['skills']))
|
|
179
|
+
evidence.push('skills[]');
|
|
180
|
+
if (isPlainObject(cfg['providers']))
|
|
181
|
+
evidence.push('providers{}');
|
|
182
|
+
if (typeof cfg['snapshots'] === 'boolean')
|
|
183
|
+
evidence.push('snapshots');
|
|
184
|
+
if (isPlainObject(cfg['media']))
|
|
185
|
+
evidence.push('media{}');
|
|
186
|
+
const mcp = cfg['mcp'];
|
|
187
|
+
if (isPlainObject(mcp) && isPlainObject(mcp['servers']))
|
|
188
|
+
evidence.push('mcp.servers{}');
|
|
189
|
+
}
|
|
190
|
+
return { generation: evidence.length > 0 ? 2 : null, evidence };
|
|
191
|
+
}
|
|
192
|
+
/** Accepts `1`, `2`, `v1`, `v2`, `opencode2`… in any case; anything else is not an override. */
|
|
193
|
+
function parseGenerationOverride(raw) {
|
|
194
|
+
if (typeof raw === 'number')
|
|
195
|
+
return raw === 1 || raw === 2 ? raw : null;
|
|
196
|
+
if (typeof raw !== 'string')
|
|
197
|
+
return null;
|
|
198
|
+
const m = /^\s*(?:opencode\s*)?v?([12])\s*$/i.exec(raw);
|
|
199
|
+
return m ? Number(m[1]) : null;
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Combine the evidence in precedence order: explicit override → probe → config
|
|
203
|
+
* signals → unknown. Pure; the caller supplies already-gathered evidence.
|
|
204
|
+
*/
|
|
205
|
+
function resolveOpencodeGeneration(inputs) {
|
|
206
|
+
const notes = [];
|
|
207
|
+
if (inputs.override !== undefined && inputs.override !== null && inputs.override !== '') {
|
|
208
|
+
const forced = parseGenerationOverride(inputs.override);
|
|
209
|
+
if (forced !== null) {
|
|
210
|
+
return { generation: forced, version: null, source: 'override', detail: `forced to ${forced}.x` };
|
|
211
|
+
}
|
|
212
|
+
notes.push(`ignored invalid generation override ${JSON.stringify(typeof inputs.override === 'string' ? inputs.override : typeof inputs.override).slice(0, 40)}`);
|
|
213
|
+
}
|
|
214
|
+
const probe = inputs.probe;
|
|
215
|
+
if (probe) {
|
|
216
|
+
if (probe.ok) {
|
|
217
|
+
const gen = generationForMajor(probe.major);
|
|
218
|
+
if (gen !== null) {
|
|
219
|
+
return { generation: gen, version: probe.version, source: 'probe', detail: notes.join('; ') || null };
|
|
220
|
+
}
|
|
221
|
+
notes.push(`opencode --version reported unsupported major ${probe.major}`);
|
|
222
|
+
}
|
|
223
|
+
else {
|
|
224
|
+
notes.push(`probe ${probe.reason}: ${probe.detail}`);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
const signals = inputs.signals;
|
|
228
|
+
if (signals && signals.generation !== null) {
|
|
229
|
+
notes.push(`2.x config signals: ${signals.evidence.join(', ')}`);
|
|
230
|
+
return { generation: signals.generation, version: null, source: 'signals', detail: notes.join('; ') };
|
|
231
|
+
}
|
|
232
|
+
return {
|
|
233
|
+
generation: null,
|
|
234
|
+
version: null,
|
|
235
|
+
source: 'unknown',
|
|
236
|
+
detail: notes.length > 0 ? notes.join('; ') : 'no evidence gathered',
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
// ─── Memoized detection for spawn-time callers ───────────────────────────────
|
|
240
|
+
let memo = null;
|
|
241
|
+
/** Test hook: forget the memoized detection. */
|
|
242
|
+
function resetOpencodeGenerationMemo() {
|
|
243
|
+
memo = null;
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Detect the installed generation once per process.
|
|
247
|
+
*
|
|
248
|
+
* For callers that build a `opencode run …` command line (worktree dispatch,
|
|
249
|
+
* the reviewer lane): the override environment variable wins, then one
|
|
250
|
+
* `--version` probe, memoized so a multi-lane review spawns it once.
|
|
251
|
+
*/
|
|
252
|
+
function detectOpencodeGeneration(opts = {}) {
|
|
253
|
+
const env = opts.overrideEnv ?? process.env;
|
|
254
|
+
const override = env[exports.OPENCODE_GENERATION_ENV];
|
|
255
|
+
const key = `${opts.binary ?? 'opencode'}\u0000${String(override ?? '')}`;
|
|
256
|
+
if (memo && memo.key === key)
|
|
257
|
+
return memo.value;
|
|
258
|
+
const value = resolveOpencodeGeneration({
|
|
259
|
+
override,
|
|
260
|
+
probe: parseGenerationOverride(override) !== null ? null : probeOpencodeVersion(opts),
|
|
261
|
+
});
|
|
262
|
+
memo = { key, value };
|
|
263
|
+
return value;
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* 2.x `opencode run` has no `--dir` (E3), so a descriptor that binds the worktree
|
|
267
|
+
* through `--dir` must bind it through the spawned process's own working directory
|
|
268
|
+
* instead — the "process-cwd" shape `resolveOrchestratorExec` already supports
|
|
269
|
+
* (`cwdFlag: null`, as for Kimi Code). Every other descriptor and generation is
|
|
270
|
+
* returned untouched.
|
|
271
|
+
*/
|
|
272
|
+
function adaptOrchestratorExecForGeneration(orchestratorExec, generation) {
|
|
273
|
+
if (generation !== 2)
|
|
274
|
+
return orchestratorExec;
|
|
275
|
+
if (!orchestratorExec || typeof orchestratorExec !== 'object')
|
|
276
|
+
return orchestratorExec;
|
|
277
|
+
if (orchestratorExec.cwdFlag !== '--dir')
|
|
278
|
+
return orchestratorExec;
|
|
279
|
+
return { ...orchestratorExec, cwdFlag: null };
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Adapt an OpenCode reviewer-lane resolution input for generation 2.
|
|
283
|
+
*
|
|
284
|
+
* 2.x rejects `--variant` (E3); a reasoning effort rides on the model reference
|
|
285
|
+
* instead (`provider/model#variant`, [D8] / [D5]). So for the `opencode` lane:
|
|
286
|
+
* - the `--variant` argv is dropped;
|
|
287
|
+
* - when an effort level and a configured model both exist, the model is
|
|
288
|
+
* returned from `configGet` with `#<level>` appended (never doubled: a model
|
|
289
|
+
* that already carries a `#variant` is the operator's explicit choice);
|
|
290
|
+
* - when an effort level exists but no model is configured, nothing can carry
|
|
291
|
+
* it, so the level is dropped and a warning says so.
|
|
292
|
+
* Any other lane, and generation 1 / unknown, is returned unchanged.
|
|
293
|
+
*/
|
|
294
|
+
function adaptLaneInputForGeneration(input, generation) {
|
|
295
|
+
const warnings = [];
|
|
296
|
+
if (generation !== 2)
|
|
297
|
+
return { input, warnings };
|
|
298
|
+
const lane = input.lane;
|
|
299
|
+
if (!lane || lane.slug !== 'opencode')
|
|
300
|
+
return { input, warnings };
|
|
301
|
+
const modelKey = typeof lane.modelConfigKey === 'string' ? lane.modelConfigKey : null;
|
|
302
|
+
const effort = typeof input.effortValue === 'string' && input.effortValue.length > 0 ? input.effortValue : null;
|
|
303
|
+
const hadEffortArgs = Array.isArray(input.effortArgs) && input.effortArgs.length > 0;
|
|
304
|
+
let configGet = input.configGet;
|
|
305
|
+
if (effort !== null && modelKey !== null) {
|
|
306
|
+
const configured = (0, review_lane_invocation_cjs_1.configString)(input.configGet(modelKey));
|
|
307
|
+
if (configured === null) {
|
|
308
|
+
warnings.push(`reasoning effort '${effort}' not applied to the OpenCode reviewer: OpenCode 2.x has no --variant ` +
|
|
309
|
+
`flag and no model is configured to carry a #variant. Set a model to apply it.`);
|
|
310
|
+
}
|
|
311
|
+
else if (!configured.includes('#')) {
|
|
312
|
+
const original = input.configGet;
|
|
313
|
+
configGet = (key) => key === modelKey ? `${configured}#${effort}` : original(key);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
else if (effort !== null) {
|
|
317
|
+
warnings.push(`reasoning effort '${effort}' not applied to the OpenCode reviewer: the lane declares no model key to carry a #variant.`);
|
|
318
|
+
}
|
|
319
|
+
if (!hadEffortArgs && configGet === input.configGet && warnings.length === 0) {
|
|
320
|
+
return { input, warnings };
|
|
321
|
+
}
|
|
322
|
+
return { input: { ...input, configGet, effortArgs: [] }, warnings };
|
|
323
|
+
}
|
|
324
|
+
/**
|
|
325
|
+
* 2.x `opencode run` treats a trailing `-` as message text, not as a "read stdin" marker: the model
|
|
326
|
+
* receives `-\n<prompt>` (verified on 2.0.27, E12). Piped stdin is read either way (E4), so for a
|
|
327
|
+
* stdin-fed opencode plan on generation 2 the marker is dropped and the run is made `--standalone`.
|
|
328
|
+
* Everything else is untouched.
|
|
329
|
+
*/
|
|
330
|
+
function adaptLanePlanForGeneration(result, generation) {
|
|
331
|
+
if (generation !== 2 || !result || result.ok !== true || !result.plan)
|
|
332
|
+
return result;
|
|
333
|
+
const plan = result.plan;
|
|
334
|
+
if (plan.slug !== 'opencode' || plan.stdin === null || plan.stdin === undefined)
|
|
335
|
+
return result;
|
|
336
|
+
const argv = plan.argv;
|
|
337
|
+
if (!Array.isArray(argv))
|
|
338
|
+
return result;
|
|
339
|
+
let next = argv;
|
|
340
|
+
if (next[next.length - 1] === '-')
|
|
341
|
+
next = next.slice(0, -1);
|
|
342
|
+
// `--standalone` ("Run with a private server instead of the background service", `opencode run
|
|
343
|
+
// --help` on 2.0.27): without it `run` attaches to a long-lived managed service on a fixed port
|
|
344
|
+
// that outlives the review, serves whichever config it booted with, and fails when another
|
|
345
|
+
// process holds the port. A review is one-shot, so it gets its own server.
|
|
346
|
+
const runIdx = next.indexOf('run');
|
|
347
|
+
if (runIdx !== -1 && !next.includes('--standalone')) {
|
|
348
|
+
next = [...next.slice(0, runIdx + 1), '--standalone', ...next.slice(runIdx + 1)];
|
|
349
|
+
}
|
|
350
|
+
if (next === argv)
|
|
351
|
+
return result;
|
|
352
|
+
return { ...result, plan: { ...plan, argv: next } };
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* Default reader: `node:sqlite`, read-only. Its schema lives in the write-ahead log until a checkpoint,
|
|
356
|
+
* so reading the main file's bytes would see an empty database; SQLite itself merges both. The module
|
|
357
|
+
* prints an ExperimentalWarning on some Node versions, which is swallowed for the duration of the read.
|
|
358
|
+
* Any failure (module missing, file locked or corrupt) is `null`, never a throw.
|
|
359
|
+
*/
|
|
360
|
+
function readTableNames(dbPath) {
|
|
361
|
+
const emit = process.emitWarning.bind(process);
|
|
362
|
+
try {
|
|
363
|
+
process.emitWarning = () => undefined;
|
|
364
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
365
|
+
const sqlite = require('node:sqlite');
|
|
366
|
+
const db = new sqlite.DatabaseSync(dbPath, { readOnly: true });
|
|
367
|
+
try {
|
|
368
|
+
return db.prepare("select name from sqlite_master where type='table'").all().map((r) => r.name);
|
|
369
|
+
}
|
|
370
|
+
finally {
|
|
371
|
+
db.close();
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
catch {
|
|
375
|
+
return null;
|
|
376
|
+
}
|
|
377
|
+
finally {
|
|
378
|
+
process.emitWarning = emit;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* Which generation(s) built an OpenCode database. 1.x keeps sessions in a `session` table; 2.x in
|
|
383
|
+
* `session_v2`. A database 2.x created from scratch has no `session` table and 1.x cannot open it; a 1.x
|
|
384
|
+
* database that 2.x later used gains the 2.x tables beside `session` and 1.x keeps working — both
|
|
385
|
+
* reproduced on 1.18.29 and 2.0.27 (ADR-5331 E8, E18). GSD only reads the schema; it never writes to this
|
|
386
|
+
* database.
|
|
387
|
+
*/
|
|
388
|
+
function classifyOpencodeDatabase(dbPath, deps = {}) {
|
|
389
|
+
const exists = deps.exists ?? ((p) => node_fs_1.default.existsSync(p));
|
|
390
|
+
if (!exists(dbPath))
|
|
391
|
+
return 'empty';
|
|
392
|
+
const names = (deps.readTables ?? readTableNames)(dbPath);
|
|
393
|
+
if (names === null)
|
|
394
|
+
return 'unknown';
|
|
395
|
+
const v1 = names.includes('session');
|
|
396
|
+
const v2 = names.includes('session_v2');
|
|
397
|
+
if (v1 && v2)
|
|
398
|
+
return 'both';
|
|
399
|
+
if (v2)
|
|
400
|
+
return 'v2';
|
|
401
|
+
if (v1)
|
|
402
|
+
return 'v1';
|
|
403
|
+
return names.length === 0 ? 'empty' : 'unknown';
|
|
404
|
+
}
|
|
405
|
+
/**
|
|
406
|
+
* The directory holding `opencode.db`: the `data` entry of `opencode debug paths` (the binary's own
|
|
407
|
+
* answer, identical on 1.x and 2.x), else the XDG default every OpenCode generation shares.
|
|
408
|
+
*/
|
|
409
|
+
function resolveOpencodeDataDir(opts = {}) {
|
|
410
|
+
const spawn = opts.spawn ?? node_child_process_1.spawnSync;
|
|
411
|
+
const binary = typeof opts.binary === 'string' && opts.binary.length > 0 ? opts.binary : 'opencode';
|
|
412
|
+
try {
|
|
413
|
+
const r = spawn(binary, ['debug', 'paths'], {
|
|
414
|
+
encoding: 'utf8',
|
|
415
|
+
timeout: opts.timeoutMs ?? DEFAULT_PROBE_TIMEOUT_MS,
|
|
416
|
+
shell: false,
|
|
417
|
+
windowsHide: true,
|
|
418
|
+
maxBuffer: PROBE_OUTPUT_LIMIT,
|
|
419
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
420
|
+
...(opts.env ? { env: opts.env } : {}),
|
|
421
|
+
});
|
|
422
|
+
if (!r.error && r.status === 0) {
|
|
423
|
+
const m = /^data[ \t]+(.+?)[ \t]*$/m.exec(text(r.stdout));
|
|
424
|
+
if (m && m[1] && node_path_1.default.isAbsolute(m[1]))
|
|
425
|
+
return m[1];
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
catch {
|
|
429
|
+
/* fall through to the XDG default */
|
|
430
|
+
}
|
|
431
|
+
const env = opts.env ?? process.env;
|
|
432
|
+
const home = opts.home ?? (0, shell_command_projection_cjs_1.envGet)(env, 'HOME') ?? (0, shell_command_projection_cjs_1.envGet)(env, 'USERPROFILE') ?? '';
|
|
433
|
+
const xdgData = (0, shell_command_projection_cjs_1.envGet)(env, 'XDG_DATA_HOME');
|
|
434
|
+
const base = xdgData && node_path_1.default.isAbsolute(xdgData) ? xdgData : node_path_1.default.join(home, '.local', 'share');
|
|
435
|
+
return node_path_1.default.join(base, 'opencode');
|
|
436
|
+
}
|
|
437
|
+
/**
|
|
438
|
+
* What to tell the operator about the data directory, given the installed generation and the schema
|
|
439
|
+
* generation(s) found there; `null` when there is nothing to say.
|
|
440
|
+
*/
|
|
441
|
+
function describeDataDirNotice(generation, kind, dataDir) {
|
|
442
|
+
if (generation === 1 && kind === 'v2') {
|
|
443
|
+
return {
|
|
444
|
+
level: 'warn',
|
|
445
|
+
text: `OpenCode 1.x is installed but its data directory (${dataDir}) was created by OpenCode 2.x. ` +
|
|
446
|
+
"1.x cannot open it (\"Database is not empty and has no session table\"). Reinstall 2.x, or restore a " +
|
|
447
|
+
'backup of the data directory taken before 2.x first ran. GSD never modifies this database.',
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
if (generation === 1 && kind === 'both') {
|
|
451
|
+
return {
|
|
452
|
+
level: 'info',
|
|
453
|
+
text: `OpenCode 2.x has also used the data directory (${dataDir}); it keeps its sessions in its own tables ` +
|
|
454
|
+
'beside the 1.x ones, and 1.x still opens it.',
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
if (generation === 2 && kind === 'v1') {
|
|
458
|
+
return {
|
|
459
|
+
level: 'info',
|
|
460
|
+
text: `OpenCode 2.x will add its own tables to the data directory (${dataDir}) beside the 1.x ones; ` +
|
|
461
|
+
'OpenCode 1.x still opens it afterwards. A data directory that 2.x creates from scratch, by contrast, ' +
|
|
462
|
+
'cannot be opened by 1.x.',
|
|
463
|
+
};
|
|
464
|
+
}
|
|
465
|
+
return null;
|
|
466
|
+
}
|
|
467
|
+
/**
|
|
468
|
+
* End of the OpenCode 1.x bug-fix-only window (ADR-5331 D7, epic #5331 Phase 3). A maintainer decision:
|
|
469
|
+
* 1.x receives fixes until this date, after which Phase 4 removes the 1.x paths for the `opencode` runtime.
|
|
470
|
+
* Kilo is a separate runtime on the 1.x line and is not affected.
|
|
471
|
+
*/
|
|
472
|
+
exports.OPENCODE_V1_FREEZE_DATE = '2026-12-31';
|
|
473
|
+
/**
|
|
474
|
+
* The deprecation notice shown when the installed `opencode` binary is OpenCode 1.x; `null` for 2.x and for an
|
|
475
|
+
* undetected generation. The upgrade commands are the binary's own: `opencode upgrade --major` is a flag of
|
|
476
|
+
* 1.19.0 (absent from 1.18.29) ("upgrade to OpenCode 2 and remove OpenCode 1"), and the V2 installer and package replace the 1.x binary
|
|
477
|
+
* (https://opencode.ai/v2/docs/migrate-v1/).
|
|
478
|
+
*/
|
|
479
|
+
function describeDeprecationNotice(generation) {
|
|
480
|
+
if (generation !== 1)
|
|
481
|
+
return null;
|
|
482
|
+
return (`OpenCode 1.x is deprecated for GSD: it receives bug fixes only until ${exports.OPENCODE_V1_FREEZE_DATE}, after which ` +
|
|
483
|
+
'GSD supports OpenCode 2.x only. Upgrade with `opencode upgrade --major` (OpenCode 1.19 and newer), or install ' +
|
|
484
|
+
'OpenCode 2 (`curl -fsSL https://opencode.ai/v2/install | bash`, or `npm install -g @opencode/cli`) and re-run the GSD installer. ' +
|
|
485
|
+
'Your configuration is read in place by 2.x. Kilo is not affected.');
|
|
486
|
+
}
|
|
@@ -44,6 +44,7 @@
|
|
|
44
44
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
45
|
exports.MAX_USER_PATTERN_LEN = void 0;
|
|
46
46
|
exports.escapeRegex = escapeRegex;
|
|
47
|
+
exports.escapeEre = escapeEre;
|
|
47
48
|
exports.literalPattern = literalPattern;
|
|
48
49
|
exports.compileUserPattern = compileUserPattern;
|
|
49
50
|
// re2js is vendored, not an npm dependency at runtime: gsd-core/bin/** is
|
|
@@ -66,6 +67,15 @@ const escapeMetachars = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
|
66
67
|
function escapeRegex(value) {
|
|
67
68
|
return (escapeBuiltin ?? escapeMetachars)(value);
|
|
68
69
|
}
|
|
70
|
+
/**
|
|
71
|
+
* Escape `value` for a POSIX extended regular expression (e.g. `git log --extended-regexp
|
|
72
|
+
* --grep=…`). Always the plain backslash-escape of every ERE metacharacter, never
|
|
73
|
+
* `RegExp.escape`: that built-in writes a leading digit as `\x31`, which ERE does not read as a
|
|
74
|
+
* digit. #5139 (a plan id interpolated into a git `--grep` pattern).
|
|
75
|
+
*/
|
|
76
|
+
function escapeEre(value) {
|
|
77
|
+
return escapeMetachars(value);
|
|
78
|
+
}
|
|
69
79
|
function literalPattern(value, flags) {
|
|
70
80
|
return new RegExp(escapeRegex(value), flags);
|
|
71
81
|
}
|
|
@@ -22,10 +22,12 @@ const commandRoutingHub = require("./command-routing-hub.cjs");
|
|
|
22
22
|
const { createHub, ERROR_KINDS, makeInvalidArgs } = commandRoutingHub;
|
|
23
23
|
// #2620 (ADR-0174 §6): inject the reference DispatchLogger on the live phase
|
|
24
24
|
// dispatch path, but only when observability is opt-in enabled; otherwise the
|
|
25
|
-
// Hub stays byte-for-byte silent via its no-op fallback.
|
|
25
|
+
// Hub stays byte-for-byte silent via its no-op fallback. #4975: the opt-in is
|
|
26
|
+
// GSD_AUDIT=1 or `audit.enabled` in the project config — resolveDispatchLogger
|
|
27
|
+
// owns that whole decision for every live seam.
|
|
26
28
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
27
29
|
const observabilityLogger = require("./observability/logger.cjs");
|
|
28
|
-
const {
|
|
30
|
+
const { resolveDispatchLogger } = observabilityLogger;
|
|
29
31
|
// ─── Implementation ───────────────────────────────────────────────────────────
|
|
30
32
|
function routePhaseCommand({ phase, args, cwd, raw, error }) {
|
|
31
33
|
// ── Unsupported subcommands ─────────────────────────────────────────────────
|
|
@@ -258,8 +260,8 @@ function routePhaseCommand({ phase, args, cwd, raw, error }) {
|
|
|
258
260
|
// #175: Hub is CJS-only — no mode param, no sdkLoader.
|
|
259
261
|
// #2620: wire the reference logger (ADR-0174 §6) only when observability is
|
|
260
262
|
// opt-in enabled; otherwise leave it unset so the Hub stays byte-for-byte
|
|
261
|
-
// silent via its no-op fallback.
|
|
262
|
-
const hub = createHub({ cjsRegistry, manifest, logger:
|
|
263
|
+
// silent via its no-op fallback. #4975: GSD_AUDIT=1 or `audit.enabled`.
|
|
264
|
+
const hub = createHub({ cjsRegistry, manifest, logger: resolveDispatchLogger(cwd) });
|
|
263
265
|
// ── Dispatch ────────────────────────────────────────────────────────────────
|
|
264
266
|
const result = hub.dispatch({
|
|
265
267
|
family: 'phase',
|
|
@@ -282,6 +284,11 @@ function routePhaseCommand({ phase, args, cwd, raw, error }) {
|
|
|
282
284
|
error(result.reason);
|
|
283
285
|
return;
|
|
284
286
|
}
|
|
287
|
+
if (result.kind === ERROR_KINDS.VerificationStatusInvalid) {
|
|
288
|
+
// #5118: typed reason survives to the CLI surface
|
|
289
|
+
error(result.message, result.reason);
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
285
292
|
// HandlerFailure: message field
|
|
286
293
|
error(result.message);
|
|
287
294
|
return;
|
|
@@ -47,6 +47,7 @@ exports.parseCalibrationDocument = parseCalibrationDocument;
|
|
|
47
47
|
exports.renderCalibrationDocument = renderCalibrationDocument;
|
|
48
48
|
// eslint-disable-next-line @typescript-eslint/no-require-imports -- prompt-budget.cjs is an export= CommonJS module
|
|
49
49
|
const promptBudget = require("./prompt-budget.cjs");
|
|
50
|
+
const frontmatter_fence_cjs_1 = require("./frontmatter-fence.cjs");
|
|
50
51
|
const { estimateTokens } = promptBudget;
|
|
51
52
|
/**
|
|
52
53
|
* Assert that a bare number is an UNCORRECTED projection.
|
|
@@ -239,11 +240,11 @@ function applyCalibration(rawTokens, factor) {
|
|
|
239
240
|
function extractFrontmatterBlock(text, key) {
|
|
240
241
|
if (typeof text !== 'string')
|
|
241
242
|
return null;
|
|
242
|
-
//
|
|
243
|
-
const
|
|
244
|
-
if (
|
|
243
|
+
// The block is the one the one fence owner finds (`locateFrontmatterFence`).
|
|
244
|
+
const fence = (0, frontmatter_fence_cjs_1.locateFrontmatterFence)(text);
|
|
245
|
+
if (!fence?.closed)
|
|
245
246
|
return null;
|
|
246
|
-
const lines =
|
|
247
|
+
const lines = text.slice(fence.openEnd, fence.bodyEnd).split(/\r?\n/);
|
|
247
248
|
const startIdx = lines.findIndex((l) => l === `${key}:` || l.startsWith(`${key}:`));
|
|
248
249
|
if (startIdx === -1)
|
|
249
250
|
return null;
|
|
@@ -1342,7 +1342,7 @@ const unpad = (digits) => digits.replace(/^0+(?=\d)/, '');
|
|
|
1342
1342
|
* candidates (#2237).
|
|
1343
1343
|
*
|
|
1344
1344
|
* TAKE `matches[0]` — `cmdPhasesList`, `cmdInitManager`, `cmdRoadmapAnalyze`,
|
|
1345
|
-
* `
|
|
1345
|
+
* the schema-drift gate (`resolvePhaseDirByToken`), `detectVerifyFailed`. Each read a directory to
|
|
1346
1346
|
* DECORATE a row they are already emitting; each used `.find()` before this
|
|
1347
1347
|
* PR, so first-match is their prior behavior preserved verbatim, and each is
|
|
1348
1348
|
* order-stable because the directory list is sorted and this function filters
|
|
@@ -31,6 +31,9 @@ const markdown_table_cjs_1 = require("./markdown-table.cjs");
|
|
|
31
31
|
// eslint-disable-next-line @typescript-eslint/no-require-imports -- phase-id.cjs is an export= CommonJS module
|
|
32
32
|
const phaseIdMod = require("./phase-id.cjs");
|
|
33
33
|
const { isSentinelPhaseId } = phaseIdMod;
|
|
34
|
+
// #5060: `phase-status.cjs` is a load-time leaf (no top-level requires), safe
|
|
35
|
+
// to import here for the ROADMAP Status-cell reader.
|
|
36
|
+
const phase_status_cjs_1 = require("./phase-status.cjs");
|
|
34
37
|
/**
|
|
35
38
|
* #3227: the single owner of "where is this ROADMAP's Progress table".
|
|
36
39
|
* Lifted verbatim out of deriveProgressFromRoadmap so `state-contract.cts`
|
|
@@ -93,8 +96,6 @@ function deriveProgressFromRoadmap(roadmapContent) {
|
|
|
93
96
|
const table = locateProgressTable(roadmapContent);
|
|
94
97
|
if (table) {
|
|
95
98
|
const allRows = table.rows;
|
|
96
|
-
const completed = allRows.filter((r) => /^complete$/i.test((r['Status'] ?? '').trim())).length;
|
|
97
|
-
completedPhases = completed > 0 ? completed : null;
|
|
98
99
|
// Data rows only (exclude sentinel phases 0 and 999.x).
|
|
99
100
|
// #3185: canonical sentinel predicate (SENTINEL_RANGES [0,999]) — this was a local 999-only literal that admitted Phase 0.
|
|
100
101
|
const dataRows = allRows.filter((r) => {
|
|
@@ -102,6 +103,12 @@ function deriveProgressFromRoadmap(roadmapContent) {
|
|
|
102
103
|
return /^\d/.test(phase) && !isSentinelPhaseId(phase);
|
|
103
104
|
});
|
|
104
105
|
totalPhases = dataRows.length > 0 ? dataRows.length : null;
|
|
106
|
+
// #5060: routed through the Phase Status Module's `parseRoadmapStatusCell`
|
|
107
|
+
// owner (rather than a local `/^complete$/i` regex) and counted over
|
|
108
|
+
// `dataRows` (not `allRows`) — a sentinel row's Status cell must never
|
|
109
|
+
// contribute to `completedPhases`.
|
|
110
|
+
const completed = dataRows.filter((r) => (0, phase_status_cjs_1.parseRoadmapStatusCell)(r['Status']) === phase_status_cjs_1.WIRE_STATUS.COMPLETE).length;
|
|
111
|
+
completedPhases = completed > 0 ? completed : null;
|
|
105
112
|
let totalPlansSum = 0;
|
|
106
113
|
for (const r of allRows) {
|
|
107
114
|
const cell = (r['Plans Complete'] ?? '').trim();
|