@opengsd/gsd-core 1.15.0 → 1.16.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 -1
- package/.opencode/plugins/gsd-core.js +85 -5
- 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 +1 -1
- package/agents/gsd-debug-session-manager.md +1 -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/install.js +126 -196
- 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 +142 -56
- package/gsd-core/bin/lib/active-workstream-store.cjs +15 -0
- 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 +183 -103
- package/gsd-core/bin/lib/capability-validator.cjs +16 -0
- 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 +173 -170
- 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 +141 -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 +80 -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/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 +162 -96
- package/gsd-core/bin/lib/install-engine.cjs +19 -35
- package/gsd-core/bin/lib/install-profiles.cjs +7 -4
- package/gsd-core/bin/lib/io.cjs +122 -3
- 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/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/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 +280 -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 +11 -8
- 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 +91 -157
- 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 +307 -591
- package/gsd-core/bin/lib/runtime-name-policy.cjs +142 -27
- package/gsd-core/bin/lib/runtime-slash.cjs +47 -30
- package/gsd-core/bin/lib/shell-command-projection.cjs +37 -2
- package/gsd-core/bin/lib/smart-entry.cjs +19 -3
- 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.cjs +214 -105
- 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/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 +1165 -314
- 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 +84 -90
- 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 +1 -1
- 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 +78 -170
- 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 +3 -2
- 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 +8 -0
- package/hooks/dist/gsd-context-monitor.js +31 -8
- package/hooks/dist/gsd-cursor-subagent-start.js +8 -0
- package/hooks/dist/gsd-secret-read-guard.js +147 -16
- package/hooks/dist/gsd-statusline.js +15 -7
- 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 +8 -0
- package/hooks/gsd-context-monitor.js +31 -8
- package/hooks/gsd-cursor-subagent-start.js +8 -0
- package/hooks/gsd-secret-read-guard.js +147 -16
- package/hooks/gsd-statusline.js +15 -7
- 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-features.cjs +13 -8
- package/scripts/gen-hooks-cli-exit.cjs +12 -28
- package/scripts/gen-loop-host-contract.cjs +10 -1
- 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 +20 -2
- package/scripts/lib/ndjson-reporter.cjs +28 -3
- package/scripts/lib/platform-conformance-tier.generated.cjs +34 -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 +50 -5
- 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
|
@@ -14,6 +14,7 @@ const node_path_1 = __importDefault(require("node:path"));
|
|
|
14
14
|
const text_lines_cjs_1 = require("./text-lines.cjs");
|
|
15
15
|
const shell_command_projection_cjs_1 = require("./shell-command-projection.cjs");
|
|
16
16
|
const pattern_cjs_1 = require("./pattern.cjs");
|
|
17
|
+
const frontmatter_fence_cjs_1 = require("./frontmatter-fence.cjs");
|
|
17
18
|
const security_cjs_1 = require("./security.cjs");
|
|
18
19
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
19
20
|
const ioMod = require("./io.cjs");
|
|
@@ -62,7 +63,7 @@ const planningWorkspace = require("./planning-workspace.cjs");
|
|
|
62
63
|
const { planningDir, planningPaths, todosDir, resolvePhaseIdConvention } = planningWorkspace;
|
|
63
64
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
64
65
|
const frontmatter = require("./frontmatter.cjs");
|
|
65
|
-
const { extractFrontmatter, agentScalarNeedsDoubleQuoting, escapeDoubleQuotedScalar } = frontmatter;
|
|
66
|
+
const { extractFrontmatter, agentScalarNeedsDoubleQuoting, escapeDoubleQuotedScalar, FRONTMATTER_UNPARSEABLE } = frontmatter;
|
|
66
67
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
67
68
|
const modelProfiles = require("./model-profiles.cjs");
|
|
68
69
|
const { MODEL_PROFILES, VALID_PHASE_TYPES } = modelProfiles;
|
|
@@ -72,9 +73,40 @@ const phase_lifecycle_cjs_1 = require("./phase-lifecycle.cjs");
|
|
|
72
73
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
73
74
|
const planScanMod = require("./plan-scan.cjs");
|
|
74
75
|
const { scanPhasePlans } = planScanMod;
|
|
75
|
-
//
|
|
76
|
+
// #5060: Phase Status Module — the single owner of "what state is phase P
|
|
77
|
+
// in?" (ADR-5057 §1/§2). Replaces this file's own precedence ladder and
|
|
78
|
+
// determinePhaseStatus derivation.
|
|
79
|
+
const phase_status_cjs_1 = require("./phase-status.cjs");
|
|
80
|
+
// #5118: an aggregate over a refused verification report fails through the owner.
|
|
81
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
76
82
|
const verificationMod = require("./verification.cjs");
|
|
77
|
-
const {
|
|
83
|
+
const { failOnVerificationStatusError, firstStatusError } = verificationMod;
|
|
84
|
+
/** The effort-sync line editors' fence reading: a block behind a preamble is still edited (#3706). */
|
|
85
|
+
const EFFORT_SYNC_FENCE = Object.freeze({ allowPreamble: true });
|
|
86
|
+
/** Prefer the Claude agents beside the running local install over the global home. */
|
|
87
|
+
function claudeEffortAgentsDir(explicitDir) {
|
|
88
|
+
if (explicitDir)
|
|
89
|
+
return node_path_1.default.join(explicitDir, 'agents');
|
|
90
|
+
// Compiled commands.cjs lives at <configDir>/gsd-core/bin/lib. In a source
|
|
91
|
+
// checkout the same path leads to the repository root, where no install
|
|
92
|
+
// manifest exists; keep the existing global fallback in that case.
|
|
93
|
+
const configDir = node_path_1.default.resolve(__dirname, '..', '..', '..');
|
|
94
|
+
try {
|
|
95
|
+
const manifestPath = node_path_1.default.join(configDir, 'gsd-file-manifest.json');
|
|
96
|
+
if (node_fs_1.default.lstatSync(manifestPath).isFile()) {
|
|
97
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
98
|
+
const migrations = require('./installer-migrations.cjs');
|
|
99
|
+
const manifest = migrations.readInstallManifest(configDir);
|
|
100
|
+
if (manifest.runtime === 'claude' && manifest.scope === 'local') {
|
|
101
|
+
return node_path_1.default.join(configDir, 'agents');
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
catch { /* an absent or unreadable manifest retains the global fallback */ }
|
|
106
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/unbound-method
|
|
107
|
+
const { getGlobalConfigDir } = require('./runtime-homes.cjs');
|
|
108
|
+
return node_path_1.default.join(getGlobalConfigDir('claude'), 'agents');
|
|
109
|
+
}
|
|
78
110
|
/**
|
|
79
111
|
* Project one canonical bracket directory onto its display identity and slug.
|
|
80
112
|
*
|
|
@@ -101,94 +133,6 @@ function recoverBracketPhaseName(dir, phaseToken) {
|
|
|
101
133
|
const afterToken = dir.slice(tokenOffset + tokenBoundary.length).replace(/^-/, '');
|
|
102
134
|
return afterToken ? afterToken.replace(/-/g, ' ') : '';
|
|
103
135
|
}
|
|
104
|
-
// ─── Phase Status ─────────────────────────────────────────────────────────────
|
|
105
|
-
/**
|
|
106
|
-
* Phase-status precedence ladder — furthest-along wins (#2408).
|
|
107
|
-
*
|
|
108
|
-
* `cmdStats` builds `phasesByNumber` by scanning on-disk phase directories.
|
|
109
|
-
* When two directories normalize to the same phase key (e.g. `05-real/` and
|
|
110
|
-
* `05-real-stray/`), the status field must be folded by precedence rather
|
|
111
|
-
* than overwritten last-write-wins — otherwise `/gsd-stats` reports whatever
|
|
112
|
-
* directory `fs.readdirSync` happened to yield last, which is non-deterministic
|
|
113
|
-
* across platforms and can silently call a `Complete` phase `Not Started`.
|
|
114
|
-
*/
|
|
115
|
-
const PHASE_STATUS_PRECEDENCE = [
|
|
116
|
-
'Complete',
|
|
117
|
-
'Needs Review',
|
|
118
|
-
'Executed',
|
|
119
|
-
'In Progress',
|
|
120
|
-
'Planned',
|
|
121
|
-
'Not Started',
|
|
122
|
-
'Pending',
|
|
123
|
-
];
|
|
124
|
-
const PHASE_STATUS_RANK = new Map(PHASE_STATUS_PRECEDENCE.map((s, i) => [s, i]));
|
|
125
|
-
/**
|
|
126
|
-
* Fold two phase statuses by precedence — returns whichever is further along
|
|
127
|
-
* the {@link PHASE_STATUS_PRECEDENCE} ladder. Unrecognized statuses fall behind
|
|
128
|
-
* every recognized one (so a recognized status always wins over an unknown one;
|
|
129
|
-
* two unrecognized statuses favor `a` for determinism).
|
|
130
|
-
*/
|
|
131
|
-
function foldPhaseStatus(a, b) {
|
|
132
|
-
const ra = PHASE_STATUS_RANK.get(a);
|
|
133
|
-
const rb = PHASE_STATUS_RANK.get(b);
|
|
134
|
-
if (ra === undefined && rb === undefined)
|
|
135
|
-
return a;
|
|
136
|
-
if (ra === undefined)
|
|
137
|
-
return b;
|
|
138
|
-
if (rb === undefined)
|
|
139
|
-
return a;
|
|
140
|
-
// Lower rank = higher precedence (Complete=0 wins over Not Started=5).
|
|
141
|
-
return ra <= rb ? a : b;
|
|
142
|
-
}
|
|
143
|
-
/**
|
|
144
|
-
* Determine phase status by checking plan/summary counts AND verification state.
|
|
145
|
-
* Introduces "Executed" for phases with all summaries but no passing verification.
|
|
146
|
-
*/
|
|
147
|
-
function determinePhaseStatus(plans, summaries, phaseDir, defaultPending) {
|
|
148
|
-
if (plans === 0)
|
|
149
|
-
return defaultPending;
|
|
150
|
-
if (summaries < plans && summaries > 0)
|
|
151
|
-
return 'In Progress';
|
|
152
|
-
if (summaries < plans)
|
|
153
|
-
return 'Planned';
|
|
154
|
-
// summaries >= plans — check verification
|
|
155
|
-
try {
|
|
156
|
-
const files = node_fs_1.default.readdirSync(phaseDir);
|
|
157
|
-
// #3473 F2: routed through the shared resolver (readdir order is
|
|
158
|
-
// filesystem-dependent, so the prior hand-rolled `.find()` could pick
|
|
159
|
-
// either file when a phase held both a canonical report and an ad-hoc
|
|
160
|
-
// `-CORRECTION-VERIFICATION.md` worksheet — see #3357).
|
|
161
|
-
// #3492: pin selection to THIS phase's own token so a stray cross-phase
|
|
162
|
-
// or sentinel-numbered canonically-shaped file cannot outrank this
|
|
163
|
-
// phase's own (possibly non-canonical) report.
|
|
164
|
-
const phaseDirName = node_path_1.default.basename(phaseDir);
|
|
165
|
-
const phaseToken = extractPhaseToken(phaseDirName);
|
|
166
|
-
const verificationFile = resolveVerificationFile(files, { allowBare: true, phaseToken, phaseDirName });
|
|
167
|
-
if (verificationFile) {
|
|
168
|
-
const verificationFilePath = node_path_1.default.join(phaseDir, verificationFile);
|
|
169
|
-
const content = (0, shell_command_projection_cjs_1.platformReadSync)(verificationFilePath) || '';
|
|
170
|
-
// #1159 (Defect A): read ONLY the frontmatter `status` key to avoid false
|
|
171
|
-
// matches from historical body metadata such as `previous_status: gaps_found`.
|
|
172
|
-
// Full-text regexes like /status:\s*gaps_found/ match the substring inside
|
|
173
|
-
// `previous_status: gaps_found`, producing incorrect phase status labels.
|
|
174
|
-
const fm = extractFrontmatter(content, verificationFilePath);
|
|
175
|
-
// Normalise to lower-case to preserve the prior case-insensitive behaviour
|
|
176
|
-
// while reading only the frontmatter `status` key (not the full body text).
|
|
177
|
-
const fmStatus = typeof fm['status'] === 'string' ? fm['status'].trim().toLowerCase() : '';
|
|
178
|
-
if (fmStatus === 'passed')
|
|
179
|
-
return 'Complete';
|
|
180
|
-
if (fmStatus === 'human_needed')
|
|
181
|
-
return 'Needs Review';
|
|
182
|
-
if (fmStatus === 'gaps_found')
|
|
183
|
-
return 'Executed';
|
|
184
|
-
// Verification exists but unrecognized status — treat as executed
|
|
185
|
-
return 'Executed';
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
catch { /* directory read failed — fall through */ }
|
|
189
|
-
// No verification file — executed but not verified
|
|
190
|
-
return 'Executed';
|
|
191
|
-
}
|
|
192
136
|
function cmdGenerateSlug(text, raw) {
|
|
193
137
|
if (!text) {
|
|
194
138
|
error('text required for slug generation');
|
|
@@ -602,19 +546,19 @@ function cmdResolveExecution(cwd, agentType, raw, opts) {
|
|
|
602
546
|
let effortEffective = effort;
|
|
603
547
|
if (runtime === 'claude') {
|
|
604
548
|
try {
|
|
605
|
-
|
|
606
|
-
const { getGlobalConfigDir } = require('./runtime-homes.cjs');
|
|
607
|
-
const agentsDirEff = node_path_1.default.join(getGlobalConfigDir(runtime), 'agents');
|
|
549
|
+
const agentsDirEff = claudeEffortAgentsDir();
|
|
608
550
|
// agentType is an unvalidated CLI positional: keep the read inside the
|
|
609
551
|
// agents dir so `../../x` cannot point it elsewhere (defense in depth —
|
|
610
552
|
// the reflected surface is only a frontmatter effort line). Untrusted
|
|
611
553
|
// input feeding a real read → realpath family (ADR-4650 decision 6).
|
|
612
554
|
const agentPath = (0, security_cjs_1.assertWithinRoot)(`${agentType}.md`, agentsDirEff, 'agent file');
|
|
613
555
|
const agentContent = node_fs_1.default.readFileSync(agentPath, 'utf8');
|
|
614
|
-
//
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
556
|
+
// Byte-0 read: the runtime only honors a frontmatter block that opens at
|
|
557
|
+
// byte 0, so a block behind a preamble is not frontmatter to the loader
|
|
558
|
+
// and this reports what the installed agent will actually run at (#5105).
|
|
559
|
+
const fmSpanEff = agentFrontmatterSpan(agentContent);
|
|
560
|
+
if (fmSpanEff) {
|
|
561
|
+
const effortLine = /^effort:[ \t]*(.+?)[ \t]*$/m.exec(fmSpanEff.body);
|
|
618
562
|
if (effortLine) {
|
|
619
563
|
effortEffective = effortLine[1];
|
|
620
564
|
effortEffectiveSource = 'frontmatter';
|
|
@@ -701,33 +645,42 @@ function effortSurfaceForHost(cwd, host) {
|
|
|
701
645
|
return 'none';
|
|
702
646
|
}
|
|
703
647
|
}
|
|
648
|
+
/**
|
|
649
|
+
* The leading frontmatter block of an installed agent file, as the one fence owner
|
|
650
|
+
* (`locateFrontmatterFence`) finds it: `body` is the text between the two fences (the last
|
|
651
|
+
* content line's line ending included), `bodyStart`/`closingStart` bound it, and `eol` is the
|
|
652
|
+
* opening fence's line ending. Null when the file has no closed block. By default a block that
|
|
653
|
+
* does not open at byte 0 is not one, exactly as the runtime that loads the agent reads it;
|
|
654
|
+
* the effort-sync line editors pass `EFFORT_SYNC_FENCE` to edit a block behind a preamble too
|
|
655
|
+
* (#3706 pinned that).
|
|
656
|
+
*/
|
|
657
|
+
function agentFrontmatterSpan(content, options) {
|
|
658
|
+
const fence = (0, frontmatter_fence_cjs_1.locateFrontmatterFence)(content, options);
|
|
659
|
+
if (!fence?.closed)
|
|
660
|
+
return null;
|
|
661
|
+
return { body: content.slice(fence.openEnd, fence.closingStart), bodyStart: fence.openEnd, closingStart: fence.closingStart, eol: fence.eol };
|
|
662
|
+
}
|
|
704
663
|
/**
|
|
705
664
|
* #488 — Replace or inject the `<key>:` value in YAML frontmatter.
|
|
706
665
|
* Unlike injectEffortFrontmatter (install.js), this overwrites an existing value.
|
|
707
666
|
* #3706: key-parameterised so the same line-editor serves both claude's
|
|
708
|
-
* `effort:` and OpenCode's `variant:`. #3706: all offsets (eol,
|
|
709
|
-
* closingStart)
|
|
710
|
-
*
|
|
667
|
+
* `effort:` and OpenCode's `variant:`. #3706: all offsets (eol, bodyStart,
|
|
668
|
+
* closingStart) come from the block `agentFrontmatterSpan` found, and the
|
|
669
|
+
* existing-key replace is scoped to the frontmatter span only.
|
|
711
670
|
*/
|
|
712
671
|
function setFrontmatterKeyLine(content, key, value) {
|
|
713
|
-
const
|
|
714
|
-
|
|
715
|
-
if (!match)
|
|
672
|
+
const span = agentFrontmatterSpan(content, EFFORT_SYNC_FENCE);
|
|
673
|
+
if (!span)
|
|
716
674
|
return content;
|
|
717
|
-
const fmBody =
|
|
675
|
+
const fmBody = span.body;
|
|
718
676
|
// Both writers of these frontmatter keys — this sync path and the
|
|
719
677
|
// install-side `frontmatterScalar` in runtime-artifact-conversion.cts —
|
|
720
678
|
// now share one escaping rule: quote via `agentScalarNeedsDoubleQuoting` +
|
|
721
679
|
// `escapeDoubleQuotedScalar` (both from frontmatter.cts) rather than each
|
|
722
680
|
// interpolating `value` raw/differently.
|
|
723
681
|
const renderedValue = agentScalarNeedsDoubleQuoting(value) ? `"${escapeDoubleQuotedScalar(value)}"` : value;
|
|
724
|
-
//
|
|
725
|
-
|
|
726
|
-
// offset below by one byte and mangles the opening fence.
|
|
727
|
-
const eol = /^---\r\n/.test(match[0]) ? '\r\n' : '\n';
|
|
728
|
-
const openLen = 3 + eol.length;
|
|
729
|
-
const bodyStart = match.index + openLen;
|
|
730
|
-
const closingStart = bodyStart + fmBody.length;
|
|
682
|
+
// Every offset and the EOL come from the block the fence owner found (#3706).
|
|
683
|
+
const { eol, bodyStart, closingStart } = span;
|
|
731
684
|
// #3706: key is now generic (not just the literal 'effort'/'variant'
|
|
732
685
|
// callers happen to pass today) — escape it before interpolating into the
|
|
733
686
|
// RegExp so a future caller can't have its key metacharacters reinterpreted.
|
|
@@ -763,19 +716,17 @@ function setFrontmatterKeyLine(content, key, value) {
|
|
|
763
716
|
* codex-agent-toml strip discipline: targeted line removal, EOL-aware, every
|
|
764
717
|
* other byte (comments, sibling keys, the body) untouched.
|
|
765
718
|
* #3706: key-parameterised so the same line-editor serves both claude's
|
|
766
|
-
* `effort:` and OpenCode's `variant:`. #3706:
|
|
767
|
-
*
|
|
768
|
-
* would otherwise misalign every offset below.
|
|
719
|
+
* `effort:` and OpenCode's `variant:`. #3706: every offset comes from the
|
|
720
|
+
* block `agentFrontmatterSpan` found.
|
|
769
721
|
*/
|
|
770
722
|
function removeFrontmatterKeyLine(content, key) {
|
|
771
723
|
// Scoped to the FIRST frontmatter block (not a whole-file /m match): a
|
|
772
724
|
// preamble or body line starting with `<key>:` (a fenced config example,
|
|
773
725
|
// a thematic-break flanked fragment) must never be the line removed.
|
|
774
|
-
const
|
|
775
|
-
|
|
776
|
-
if (!match)
|
|
726
|
+
const span = agentFrontmatterSpan(content, EFFORT_SYNC_FENCE);
|
|
727
|
+
if (!span)
|
|
777
728
|
return content;
|
|
778
|
-
const fmBody =
|
|
729
|
+
const fmBody = span.body;
|
|
779
730
|
// #3706: same generic-key escape as setFrontmatterKeyLine above.
|
|
780
731
|
const lineRe = new RegExp(`^${(0, pattern_cjs_1.escapeRegex)(key)}:[ \\t]*.*\\r?\\n?`, 'm');
|
|
781
732
|
if (!lineRe.test(fmBody))
|
|
@@ -790,12 +741,7 @@ function removeFrontmatterKeyLine(content, key) {
|
|
|
790
741
|
// is removed in one pass.
|
|
791
742
|
const stripAllRe = new RegExp(`^${(0, pattern_cjs_1.escapeRegex)(key)}:[ \\t]*.*\\r?\\n?`, 'gm');
|
|
792
743
|
const strippedFm = fmBody.replace(stripAllRe, '');
|
|
793
|
-
|
|
794
|
-
// block, not the start of the file, or a preambled CRLF document misaligns.
|
|
795
|
-
const eol = /^---\r\n/.test(match[0]) ? '\r\n' : '\n';
|
|
796
|
-
const openLen = 3 + eol.length;
|
|
797
|
-
const closingStart = match.index + openLen + fmBody.length;
|
|
798
|
-
return content.slice(0, match.index + openLen) + strippedFm + content.slice(closingStart);
|
|
744
|
+
return content.slice(0, span.bodyStart) + strippedFm + content.slice(span.closingStart);
|
|
799
745
|
}
|
|
800
746
|
/** #488 — Replace or inject the `effort:` value in YAML frontmatter. */
|
|
801
747
|
function setEffortFrontmatter(content, effortValue) {
|
|
@@ -840,8 +786,6 @@ function cmdEffortSync(cwd, raw, opts) {
|
|
|
840
786
|
output({ synced: 0, skipped: 0, changes: [], dry_run: dryRun, reason: `runtime '${runtime}' does not use effort: frontmatter` }, raw, '');
|
|
841
787
|
return;
|
|
842
788
|
}
|
|
843
|
-
// eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/unbound-method
|
|
844
|
-
const { getGlobalConfigDir } = require('./runtime-homes.cjs');
|
|
845
789
|
// Use install-time resolvers: they merge ~/.gsd/defaults.json with project config,
|
|
846
790
|
// matching the exact logic used when agents were originally installed. #2071: these
|
|
847
791
|
// live in the shipped sibling install-effort-resolver.cjs (extracted from the
|
|
@@ -849,7 +793,7 @@ function cmdEffortSync(cwd, raw, opts) {
|
|
|
849
793
|
// eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/unbound-method
|
|
850
794
|
const { readGsdEffectiveEffortConfig, resolveInstallTimeEffort } = require('./install-effort-resolver.cjs');
|
|
851
795
|
const effortCfg = readGsdEffectiveEffortConfig(cwd);
|
|
852
|
-
const agentsDir =
|
|
796
|
+
const agentsDir = claudeEffortAgentsDir(opts.configDir);
|
|
853
797
|
if (!node_fs_1.default.existsSync(agentsDir)) {
|
|
854
798
|
output({ synced: 0, skipped: 0, changes: [], dry_run: dryRun, agents_dir: agentsDir, reason: 'agents directory not found' }, raw, '');
|
|
855
799
|
return;
|
|
@@ -867,6 +811,10 @@ function cmdEffortSync(cwd, raw, opts) {
|
|
|
867
811
|
}).sort(); // #3706: sorted like the codex and
|
|
868
812
|
// opencode branches — readdir order is platform-dependent, so leaving it unsorted makes the
|
|
869
813
|
// reported `changes` ordering differ across machines for identical inputs.
|
|
814
|
+
if (files.length === 0) {
|
|
815
|
+
output({ synced: 0, skipped: 0, changes: [], dry_run: dryRun, agents_dir: agentsDir, reason: 'no GSD agent files found' }, raw, '');
|
|
816
|
+
return;
|
|
817
|
+
}
|
|
870
818
|
const changes = [];
|
|
871
819
|
let synced = 0;
|
|
872
820
|
let skipped = 0;
|
|
@@ -911,7 +859,7 @@ function cmdEffortSync(cwd, raw, opts) {
|
|
|
911
859
|
// drift and the sync re-added a hand-stripped key on every apply. A
|
|
912
860
|
// present key under inherit is stripped, reported as {from, to: null}.
|
|
913
861
|
if (universalEffort === 'inherit') {
|
|
914
|
-
const fmMatchInherit =
|
|
862
|
+
const fmMatchInherit = agentFrontmatterSpan(content, EFFORT_SYNC_FENCE);
|
|
915
863
|
if (!fmMatchInherit) {
|
|
916
864
|
skipped++;
|
|
917
865
|
continue;
|
|
@@ -922,12 +870,12 @@ function cmdEffortSync(cwd, raw, opts) {
|
|
|
922
870
|
// from a null value alone is wrong here — it would leave an
|
|
923
871
|
// unresolvable `effort: null` key on disk forever. Test presence with
|
|
924
872
|
// its own regex, and only compare values once presence is known.
|
|
925
|
-
const effortPresentInherit = /^effort:/m.test(fmMatchInherit
|
|
873
|
+
const effortPresentInherit = /^effort:/m.test(fmMatchInherit.body);
|
|
926
874
|
if (!effortPresentInherit) {
|
|
927
875
|
skipped++;
|
|
928
876
|
continue;
|
|
929
877
|
}
|
|
930
|
-
const effortMatchInherit = /^effort:[ \t]*(.+?)[ \t]*$/m.exec(fmMatchInherit
|
|
878
|
+
const effortMatchInherit = /^effort:[ \t]*(.+?)[ \t]*$/m.exec(fmMatchInherit.body);
|
|
931
879
|
// `effortPresentInherit` is guaranteed true here (checked above), so a
|
|
932
880
|
// failed value match means the key is present with an EMPTY value —
|
|
933
881
|
// report `''`, not `null`, so "present-but-empty" is never conflated
|
|
@@ -986,7 +934,7 @@ function cmdEffortSync(cwd, raw, opts) {
|
|
|
986
934
|
// codex's 'ultra' rejection can produce a null value).
|
|
987
935
|
const rendered = (0, model_catalog_cjs_1.renderEffortForRuntime)(runtime, universalEffort);
|
|
988
936
|
const newEffortValue = rendered.value;
|
|
989
|
-
const fmMatch =
|
|
937
|
+
const fmMatch = agentFrontmatterSpan(content, EFFORT_SYNC_FENCE);
|
|
990
938
|
if (!fmMatch) {
|
|
991
939
|
skipped++;
|
|
992
940
|
continue;
|
|
@@ -999,8 +947,8 @@ function cmdEffortSync(cwd, raw, opts) {
|
|
|
999
947
|
// never null on this path (guarded above), so an absent key already
|
|
1000
948
|
// yields `currentEffort === null !== newEffortValue` without consulting
|
|
1001
949
|
// presence separately.
|
|
1002
|
-
const effortPresent = /^effort:/m.test(fmMatch
|
|
1003
|
-
const effortMatch = /^effort:[ \t]*(.+?)[ \t]*$/m.exec(fmMatch
|
|
950
|
+
const effortPresent = /^effort:/m.test(fmMatch.body);
|
|
951
|
+
const effortMatch = /^effort:[ \t]*(.+?)[ \t]*$/m.exec(fmMatch.body);
|
|
1004
952
|
// `null` (key absent) and `''` (key present, value empty) are distinct
|
|
1005
953
|
// states `effortPresent` deliberately disambiguates — collapsing both to
|
|
1006
954
|
// `null` here would make the reported `from` lie about which case fired.
|
|
@@ -1273,7 +1221,7 @@ function cmdEffortSyncOpencode(cwd, raw, dryRun, configDir) {
|
|
|
1273
1221
|
// exactly what install writes.
|
|
1274
1222
|
const universal = effortCfg ? resolveInstallTimeEffort(effortCfg, agentName) : null;
|
|
1275
1223
|
const target = universal ? clampEffortForHost('opencode', universal) : null;
|
|
1276
|
-
const fmMatch =
|
|
1224
|
+
const fmMatch = agentFrontmatterSpan(content, EFFORT_SYNC_FENCE);
|
|
1277
1225
|
if (!fmMatch) {
|
|
1278
1226
|
skipped++;
|
|
1279
1227
|
continue;
|
|
@@ -1285,8 +1233,8 @@ function cmdEffortSyncOpencode(cwd, raw, dryRun, configDir) {
|
|
|
1285
1233
|
// null — it would leave an unresolvable `variant: null` key on disk
|
|
1286
1234
|
// forever. Test presence with its own regex, and only compare values
|
|
1287
1235
|
// once presence is known.
|
|
1288
|
-
const variantPresent = /^variant:/m.test(fmMatch
|
|
1289
|
-
const variantMatch = /^variant:[ \t]*(.+?)[ \t]*$/m.exec(fmMatch
|
|
1236
|
+
const variantPresent = /^variant:/m.test(fmMatch.body);
|
|
1237
|
+
const variantMatch = /^variant:[ \t]*(.+?)[ \t]*$/m.exec(fmMatch.body);
|
|
1290
1238
|
// `null` (key absent) and `''` (key present, value empty) are distinct
|
|
1291
1239
|
// states this code deliberately tracks via `variantPresent` above — a
|
|
1292
1240
|
// reported `from` that collapses both to `null` would make "no key" and
|
|
@@ -2883,13 +2831,38 @@ function cmdSummaryExtract(cwd, summaryPath, fields, raw) {
|
|
|
2883
2831
|
if (!summaryPath) {
|
|
2884
2832
|
error('summary-path required for summary-extract');
|
|
2885
2833
|
}
|
|
2886
|
-
|
|
2834
|
+
// #5013: requireSafePath (not path.join) so an absolute summaryPath resolves to
|
|
2835
|
+
// that literal path instead of being concatenated onto cwd — path.join has no
|
|
2836
|
+
// special case for an absolute second argument the way path.resolve does. Uses
|
|
2837
|
+
// requireSafePath (the same PathAcceptance.AbsoluteInsideRoot pattern
|
|
2838
|
+
// cmdAuditAcknowledge's write-side counterpart already uses in audit.cts)
|
|
2839
|
+
// rather than a bare path.resolve: a plain path.resolve would also have made
|
|
2840
|
+
// an absolute summaryPath anywhere on the filesystem resolve and have its
|
|
2841
|
+
// content echoed back (this command reads and returns file content, unlike
|
|
2842
|
+
// cmdVerifyPathExists's exists-only check) — an orthogonal-review finding on
|
|
2843
|
+
// this same fix. requireSafePath keeps the acceptance criterion (an absolute
|
|
2844
|
+
// path within the project resolves correctly) while refusing one that
|
|
2845
|
+
// escapes cwd, and rejects relative `../` escapes the same way (pre-existing
|
|
2846
|
+
// in this function, closed as a direct consequence, not separately in scope).
|
|
2847
|
+
const fullPath = (0, security_cjs_1.requireSafePath)(summaryPath, cwd, 'summary-extract target', security_cjs_1.PathAcceptance.AbsoluteInsideRoot);
|
|
2887
2848
|
if (!node_fs_1.default.existsSync(fullPath)) {
|
|
2888
2849
|
output({ error: 'File not found', path: summaryPath }, raw, undefined);
|
|
2889
2850
|
return;
|
|
2890
2851
|
}
|
|
2891
2852
|
const content = node_fs_1.default.readFileSync(fullPath, 'utf-8');
|
|
2892
2853
|
const fm = extractFrontmatter(content, fullPath);
|
|
2854
|
+
// #5013: an unparseable frontmatter block (malformed YAML in the fence) is
|
|
2855
|
+
// NOT the same as an absent/empty one — extractFrontmatter marks it with
|
|
2856
|
+
// FRONTMATTER_UNPARSEABLE (mirrors state.cts's isUnparseableFrontmatter /
|
|
2857
|
+
// state-transition.cts's identical helper). Left unchecked, every field
|
|
2858
|
+
// below silently falls back to its empty default and this command exits 0,
|
|
2859
|
+
// so a milestone audit reading requirements_completed through it would
|
|
2860
|
+
// report "zero requirements completed" for a plan that completed several,
|
|
2861
|
+
// with nothing on stderr. Refuse instead, naming the file, the same way
|
|
2862
|
+
// cmdAuditAcknowledge (audit.cts) refuses on the write-side counterpart.
|
|
2863
|
+
if (fm[FRONTMATTER_UNPARSEABLE] === true) {
|
|
2864
|
+
error(`summary-extract: the frontmatter of "${summaryPath}" is not parseable YAML — fix the syntax error in the file (e.g. an unquoted ": " inside a block-sequence item) before re-running; every field would otherwise silently read as empty`, ERROR_REASON.SUMMARY_EXTRACT_UNPARSEABLE, { path: summaryPath });
|
|
2865
|
+
}
|
|
2893
2866
|
// Parse key-decisions into structured format
|
|
2894
2867
|
const parseDecisions = (decisionsList) => {
|
|
2895
2868
|
if (!decisionsList || !Array.isArray(decisionsList))
|
|
@@ -3060,6 +3033,8 @@ function cmdProgressRender(cwd, format, raw) {
|
|
|
3060
3033
|
let totalPlans = 0;
|
|
3061
3034
|
let totalSummaries = 0;
|
|
3062
3035
|
let phaseScope = null;
|
|
3036
|
+
// #5118: carried out of the scan below and failed on after it.
|
|
3037
|
+
let statusError;
|
|
3063
3038
|
try {
|
|
3064
3039
|
// #3185 (ADR-3180 Decision 1): the single owner applies the milestone
|
|
3065
3040
|
// window AND the sentinel filter and returns dirs already sorted by
|
|
@@ -3096,14 +3071,16 @@ function cmdProgressRender(cwd, format, raw) {
|
|
|
3096
3071
|
phaseNum = dm ? dm[1] : dir;
|
|
3097
3072
|
phaseName = dm && dm[2] ? dm[2].replace(/-/g, ' ') : '';
|
|
3098
3073
|
}
|
|
3099
|
-
// #3183: canonical plan/summary counts
|
|
3100
|
-
// canonical pairing) from the
|
|
3101
|
-
|
|
3102
|
-
const
|
|
3103
|
-
|
|
3074
|
+
// #3183/#5060: canonical plan/summary counts and lifecycle status
|
|
3075
|
+
// (root+nested, superseded-excluded, canonical pairing) from the
|
|
3076
|
+
// single owner (Phase Status Module, ADR-5057).
|
|
3077
|
+
const ps = (0, phase_status_cjs_1.phaseStatus)(node_path_1.default.join(phasesDir, dir), { convention: phaseIdConvention });
|
|
3078
|
+
statusError = firstStatusError(statusError, ps.value.statusError);
|
|
3079
|
+
const plans = ps.value.planCount;
|
|
3080
|
+
const summaries = ps.value.summaryCount;
|
|
3104
3081
|
totalPlans += plans;
|
|
3105
3082
|
totalSummaries += summaries;
|
|
3106
|
-
const status =
|
|
3083
|
+
const status = (0, phase_status_cjs_1.toDisplayLabel)(ps.value.status, { pendingWord: 'Pending' });
|
|
3107
3084
|
phases.push({
|
|
3108
3085
|
number: phaseNum,
|
|
3109
3086
|
...(displayId ? { display_id: displayId } : {}),
|
|
@@ -3115,6 +3092,9 @@ function cmdProgressRender(cwd, format, raw) {
|
|
|
3115
3092
|
}
|
|
3116
3093
|
}
|
|
3117
3094
|
catch { /* intentionally empty */ }
|
|
3095
|
+
// #5118: a read-only aggregate over a refused report prints nothing.
|
|
3096
|
+
if (statusError)
|
|
3097
|
+
failOnVerificationStatusError(statusError);
|
|
3118
3098
|
// #3217 (ADR-3180 §7.6 rule 4): `phaseScope` was already computed above
|
|
3119
3099
|
// (Phase 3, #3222) but never consulted before rendering — a percentage was
|
|
3120
3100
|
// rendered from counts the scope said were not answers (TRUNCATED /
|
|
@@ -3273,29 +3253,35 @@ function cmdTodoMatchPhase(cwd, phase, raw) {
|
|
|
3273
3253
|
// #4096: upsert completion keys INSIDE the leading frontmatter block. Never a
|
|
3274
3254
|
// bare prefix line above the opening `---` (that displaces the fence to line 2
|
|
3275
3255
|
// and breaks every fence-locating reader). A file with no well-formed block
|
|
3276
|
-
// (absent, or an unterminated opening fence) gains a complete block.
|
|
3256
|
+
// (absent, or an unterminated opening fence) gains a complete block. The block is
|
|
3257
|
+
// the one the one fence owner (`locateFrontmatterFence`) finds, so this writer and
|
|
3258
|
+
// every todo reader agree on where it is.
|
|
3277
3259
|
function upsertTodoCompletionFields(content, today) {
|
|
3278
|
-
const lines = content.split('\n');
|
|
3279
3260
|
const fields = [`completed: ${today}`, 'status: completed'];
|
|
3280
|
-
const
|
|
3281
|
-
|
|
3282
|
-
if (!hasOpeningFence || closeIdx === -1) {
|
|
3261
|
+
const fence = (0, frontmatter_fence_cjs_1.locateFrontmatterFence)(content);
|
|
3262
|
+
if (!fence?.closed) {
|
|
3283
3263
|
// No parseable frontmatter: wrap the whole content in a complete block
|
|
3284
|
-
// rather than prefixing bare keys (#4096 fix 2).
|
|
3285
|
-
|
|
3286
|
-
|
|
3287
|
-
|
|
3264
|
+
// rather than prefixing bare keys (#4096 fix 2). A leading BOM (#2977) stays
|
|
3265
|
+
// the document's first character, ahead of the new fence.
|
|
3266
|
+
const bom = content.charCodeAt(0) === 0xFEFF ? content.slice(0, 1) : '';
|
|
3267
|
+
return `${bom}---\n${fields.join('\n')}\n---\n\n${content.slice(bom.length)}`;
|
|
3268
|
+
}
|
|
3269
|
+
// The block's lines, split on a bare '\n' so a CRLF line keeps its '\r' — and a
|
|
3270
|
+
// written field line carries one too, so a CRLF block never gains LF-only lines.
|
|
3271
|
+
const inner = content.slice(fence.openEnd, fence.closingStart);
|
|
3272
|
+
const block = inner === '' ? [] : inner.slice(0, -1).split('\n');
|
|
3273
|
+
const cr = fence.eol === '\r\n' ? '\r' : '';
|
|
3288
3274
|
for (const field of fields) {
|
|
3289
3275
|
const key = `${field.slice(0, field.indexOf(':'))}:`;
|
|
3290
3276
|
const idx = block.findIndex(l => l.startsWith(key));
|
|
3291
3277
|
if (idx === -1) {
|
|
3292
|
-
block.push(field);
|
|
3278
|
+
block.push(field + cr);
|
|
3293
3279
|
}
|
|
3294
3280
|
else {
|
|
3295
|
-
block[idx] = field;
|
|
3281
|
+
block[idx] = field + cr;
|
|
3296
3282
|
}
|
|
3297
3283
|
}
|
|
3298
|
-
return
|
|
3284
|
+
return `${content.slice(0, fence.openEnd)}${block.join('\n')}\n${content.slice(fence.closingStart)}`;
|
|
3299
3285
|
}
|
|
3300
3286
|
function cmdTodoComplete(cwd, filename, options, raw) {
|
|
3301
3287
|
if (!filename) {
|
|
@@ -3313,7 +3299,7 @@ function cmdTodoComplete(cwd, filename, options, raw) {
|
|
|
3313
3299
|
// to a location inside todosRoot (or inside pending/) and would pass
|
|
3314
3300
|
// containment, yet none of them is a bare filename. Reject on basename
|
|
3315
3301
|
// shape FIRST, before any path is even joined — same predicate shape as
|
|
3316
|
-
// findPhaseArtifact in
|
|
3302
|
+
// findPhaseArtifact in gate-predicate.cts. Checking both `/` and
|
|
3317
3303
|
// `\` explicitly (not just path.basename) matters on POSIX, where a
|
|
3318
3304
|
// literal backslash is just an ordinary filename character to
|
|
3319
3305
|
// path.basename but not to path.win32.basename or to the user's intent.
|
|
@@ -3448,6 +3434,9 @@ function cmdStats(cwd, format, raw) {
|
|
|
3448
3434
|
: null;
|
|
3449
3435
|
const milestoneVersion = milestoneDisplay ?? milestone?.version ?? null;
|
|
3450
3436
|
// Phase & plan stats (reuse progress pattern)
|
|
3437
|
+
// #5060: `phaseStatus` here is the internal PHASE_STATUS ladder value, never
|
|
3438
|
+
// a display label — the output projection below is the only place a label
|
|
3439
|
+
// is produced (toDisplayLabel).
|
|
3451
3440
|
const phasesByNumber = new Map();
|
|
3452
3441
|
let totalPlans = 0;
|
|
3453
3442
|
let totalSummaries = 0;
|
|
@@ -3505,11 +3494,14 @@ function cmdStats(cwd, format, raw) {
|
|
|
3505
3494
|
name: phaseName.replace(/\(INSERTED\)/i, '').trim(),
|
|
3506
3495
|
plans: 0,
|
|
3507
3496
|
summaries: 0,
|
|
3508
|
-
|
|
3497
|
+
phaseStatus: phase_status_cjs_1.PHASE_STATUS.NOT_STARTED,
|
|
3509
3498
|
});
|
|
3510
3499
|
}
|
|
3511
3500
|
}
|
|
3512
3501
|
catch { /* intentionally empty */ }
|
|
3502
|
+
// #5118: the first phase whose verification report `status` is outside the
|
|
3503
|
+
// closed set — carried out of the scan below and failed on after it.
|
|
3504
|
+
let statusError;
|
|
3513
3505
|
try {
|
|
3514
3506
|
// #3185 (ADR-3180 Decision 1): route through the single owner. This
|
|
3515
3507
|
// previously applied the milestone window but NOT a directory-level
|
|
@@ -3546,14 +3538,15 @@ function cmdStats(cwd, format, raw) {
|
|
|
3546
3538
|
const afterToken = dir.slice(phaseToken ? phaseToken.length : 0).replace(/^-/, '');
|
|
3547
3539
|
phaseName = afterToken ? afterToken.replace(/-/g, ' ') : '';
|
|
3548
3540
|
}
|
|
3549
|
-
// #3183: canonical plan/summary counts
|
|
3550
|
-
// canonical pairing) from the
|
|
3551
|
-
|
|
3552
|
-
const
|
|
3553
|
-
|
|
3541
|
+
// #3183/#5060: canonical plan/summary counts and lifecycle status
|
|
3542
|
+
// (root+nested, superseded-excluded, canonical pairing) from the
|
|
3543
|
+
// single owner (Phase Status Module, ADR-5057).
|
|
3544
|
+
const ps = (0, phase_status_cjs_1.phaseStatus)(node_path_1.default.join(phasesDir, dir), { convention: phaseIdConvention });
|
|
3545
|
+
statusError = firstStatusError(statusError, ps.value.statusError);
|
|
3546
|
+
const plans = ps.value.planCount;
|
|
3547
|
+
const summaries = ps.value.summaryCount;
|
|
3554
3548
|
totalPlans += plans;
|
|
3555
3549
|
totalSummaries += summaries;
|
|
3556
|
-
const status = determinePhaseStatus(plans, summaries, node_path_1.default.join(phasesDir, dir), 'Not Started');
|
|
3557
3550
|
const normalizedNum = normalizePhaseName(phaseNum);
|
|
3558
3551
|
const existing = phasesByNumber.get(normalizedNum);
|
|
3559
3552
|
phasesByNumber.set(normalizedNum, {
|
|
@@ -3569,13 +3562,26 @@ function cmdStats(cwd, format, raw) {
|
|
|
3569
3562
|
// platforms, so a naive overwrite can report a Complete phase as Not
|
|
3570
3563
|
// Started (or vice versa) depending on read order. The fold picks the
|
|
3571
3564
|
// furthest-along status, matching what an operator expects.
|
|
3572
|
-
|
|
3565
|
+
phaseStatus: existing ? (0, phase_status_cjs_1.foldPhaseStatuses)(existing.phaseStatus, ps.value.status) : ps.value.status,
|
|
3573
3566
|
});
|
|
3574
3567
|
}
|
|
3575
3568
|
}
|
|
3576
3569
|
catch { /* intentionally empty */ }
|
|
3577
|
-
|
|
3578
|
-
|
|
3570
|
+
// #5118: a read-only aggregate over a refused report prints nothing.
|
|
3571
|
+
if (statusError)
|
|
3572
|
+
failOnVerificationStatusError(statusError);
|
|
3573
|
+
const internalPhases = [...phasesByNumber.values()].sort((a, b) => comparePhaseNum(a.number, b.number));
|
|
3574
|
+
const completedPhases = internalPhases.filter(p => p.phaseStatus === phase_status_cjs_1.PHASE_STATUS.COMPLETE).length;
|
|
3575
|
+
// #5060: project the internal PHASE_STATUS ladder to the display label at
|
|
3576
|
+
// the output boundary only — the internal `phaseStatus` field is never emitted.
|
|
3577
|
+
const phases = internalPhases.map(p => ({
|
|
3578
|
+
number: p.number,
|
|
3579
|
+
...(p.display_id ? { display_id: p.display_id } : {}),
|
|
3580
|
+
name: p.name,
|
|
3581
|
+
plans: p.plans,
|
|
3582
|
+
summaries: p.summaries,
|
|
3583
|
+
status: (0, phase_status_cjs_1.toDisplayLabel)(p.phaseStatus, { pendingWord: 'Not Started' }),
|
|
3584
|
+
}));
|
|
3579
3585
|
// #3217 (ADR-3180 §7.6 rule 4): both percentages here are derived from the
|
|
3580
3586
|
// same `phaseScope`-carrying directory enumeration above (Phase 3, #3222) —
|
|
3581
3587
|
// withhold both when that scope is not COMPLETE, same rationale as
|
|
@@ -3901,9 +3907,6 @@ function cmdCommitDocsGuardDisable(cwd, raw) {
|
|
|
3901
3907
|
module.exports = {
|
|
3902
3908
|
effortSurfaceForHost,
|
|
3903
3909
|
groupFilesBySubrepo,
|
|
3904
|
-
determinePhaseStatus,
|
|
3905
|
-
foldPhaseStatus,
|
|
3906
|
-
PHASE_STATUS_PRECEDENCE,
|
|
3907
3910
|
cmdGenerateSlug,
|
|
3908
3911
|
cmdCurrentTimestamp,
|
|
3909
3912
|
cmdListTodos,
|
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
* Complexity-triggered refactor extension point — analyzer, evaluator, and
|
|
4
4
|
* baseline persistence (issue #1953).
|
|
5
5
|
*
|
|
6
|
-
* LEAF MODULE — imports ONLY: node:fs, node:path
|
|
6
|
+
* LEAF MODULE — imports ONLY: node:fs, node:path, and the zero-import leaf
|
|
7
|
+
* frontmatter-fence.cjs (the one frontmatter fence owner, read by parseProposal). No other
|
|
8
|
+
* src/ imports.
|
|
7
9
|
*
|
|
8
10
|
* Pipeline: analyzeSource (decision-point complexity per function, via
|
|
9
11
|
* stripLiterals to blank comments/string/regex content while preserving
|
|
@@ -49,6 +51,7 @@ exports.readBaseline = readBaseline;
|
|
|
49
51
|
exports.writeBaseline = writeBaseline;
|
|
50
52
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
51
53
|
const node_path_1 = __importDefault(require("node:path"));
|
|
54
|
+
const frontmatter_fence_cjs_1 = require("./frontmatter-fence.cjs");
|
|
52
55
|
// ─── Constants ─────────────────────────────────────────────────────────────
|
|
53
56
|
exports.BASELINE_FILE_NAME = 'complexity-baseline.json'; // under .planning/
|
|
54
57
|
exports.PROPOSAL_SUFFIX = '-REFACTOR.md'; // ${PHASE_DIR}/${PADDED}-REFACTOR.md
|
|
@@ -1018,13 +1021,11 @@ function renderProposal(p) {
|
|
|
1018
1021
|
*/
|
|
1019
1022
|
function parseProposal(text) {
|
|
1020
1023
|
try {
|
|
1021
|
-
|
|
1024
|
+
// The block is the one the one fence owner finds (`locateFrontmatterFence`).
|
|
1025
|
+
const fence = (0, frontmatter_fence_cjs_1.locateFrontmatterFence)(text);
|
|
1026
|
+
if (!fence?.closed)
|
|
1022
1027
|
return null;
|
|
1023
|
-
const
|
|
1024
|
-
const closeIdx = text.indexOf('\n---', headerEnd);
|
|
1025
|
-
if (closeIdx === -1)
|
|
1026
|
-
return null;
|
|
1027
|
-
const yamlBody = text.slice(headerEnd, closeIdx);
|
|
1028
|
+
const yamlBody = text.slice(fence.openEnd, fence.bodyEnd);
|
|
1028
1029
|
const fm = {};
|
|
1029
1030
|
for (const rawLine of yamlBody.split(/\r?\n/)) {
|
|
1030
1031
|
const line = rawLine.replace(/\r$/, '');
|