@opengsd/gsd-core 1.13.0 → 1.15.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/README.ja-JP.md +3 -3
- package/README.ko-KR.md +3 -3
- package/README.pt-BR.md +3 -3
- package/README.zh-CN.md +3 -3
- package/agents/gsd-advisor-researcher.compact.md +85 -0
- package/agents/gsd-ai-researcher.compact.md +96 -0
- package/agents/gsd-assumptions-analyzer.compact.md +81 -0
- package/agents/gsd-code-fixer.compact.md +459 -0
- package/agents/gsd-code-fixer.md +9 -8
- package/agents/gsd-code-reviewer.compact.md +269 -0
- package/agents/gsd-code-reviewer.md +15 -3
- package/agents/gsd-codebase-mapper.compact.md +760 -0
- package/agents/gsd-debug-session-manager.compact.md +360 -0
- package/agents/gsd-debug-session-manager.md +17 -2
- package/agents/gsd-debugger.md +2 -2
- package/agents/gsd-doc-classifier.compact.md +192 -0
- package/agents/gsd-doc-synthesizer.compact.md +200 -0
- package/agents/gsd-doc-verifier.compact.md +143 -0
- package/agents/gsd-doc-writer.compact.md +440 -0
- package/agents/gsd-dom-verifier.compact.md +138 -0
- package/agents/gsd-domain-researcher.compact.md +141 -0
- package/agents/gsd-eval-auditor.compact.md +160 -0
- package/agents/gsd-eval-auditor.md +1 -1
- package/agents/gsd-eval-planner.compact.md +137 -0
- package/agents/gsd-executor.md +13 -8
- package/agents/gsd-framework-selector.compact.md +82 -0
- package/agents/gsd-integration-checker.compact.md +245 -0
- package/agents/gsd-intel-updater.compact.md +226 -0
- package/agents/gsd-intel-updater.md +1 -1
- package/agents/gsd-mempalace-curator.compact.md +45 -0
- package/agents/gsd-nyquist-auditor.compact.md +179 -0
- package/agents/gsd-pattern-mapper.compact.md +275 -0
- package/agents/gsd-phase-researcher.md +19 -11
- package/agents/gsd-plan-checker.md +8 -7
- package/agents/gsd-planner.md +12 -8
- package/agents/gsd-project-researcher.compact.md +587 -0
- package/agents/gsd-project-researcher.md +1 -1
- package/agents/gsd-research-synthesizer.compact.md +212 -0
- package/agents/gsd-research-synthesizer.md +1 -1
- package/agents/gsd-roadmapper.compact.md +454 -0
- package/agents/gsd-roadmapper.md +13 -0
- package/agents/gsd-security-auditor.compact.md +162 -0
- package/agents/gsd-ui-auditor.compact.md +404 -0
- package/agents/gsd-ui-auditor.md +155 -17
- package/agents/gsd-ui-checker.compact.md +277 -0
- package/agents/gsd-ui-researcher.compact.md +282 -0
- package/agents/gsd-ui-researcher.md +1 -1
- package/agents/gsd-user-profiler.compact.md +108 -0
- package/agents/gsd-verifier.md +10 -9
- package/bin/install.js +848 -163
- package/commands/gsd/autonomous.md +2 -2
- package/commands/gsd/capture.md +1 -1
- package/commands/gsd/cleanup.md +1 -0
- package/commands/gsd/code-review.md +2 -1
- package/commands/gsd/complete-milestone.md +1 -0
- package/commands/gsd/config.md +1 -0
- package/commands/gsd/debug.md +1 -0
- package/commands/gsd/graphify.md +1 -0
- package/commands/gsd/health.md +1 -0
- package/commands/gsd/mempalace-capture.md +8 -3
- package/commands/gsd/mempalace-recall.md +1 -0
- package/commands/gsd/new-milestone.md +1 -0
- package/commands/gsd/new-project.md +1 -0
- package/commands/gsd/next.md +1 -0
- package/commands/gsd/pause-work.md +1 -0
- package/commands/gsd/phase.md +1 -0
- package/commands/gsd/plan-review-convergence.md +6 -6
- package/commands/gsd/pr-branch.md +1 -0
- package/commands/gsd/progress.md +1 -1
- package/commands/gsd/quick-batch.md +1 -1
- package/commands/gsd/resume-work.md +1 -0
- package/commands/gsd/review-backlog.md +1 -0
- package/commands/gsd/review.md +2 -3
- package/commands/gsd/settings.md +2 -1
- package/commands/gsd/stats.md +1 -0
- package/commands/gsd/thread.md +1 -0
- package/commands/gsd/workspace.md +1 -0
- package/commands/gsd/workstreams.md +1 -0
- package/gsd-core/bin/check-latest-version.cjs +8 -3
- package/gsd-core/bin/gsd-tools.cjs +672 -146
- package/gsd-core/bin/lib/adr-parser.cjs +4 -2
- package/gsd-core/bin/lib/artifacts.cjs +2 -1
- package/gsd-core/bin/lib/audit.cjs +119 -34
- package/gsd-core/bin/lib/broken-windows.cjs +168 -49
- package/gsd-core/bin/lib/capability-lifecycle.cjs +10 -6
- package/gsd-core/bin/lib/capability-loader.cjs +135 -1
- package/gsd-core/bin/lib/capability-registry.cjs +96 -189
- package/gsd-core/bin/lib/capability-source.cjs +19 -2
- package/gsd-core/bin/lib/capability-validator.cjs +14 -2
- package/gsd-core/bin/lib/check-command-router.cjs +213 -49
- package/gsd-core/bin/lib/code-review-depth.cjs +2 -2
- package/gsd-core/bin/lib/codex-agent-toml.cjs +21 -25
- package/gsd-core/bin/lib/commands.cjs +823 -112
- package/gsd-core/bin/lib/config-loader.cjs +66 -4
- package/gsd-core/bin/lib/config.cjs +186 -45
- package/gsd-core/bin/lib/coverage.cjs +1 -1
- package/gsd-core/bin/lib/decisions.cjs +164 -45
- package/gsd-core/bin/lib/external-descriptor-trust.cjs +29 -14
- package/gsd-core/bin/lib/frontmatter.cjs +13 -0
- package/gsd-core/bin/lib/graphify.cjs +10 -2
- package/gsd-core/bin/lib/gsd2-import.cjs +1 -2
- package/gsd-core/bin/lib/health-diagnostic-rules/state-consistency.cjs +12 -1
- package/gsd-core/bin/lib/health-diagnostic-rules/worktree-health.cjs +1 -1
- package/gsd-core/bin/lib/host-runtime-detection.cjs +9 -0
- package/gsd-core/bin/lib/init.cjs +614 -86
- package/gsd-core/bin/lib/install-engine.cjs +29 -3
- package/gsd-core/bin/lib/install-profiles.cjs +14 -0
- package/gsd-core/bin/lib/installer-migrations.cjs +41 -5
- package/gsd-core/bin/lib/loop-resolver.cjs +50 -31
- package/gsd-core/bin/lib/mcp-catalog.cjs +2 -2
- package/gsd-core/bin/lib/milestone.cjs +37 -13
- package/gsd-core/bin/lib/model-resolver.cjs +253 -53
- package/gsd-core/bin/lib/phase-command-router.cjs +16 -2
- package/gsd-core/bin/lib/phase-id-card.cjs +32 -0
- package/gsd-core/bin/lib/phase-id-display.cjs +78 -0
- package/gsd-core/bin/lib/phase-id.cjs +268 -27
- package/gsd-core/bin/lib/phase-lifecycle.cjs +61 -0
- package/gsd-core/bin/lib/phase-locator.cjs +29 -10
- package/gsd-core/bin/lib/phase.cjs +393 -88
- package/gsd-core/bin/lib/plan-document.cjs +49 -1
- package/gsd-core/bin/lib/planning-document.cjs +459 -0
- package/gsd-core/bin/lib/planning-inspect.cjs +52 -19
- package/gsd-core/bin/lib/planning-snapshot.cjs +61 -12
- package/gsd-core/bin/lib/planning-workspace.cjs +57 -3
- package/gsd-core/bin/lib/pr-branch-patterns.cjs +57 -0
- package/gsd-core/bin/lib/pristine-baseline.cjs +182 -0
- package/gsd-core/bin/lib/probe-core.cjs +7 -1
- package/gsd-core/bin/lib/prohibition-enforcement.cjs +91 -4
- package/gsd-core/bin/lib/project-root.cjs +41 -2
- package/gsd-core/bin/lib/quick-batch.cjs +1 -1
- package/gsd-core/bin/lib/refactor-trigger-command-router.cjs +61 -2
- package/gsd-core/bin/lib/research-store.cjs +11 -12
- package/gsd-core/bin/lib/review-lane-descriptor.cjs +10 -30
- package/gsd-core/bin/lib/review-lane-invocation.cjs +23 -0
- package/gsd-core/bin/lib/review-reviewer-selection.cjs +2 -2
- package/gsd-core/bin/lib/reviewer-step-dispatch.cjs +337 -0
- package/gsd-core/bin/lib/roadmap-command-router.cjs +12 -4
- package/gsd-core/bin/lib/roadmap-parser.cjs +219 -18
- package/gsd-core/bin/lib/roadmap-upgrade.cjs +1539 -13
- package/gsd-core/bin/lib/roadmap.cjs +356 -42
- package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +310 -41
- package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +15 -4
- package/gsd-core/bin/lib/runtime-artifact-layout.cjs +13 -5
- package/gsd-core/bin/lib/runtime-homes.cjs +4 -0
- package/gsd-core/bin/lib/runtime-hooks-surface.cjs +408 -37
- package/gsd-core/bin/lib/runtime-name-policy.cjs +111 -1
- package/gsd-core/bin/lib/security.cjs +126 -7
- package/gsd-core/bin/lib/shell-command-projection.cjs +10 -6
- package/gsd-core/bin/lib/state-document.cjs +130 -28
- package/gsd-core/bin/lib/state-md-schema.cjs +21 -14
- package/gsd-core/bin/lib/state-transition.cjs +181 -30
- package/gsd-core/bin/lib/state.cjs +265 -27
- package/gsd-core/bin/lib/surface.cjs +77 -3
- package/gsd-core/bin/lib/task-command-router.cjs +12 -6
- package/gsd-core/bin/lib/tdd-red-evidence.cjs +78 -5
- package/gsd-core/bin/lib/uat-predicate.cjs +47 -4
- package/gsd-core/bin/lib/uat.cjs +9 -1
- package/gsd-core/bin/lib/ui-consideration-probe.cjs +15 -2
- package/gsd-core/bin/lib/ui-frontend-evidence.cjs +100 -9
- package/gsd-core/bin/lib/undo-commit-selection.cjs +131 -0
- package/gsd-core/bin/lib/update-context.cjs +30 -24
- package/gsd-core/bin/lib/vendor/js-yaml.cjs +11 -3
- package/gsd-core/bin/lib/verification.cjs +315 -30
- package/gsd-core/bin/lib/verify-command-grounding.cjs +47 -3
- package/gsd-core/bin/lib/verify.cjs +320 -48
- package/gsd-core/bin/lib/workstream-inventory.cjs +1 -0
- package/gsd-core/bin/lib/worktree-base-ref.cjs +482 -73
- package/gsd-core/bin/lib/worktree-safety.cjs +797 -58
- package/gsd-core/bin/shared/config-defaults.manifest.json +4 -0
- package/gsd-core/bin/shared/config-schema.manifest.json +6 -0
- package/gsd-core/bin/verify-reapply-patches.cjs +439 -80
- package/gsd-core/references/checkpoints.md +5 -3
- package/gsd-core/references/compact-content-gate.md +66 -0
- package/gsd-core/references/edge-probe-fixtures/01-round-half-even/expected-coverage.json +28 -3
- package/gsd-core/references/edge-probe-fixtures/02-merge-intervals/expected-coverage.json +37 -4
- package/gsd-core/references/edge-probe-fixtures/03-truncate-graphemes/expected-coverage.json +28 -3
- package/gsd-core/references/edge-probe-fixtures/04-money-rounding/expected-coverage.json +28 -3
- package/gsd-core/references/edge-probe-fixtures/05-list-dedupe/expected-coverage.json +37 -4
- package/gsd-core/references/edge-probe-fixtures/06-resolved-mixed/expected-coverage.json +37 -4
- package/gsd-core/references/edge-probe.md +195 -21
- package/gsd-core/references/execute-phase-between-wave-reset.md +7 -6
- package/gsd-core/references/execute-phase-wave-guard.md +22 -11
- package/gsd-core/references/gsd-run-resolver.md +1 -1
- package/gsd-core/references/loop-hook-dispatch.md +18 -0
- package/gsd-core/references/model-profiles.md +13 -4
- package/gsd-core/references/phase-argument-parsing.md +9 -7
- package/gsd-core/references/phase-id-convention.md +28 -0
- package/gsd-core/references/planner-gap-closure.md +2 -0
- package/gsd-core/references/planner-load-graph-context.md +24 -13
- package/gsd-core/references/planner-verify-command-grounding.md +14 -0
- package/gsd-core/references/planning-config.md +14 -2
- package/gsd-core/references/tdd.md +30 -4
- package/gsd-core/references/thinking-models-planning.md +18 -2
- package/gsd-core/references/ui-consideration-probe.md +10 -5
- package/gsd-core/references/verification-patterns.md +17 -4
- package/gsd-core/references/verify-command-path-resolvability.md +10 -2
- package/gsd-core/references/worktree-path-safety.md +433 -2
- package/gsd-core/templates/README.md +7 -1
- package/gsd-core/templates/state.md +6 -3
- package/gsd-core/templates/summary.compact.md +212 -0
- package/gsd-core/templates/user-setup.compact.md +199 -0
- package/gsd-core/templates/user-setup.md +0 -9
- 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 +1 -1
- package/gsd-core/workflows/add-tests.md +2 -2
- package/gsd-core/workflows/add-todo.md +6 -5
- package/gsd-core/workflows/ai-integration-phase.md +11 -3
- package/gsd-core/workflows/audit-fix.md +1 -1
- package/gsd-core/workflows/audit-milestone.md +1 -1
- package/gsd-core/workflows/audit-uat.md +1 -1
- package/gsd-core/workflows/autonomous/steps/converge-fail-fast.md +9 -18
- package/gsd-core/workflows/autonomous.md +29 -16
- package/gsd-core/workflows/check-todos.md +6 -4
- package/gsd-core/workflows/cleanup.md +5 -3
- package/gsd-core/workflows/code-review/steps/dispatch-fix.md +4 -3
- package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +8 -1
- package/gsd-core/workflows/code-review-fix.md +108 -22
- package/gsd-core/workflows/code-review.md +216 -73
- package/gsd-core/workflows/complete-milestone/detail/elaboration.md +274 -0
- package/gsd-core/workflows/complete-milestone.md +41 -264
- package/gsd-core/workflows/debug.md +3 -3
- package/gsd-core/workflows/diagnose-issues.md +1 -1
- 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 +1 -1
- package/gsd-core/workflows/discuss-phase.md +1 -1
- package/gsd-core/workflows/do.md +2 -2
- package/gsd-core/workflows/docs-update/detail/elaboration.md +179 -0
- package/gsd-core/workflows/docs-update.md +17 -158
- package/gsd-core/workflows/edit-phase.md +1 -1
- package/gsd-core/workflows/eval-review.md +10 -3
- package/gsd-core/workflows/execute-phase/detail/elaboration.md +124 -0
- package/gsd-core/workflows/execute-phase/steps/code-review-disposition.md +1017 -0
- package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +19 -4
- package/gsd-core/workflows/execute-phase/steps/completion-reconciliation.md +56 -0
- package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +43 -4
- package/gsd-core/workflows/execute-phase/steps/executor-progress-policy.md +43 -0
- package/gsd-core/workflows/execute-phase/steps/gap-closure-artifacts.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/partial-wave.md +1 -1
- 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 +46 -9
- package/gsd-core/workflows/execute-phase/steps/protected-branch.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/ready-wave-gate.md +37 -0
- package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/sequential-root-pin.md +35 -0
- package/gsd-core/workflows/execute-phase/steps/stale-reverification.md +24 -0
- package/gsd-core/workflows/execute-phase/steps/tdd-applicability-resolution.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/threat-id-gate.md +28 -0
- package/gsd-core/workflows/execute-phase/steps/wave-post-gate-hooks.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/worktree-base-check.md +25 -0
- package/gsd-core/workflows/execute-phase.md +83 -172
- package/gsd-core/workflows/execute-plan.md +24 -10
- package/gsd-core/workflows/explore.md +3 -3
- package/gsd-core/workflows/extract-learnings.md +2 -1
- 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 -2
- package/gsd-core/workflows/help/modes/full.compact.md +398 -0
- package/gsd-core/workflows/help/modes/full.md +5 -5
- package/gsd-core/workflows/help/modes/topic.md +15 -5
- package/gsd-core/workflows/help.md +1 -1
- package/gsd-core/workflows/import.md +2 -2
- package/gsd-core/workflows/inbox.md +2 -2
- package/gsd-core/workflows/ingest-docs.md +3 -3
- package/gsd-core/workflows/insert-phase.md +1 -1
- package/gsd-core/workflows/list-seeds.md +1 -1
- package/gsd-core/workflows/list-workspaces.md +1 -1
- package/gsd-core/workflows/manager.md +2 -2
- package/gsd-core/workflows/map-codebase.md +52 -5
- package/gsd-core/workflows/milestone-summary.md +1 -1
- package/gsd-core/workflows/mvp-phase.md +1 -1
- package/gsd-core/workflows/new-milestone.md +56 -14
- package/gsd-core/workflows/new-project/detail/elaboration.md +216 -0
- package/gsd-core/workflows/new-project/steps/auto-mode-config.md +3 -3
- package/gsd-core/workflows/new-project/steps/codebase-map-offer.md +1 -1
- package/gsd-core/workflows/new-project.md +39 -209
- package/gsd-core/workflows/new-workspace.md +2 -2
- package/gsd-core/workflows/next.md +1 -1
- package/gsd-core/workflows/note.md +1 -1
- package/gsd-core/workflows/onboard.md +1 -1
- package/gsd-core/workflows/pause-work.md +1 -1
- package/gsd-core/workflows/plan-phase/detail/elaboration.md +209 -0
- package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +17 -5
- package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +23 -5
- package/gsd-core/workflows/plan-phase.md +45 -187
- package/gsd-core/workflows/plan-review-convergence.md +21 -5
- package/gsd-core/workflows/plant-seed.md +62 -20
- package/gsd-core/workflows/pr-branch.md +132 -20
- package/gsd-core/workflows/profile-user.md +2 -2
- package/gsd-core/workflows/progress.md +1 -1
- package/gsd-core/workflows/quick/steps/plan-checker-loop.md +25 -0
- package/gsd-core/workflows/quick/steps/quick-verification.md +1 -1
- package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +1 -1
- 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 +1 -1
- package/gsd-core/workflows/quick-batch/steps/worktree-dispatch.md +1 -1
- package/gsd-core/workflows/quick-batch.md +1 -1
- package/gsd-core/workflows/quick.md +29 -10
- package/gsd-core/workflows/reapply-patches.md +86 -6
- package/gsd-core/workflows/remove-phase.md +1 -1
- package/gsd-core/workflows/remove-workspace.md +2 -2
- package/gsd-core/workflows/resume-project.md +1 -1
- package/gsd-core/workflows/review.md +31 -16
- package/gsd-core/workflows/scan.md +1 -1
- package/gsd-core/workflows/secure-phase.md +3 -2
- package/gsd-core/workflows/settings-advanced.md +30 -10
- package/gsd-core/workflows/settings-integrations.md +2 -3
- package/gsd-core/workflows/settings.md +22 -9
- package/gsd-core/workflows/ship.md +3 -2
- 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 +2 -2
- package/gsd-core/workflows/spec-phase.md +15 -5
- 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 +5 -5
- package/gsd-core/workflows/thread.md +1 -1
- package/gsd-core/workflows/transition.md +1 -1
- package/gsd-core/workflows/ui-phase.md +44 -8
- package/gsd-core/workflows/ui-review.md +18 -4
- package/gsd-core/workflows/ultraplan-phase.md +1 -1
- package/gsd-core/workflows/undo.md +339 -20
- package/gsd-core/workflows/update.md +14 -12
- package/gsd-core/workflows/validate-phase.md +3 -2
- package/gsd-core/workflows/verify-work/detail/elaboration.md +230 -0
- package/gsd-core/workflows/verify-work/steps/automated-ui-verification.md +1 -1
- package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +1 -1
- package/gsd-core/workflows/verify-work.md +101 -196
- package/hooks/dist/gsd-agent-isolation-guard.js +66 -16
- package/hooks/dist/gsd-context-monitor.js +88 -15
- package/hooks/dist/gsd-cursor-subagent-start.js +34 -14
- package/hooks/dist/gsd-secret-read-guard.js +71 -19
- package/hooks/dist/gsd-statusline.js +81 -20
- package/hooks/dist/gsd-validate-commit.sh +97 -8
- package/hooks/dist/gsd-worktree-path-guard.js +25 -14
- package/hooks/dist/gsd-write-guard.js +46 -1
- package/hooks/dist/lib/dispatch-identity.js +187 -0
- package/hooks/dist/lib/filename-classification.js +64 -0
- package/hooks/dist/lib/isolation-deny-reason.js +53 -1
- package/hooks/dist/lib/isolation-sentinel.js +58 -19
- package/hooks/gsd-agent-isolation-guard.js +66 -16
- package/hooks/gsd-context-monitor.js +88 -15
- package/hooks/gsd-cursor-subagent-start.js +34 -14
- package/hooks/gsd-secret-read-guard.js +71 -19
- package/hooks/gsd-statusline.js +81 -20
- package/hooks/gsd-validate-commit.sh +97 -8
- package/hooks/gsd-worktree-path-guard.js +25 -14
- package/hooks/gsd-write-guard.js +46 -1
- package/hooks/lib/dispatch-identity.js +187 -0
- package/hooks/lib/filename-classification.js +64 -0
- package/hooks/lib/isolation-deny-reason.js +53 -1
- package/hooks/lib/isolation-sentinel.js +58 -19
- package/package.json +11 -6
- package/scripts/benchmark-compact-content-variants.cjs +298 -0
- package/scripts/benchmark-compact-content.cjs +368 -0
- package/scripts/build-hooks.js +15 -6
- package/scripts/check-contract-drift.cjs +131 -12
- package/scripts/check-env.cjs +36 -8
- package/scripts/check-glossary-refs.cjs +25 -21
- package/scripts/ci-next-health.cjs +271 -0
- package/scripts/ci-prepare-test-scope.cjs +7 -7
- package/scripts/ci-test-scope.cjs +126 -20
- package/scripts/ci-timeout-report.cjs +1 -1
- package/scripts/command-contract-helpers.cjs +3 -0
- package/scripts/diff-touches-shipped-paths.cjs +1 -1
- package/scripts/docs-guard-registry.cjs +35 -2
- package/scripts/gen-adr-index.cjs +8 -2
- package/scripts/gen-inventory-manifest.cjs +12 -0
- package/scripts/gen-loop-host-contract.cjs +69 -0
- package/scripts/gen-platform-conformance-tier.cjs +557 -0
- package/scripts/lib/drift-scan.cjs +1 -1
- package/scripts/lib/macos-conformance-tier.generated.cjs +224 -0
- package/scripts/lib/ndjson-reporter.cjs +3 -2
- package/scripts/lib/npm-version-check-diagnosis.cjs +59 -0
- package/scripts/lib/platform-conformance-tier.generated.cjs +287 -0
- package/scripts/lib/suite-detection.cjs +32 -0
- package/scripts/lint-allowed-tools-parity.cjs +221 -0
- package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +47 -3
- package/scripts/lint-phase-arg-assignment.cjs +257 -0
- package/scripts/lint-phase-id-drift.cjs +623 -13
- package/scripts/lint-pr-branch-pattern-drift.cjs +148 -0
- package/scripts/lint-response-language-coverage.cjs +9 -3
- package/scripts/lint-retired-runtime-name.cjs +619 -0
- package/scripts/lint-source-test-name-collision.cjs +1 -1
- package/scripts/lint-state-write-path-drift.cjs +93 -0
- package/scripts/lint-test-file-count.allowlist.json +29 -9
- package/scripts/lint-vendored-deps.cjs +128 -17
- package/scripts/lint-workflow-shellcheck-baseline.json +100 -0
- package/scripts/prompt-injection-scan.sh +18 -0
- package/scripts/release-tarball-smoke.cjs +194 -1
- package/scripts/workflow-size.cjs +139 -0
- package/skills/gsd-autonomous/SKILL.md +2 -2
- package/skills/gsd-capture/SKILL.md +1 -1
- package/skills/gsd-cleanup/SKILL.md +1 -0
- package/skills/gsd-code-review/SKILL.md +2 -1
- package/skills/gsd-complete-milestone/SKILL.md +1 -0
- package/skills/gsd-config/SKILL.md +1 -0
- package/skills/gsd-debug/SKILL.md +1 -0
- package/skills/gsd-graphify/SKILL.md +1 -0
- package/skills/gsd-health/SKILL.md +1 -0
- package/skills/gsd-mempalace-capture/SKILL.md +8 -3
- package/skills/gsd-mempalace-recall/SKILL.md +1 -0
- package/skills/gsd-new-milestone/SKILL.md +1 -0
- package/skills/gsd-new-project/SKILL.md +1 -0
- package/skills/gsd-next/SKILL.md +1 -0
- package/skills/gsd-pause-work/SKILL.md +1 -0
- package/skills/gsd-phase/SKILL.md +1 -0
- package/skills/gsd-plan-review-convergence/SKILL.md +5 -5
- package/skills/gsd-pr-branch/SKILL.md +1 -0
- package/skills/gsd-progress/SKILL.md +1 -1
- package/skills/gsd-quick-batch/SKILL.md +1 -1
- package/skills/gsd-resume-work/SKILL.md +1 -0
- package/skills/gsd-review/SKILL.md +2 -3
- package/skills/gsd-review-backlog/SKILL.md +1 -0
- package/skills/gsd-settings/SKILL.md +2 -1
- package/skills/gsd-stats/SKILL.md +1 -0
- package/skills/gsd-thread/SKILL.md +1 -0
- package/skills/gsd-workspace/SKILL.md +1 -0
- package/skills/gsd-workstreams/SKILL.md +1 -0
- package/vscode/package.json +1 -1
- package/gsd-core/templates/claude-md.md +0 -145
- package/gsd-core/templates/codebase/concerns.md +0 -310
- package/gsd-core/templates/codebase/conventions.md +0 -307
- package/gsd-core/templates/codebase/integrations.md +0 -280
- package/gsd-core/templates/codebase/structure.md +0 -285
- package/gsd-core/templates/codebase/testing.md +0 -480
- package/gsd-core/templates/debug-subagent-prompt.md +0 -91
- package/gsd-core/templates/discovery.md +0 -146
|
@@ -28,6 +28,9 @@ const node_fs_1 = __importDefault(require("node:fs"));
|
|
|
28
28
|
const node_os_1 = __importDefault(require("node:os"));
|
|
29
29
|
const node_path_1 = __importDefault(require("node:path"));
|
|
30
30
|
const shell_command_projection_cjs_1 = require("./shell-command-projection.cjs");
|
|
31
|
+
// #4717: runtime-identity fill — env rung + per-install marker rung.
|
|
32
|
+
const runtime_slash_cjs_1 = require("./runtime-slash.cjs");
|
|
33
|
+
const runtime_name_policy_cjs_1 = require("./runtime-name-policy.cjs");
|
|
31
34
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
32
35
|
const planningWorkspace = require("./planning-workspace.cjs");
|
|
33
36
|
const { planningDir, planningRoot } = planningWorkspace;
|
|
@@ -135,6 +138,7 @@ const CONFIG_DEFAULTS = {
|
|
|
135
138
|
firecrawl: _getConfigDefault('firecrawl'),
|
|
136
139
|
exa_search: _getConfigDefault('exa_search'),
|
|
137
140
|
text_mode: _getNestedConfigDefault('workflow', 'text_mode'),
|
|
141
|
+
compact_content: _getNestedConfigDefault('workflow', 'compact_content'),
|
|
138
142
|
sub_repos: _getNestedConfigDefault('planning', 'sub_repos'),
|
|
139
143
|
pr_strict: _getNestedConfigDefault('planning', 'pr_strict'),
|
|
140
144
|
resolve_model_ids: _getConfigDefault('resolve_model_ids'),
|
|
@@ -149,8 +153,21 @@ const CONFIG_DEFAULTS = {
|
|
|
149
153
|
research_before_questions: _getNestedConfigDefault('workflow', 'research_before_questions'), // #3894
|
|
150
154
|
smart_zone_tokens: _getNestedConfigDefault('workflow', 'smart_zone_tokens'),
|
|
151
155
|
inline_plan_threshold: _getNestedConfigDefault('workflow', 'inline_plan_threshold'), // #3801
|
|
156
|
+
planner_stall_detection_enabled: _getNestedConfigDefault('planner', 'stall_detection_enabled'),
|
|
152
157
|
max_prompt_tokens: _getNestedConfigDefault('review', 'max_prompt_tokens'),
|
|
153
158
|
};
|
|
159
|
+
/**
|
|
160
|
+
* Resolve the planner watchdog policy from hand-edited configuration.
|
|
161
|
+
* Only a real JSON boolean may override the default; strings/numbers/null
|
|
162
|
+
* fail safe to the manifest-owned default-on behavior (#4570).
|
|
163
|
+
*/
|
|
164
|
+
function resolvePlannerStallDetectionEnabled(value) {
|
|
165
|
+
if (typeof value === 'boolean')
|
|
166
|
+
return value;
|
|
167
|
+
// A missing or skewed manifest must never convert an absent override into
|
|
168
|
+
// permission to disable the watchdog. The documented contract is default-on.
|
|
169
|
+
return true;
|
|
170
|
+
}
|
|
154
171
|
/**
|
|
155
172
|
* Deep-merge two plain config objects. `overlay` wins on key conflict.
|
|
156
173
|
* Explicit `null` in overlay overrides base (null means "unset this key").
|
|
@@ -600,7 +617,7 @@ function _warnUnusableConfig(fault) {
|
|
|
600
617
|
* cannot dirty the working tree; the ~30 callers that omit it keep persisting, so
|
|
601
618
|
* a legacy config is still migrated exactly once by ordinary use.
|
|
602
619
|
*/
|
|
603
|
-
function
|
|
620
|
+
function loadConfigResolvedInternal(cwd, options = {}) {
|
|
604
621
|
// Opt-OUT, not opt-in: omitting the option must preserve the historical
|
|
605
622
|
// write-back for every existing caller.
|
|
606
623
|
const persist = options['persist'] !== false;
|
|
@@ -613,7 +630,7 @@ function loadConfigResolved(cwd, options = {}) {
|
|
|
613
630
|
: (options['workstreamContext'] && Object.prototype.hasOwnProperty.call(options['workstreamContext'], 'ws'))
|
|
614
631
|
? options['workstreamContext']['ws']
|
|
615
632
|
: (process.env['GSD_WORKSTREAM'] || null);
|
|
616
|
-
const ws = typeof activeWorkstream === 'string' ? activeWorkstream
|
|
633
|
+
const ws = typeof activeWorkstream === 'string' ? activeWorkstream.trim() || null : null;
|
|
617
634
|
// wsRequested: true when caller explicitly requested a non-empty workstream.
|
|
618
635
|
// Used for source labeling (Fix 4) and early absent-dir intercept (Fix 2).
|
|
619
636
|
const wsRequested = ws != null && ws !== '';
|
|
@@ -857,6 +874,7 @@ function loadConfigResolved(cwd, options = {}) {
|
|
|
857
874
|
phase_naming: get('phase_naming') ?? defaults.phase_naming,
|
|
858
875
|
project_code: get('project_code') ?? defaults.project_code,
|
|
859
876
|
subagent_timeout: get('subagent_timeout', { section: 'workflow', field: 'subagent_timeout' }) ?? defaults.subagent_timeout,
|
|
877
|
+
planner_stall_detection_enabled: resolvePlannerStallDetectionEnabled(getNested('planner', 'stall_detection_enabled')),
|
|
860
878
|
model_overrides: (parsed['model_overrides']) || null,
|
|
861
879
|
agent_tools: (parsed['agent_tools']) || null,
|
|
862
880
|
models: (parsed['models']) || null,
|
|
@@ -958,7 +976,7 @@ function loadConfigResolved(cwd, options = {}) {
|
|
|
958
976
|
// `workstream: null` still wins the `hasOwnProperty` check at the top of this
|
|
959
977
|
// function, so spreading cannot let `workstreamContext` reintroduce a workstream.
|
|
960
978
|
if (wsRequested && rootParsed) {
|
|
961
|
-
const fb =
|
|
979
|
+
const fb = loadConfigResolvedInternal(cwd, { ...options, workstream: null });
|
|
962
980
|
return fallback({ config: fb.config, source: 'root', degraded: true });
|
|
963
981
|
}
|
|
964
982
|
// Branch B, C, D, E
|
|
@@ -966,7 +984,7 @@ function loadConfigResolved(cwd, options = {}) {
|
|
|
966
984
|
if (rootParsed) {
|
|
967
985
|
// Branch B: workstream requested but ws config.json absent; root config present.
|
|
968
986
|
// (Only reached when wsRequested is false — e.g. ws='' with .planning/workstreams//config.json)
|
|
969
|
-
const fb =
|
|
987
|
+
const fb = loadConfigResolvedInternal(cwd, { ...options, workstream: null });
|
|
970
988
|
return fallback({ config: fb.config, source: 'root', degraded: true });
|
|
971
989
|
}
|
|
972
990
|
// Branch C: .planning/ exists but no config.json and no root config — federated/builtin defaults
|
|
@@ -1015,6 +1033,7 @@ function loadConfigResolved(cwd, options = {}) {
|
|
|
1015
1033
|
resolve_model_ids: (globalDefaults['resolve_model_ids']) ?? defaults.resolve_model_ids,
|
|
1016
1034
|
context_window: (globalDefaults['context_window']) ?? defaults.context_window,
|
|
1017
1035
|
subagent_timeout: (globalDefaults['subagent_timeout']) ?? defaults.subagent_timeout,
|
|
1036
|
+
planner_stall_detection_enabled: resolvePlannerStallDetectionEnabled(globalDefaults['planner']?.['stall_detection_enabled']),
|
|
1018
1037
|
model_overrides: (globalDefaults['model_overrides']) || null,
|
|
1019
1038
|
models: (globalDefaults['models']) || null,
|
|
1020
1039
|
granularity: (globalDefaults['granularity']) !== undefined ? globalDefaults['granularity'] : null,
|
|
@@ -1053,6 +1072,48 @@ function loadConfigResolved(cwd, options = {}) {
|
|
|
1053
1072
|
}
|
|
1054
1073
|
}
|
|
1055
1074
|
}
|
|
1075
|
+
/**
|
|
1076
|
+
* #4717 — fill an empty `runtime` from the environment, then the per-install
|
|
1077
|
+
* marker. writeNonClaudeDefaults stamps `runtime` into the SHARED
|
|
1078
|
+
* ~/.gsd/defaults.json with whichever non-Claude runtime installed first, so
|
|
1079
|
+
* direct readers of config.runtime saw another runtime's identity (or
|
|
1080
|
+
* nothing) on a multi-runtime machine. Copy-on-write: the builtin-defaults
|
|
1081
|
+
* branch returns a shared object, so never assign into it. An explicit
|
|
1082
|
+
* config.runtime is never overridden.
|
|
1083
|
+
*/
|
|
1084
|
+
function fillRuntimeIdentity(resolved) {
|
|
1085
|
+
const cfg = resolved?.config;
|
|
1086
|
+
if (!cfg)
|
|
1087
|
+
return resolved;
|
|
1088
|
+
const runtime = (0, runtime_name_policy_cjs_1.resolveRuntimeNameFromCandidates)(process.env['GSD_RUNTIME'], (0, runtime_slash_cjs_1.readInstallRuntimeMarker)());
|
|
1089
|
+
// Only a runtime the name policy can canonicalize is an identity. Unknown
|
|
1090
|
+
// tokens pass THROUGH resolveRuntimeNameFromCandidates (future-runtime
|
|
1091
|
+
// tolerance) and must not be materialized into config.runtime, where ~30
|
|
1092
|
+
// consumers would read them — fail safe to no identity (#4717 review).
|
|
1093
|
+
const canonicalRuntime = runtime ? (0, runtime_name_policy_cjs_1.canonicalizeRuntimeName)(runtime) : null;
|
|
1094
|
+
if (!canonicalRuntime)
|
|
1095
|
+
return resolved;
|
|
1096
|
+
// Rung 1 — empty runtime: materialize THIS install's identity (env, then
|
|
1097
|
+
// the per-install marker). An explicit runtime is never overridden.
|
|
1098
|
+
if (!cfg['runtime']) {
|
|
1099
|
+
return { ...resolved, config: { ...cfg, runtime: canonicalRuntime } };
|
|
1100
|
+
}
|
|
1101
|
+
// Rung 2 (#4717 stamped-defaults leg): the global-defaults branch forwards
|
|
1102
|
+
// the SHARED ~/.gsd/defaults.json's runtime verbatim — whichever non-Claude
|
|
1103
|
+
// runtime installed FIRST stamped that machine-wide file, and every other
|
|
1104
|
+
// runtime's resolution inherited its identity (the issue's second failure
|
|
1105
|
+
// shape). When THIS install carries its own identity (GSD_RUNTIME or the
|
|
1106
|
+
// marker), the stamp — not the operator — is speaking: correct it. Project
|
|
1107
|
+
// and workstream configs are explicit operator intent and are never touched;
|
|
1108
|
+
// with no install identity of its own the stamped value stays (status quo).
|
|
1109
|
+
if (resolved.source === 'global-defaults') {
|
|
1110
|
+
return { ...resolved, config: { ...cfg, runtime: canonicalRuntime } };
|
|
1111
|
+
}
|
|
1112
|
+
return resolved;
|
|
1113
|
+
}
|
|
1114
|
+
function loadConfigResolved(cwd, options = {}) {
|
|
1115
|
+
return fillRuntimeIdentity(loadConfigResolvedInternal(cwd, options));
|
|
1116
|
+
}
|
|
1056
1117
|
/**
|
|
1057
1118
|
* loadConfig — backwards-compatible config loading, now a thin wrapper over loadConfigResolved.
|
|
1058
1119
|
* Returns the config object only; for provenance metadata use loadConfigResolved.
|
|
@@ -1071,6 +1132,7 @@ module.exports = {
|
|
|
1071
1132
|
_getNestedConfigDefault,
|
|
1072
1133
|
_getConfigValue,
|
|
1073
1134
|
_getConfigNested,
|
|
1135
|
+
resolvePlannerStallDetectionEnabled,
|
|
1074
1136
|
_deepMergeConfig,
|
|
1075
1137
|
_warnedUnknownConfigKeys,
|
|
1076
1138
|
_warnedShadowedGlobalKeys,
|
|
@@ -20,11 +20,11 @@ const cliExitMod = require("./cli-exit.cjs");
|
|
|
20
20
|
const { ExitError } = cliExitMod;
|
|
21
21
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
22
22
|
const configLoader = require("./config-loader.cjs");
|
|
23
|
-
const { CONFIG_DEFAULTS } = configLoader;
|
|
23
|
+
const { CONFIG_DEFAULTS, resolvePlannerStallDetectionEnabled } = configLoader;
|
|
24
24
|
const shell_command_projection_cjs_1 = require("./shell-command-projection.cjs");
|
|
25
25
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
26
26
|
const planningWorkspace = require("./planning-workspace.cjs");
|
|
27
|
-
const { planningDir, planningRoot, withPlanningLock } = planningWorkspace;
|
|
27
|
+
const { planningDir, planningRoot, resolveEnvWorkstream, withPlanningLock } = planningWorkspace;
|
|
28
28
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
29
29
|
const modelProfiles = require("./model-profiles.cjs");
|
|
30
30
|
const { VALID_PROFILES, getAgentToModelMapForProfile, formatAgentToModelMapAsTable } = modelProfiles;
|
|
@@ -66,6 +66,14 @@ const SHIP_PR_BODY_TEMPLATE_TOKENS = new Set([
|
|
|
66
66
|
'padded_phase',
|
|
67
67
|
]);
|
|
68
68
|
const SHIP_PR_BODY_SOURCE_RE = /^(ROADMAP|PLAN|SUMMARY|VERIFICATION|STATE|REQUIREMENTS|CONTEXT)\.md\s+##\s+[^\r\n#][^\r\n]*$/;
|
|
69
|
+
// ADR-612 PR-5: configuration accepts every convention the runtime can read.
|
|
70
|
+
// Keep this distinct from roadmap-upgrade's supported target set: sequential
|
|
71
|
+
// is valid project configuration but is not a migration destination.
|
|
72
|
+
const VALID_PHASE_ID_CONVENTIONS = Object.freeze([
|
|
73
|
+
'sequential',
|
|
74
|
+
'milestone-prefixed',
|
|
75
|
+
'bracket',
|
|
76
|
+
]);
|
|
69
77
|
/**
|
|
70
78
|
* Schema-level defaults for well-known config keys.
|
|
71
79
|
* When a key is absent from config.json and no --default flag was supplied,
|
|
@@ -75,12 +83,18 @@ const SCHEMA_DEFAULTS = {
|
|
|
75
83
|
'context_window': 200000,
|
|
76
84
|
'executor.stall_detect_interval_minutes': 5,
|
|
77
85
|
'executor.stall_threshold_minutes': 10,
|
|
86
|
+
'planner.stall_detection_enabled': CONFIG_DEFAULTS.planner_stall_detection_enabled,
|
|
78
87
|
'planner.stall_detect_interval_minutes': 5,
|
|
79
88
|
'planner.stall_threshold_minutes': 10,
|
|
80
89
|
'git.create_tag': true,
|
|
81
90
|
// #1689: per-plan agent_hint executor routing — default-on. A no-op for plans
|
|
82
91
|
// without an agent_hint field, so existing dispatch is byte-identical.
|
|
83
92
|
'workflow.agent_hint_routing': true,
|
|
93
|
+
// #4401: Compact Content mode gate — derived from the defaults manifest via
|
|
94
|
+
// CONFIG_DEFAULTS (added in config-loader.cts) so the manifest stays the
|
|
95
|
+
// single source of truth, matching workflow.smart_zone_tokens /
|
|
96
|
+
// planning.pr_strict / workflow.inline_plan_threshold below.
|
|
97
|
+
'workflow.compact_content': CONFIG_DEFAULTS.compact_content,
|
|
84
98
|
// Derived from the defaults manifest rather than restated, so the manifest
|
|
85
99
|
// stays the single source of truth for the smart-zone budget (#2630).
|
|
86
100
|
'workflow.smart_zone_tokens': CONFIG_DEFAULTS.smart_zone_tokens,
|
|
@@ -93,6 +107,27 @@ const SCHEMA_DEFAULTS = {
|
|
|
93
107
|
// effective default existed only as the workflow's shell fallback and the
|
|
94
108
|
// docs disagreed (settings-advanced said 3). Manifest stays the one owner.
|
|
95
109
|
'workflow.inline_plan_threshold': CONFIG_DEFAULTS.inline_plan_threshold,
|
|
110
|
+
// #4285 review: an absent threshold resolved to "Key not found" while the
|
|
111
|
+
// hook silently used 35/25 — the query surface disagreeing with the reader.
|
|
112
|
+
//
|
|
113
|
+
// Restated here rather than derived: `CONFIG_DEFAULTS` is re-exported with a
|
|
114
|
+
// FLATTENED shape that drops the manifest's nested blocks, so
|
|
115
|
+
// `CONFIG_DEFAULTS.hooks` is undefined at runtime and the manifest cannot
|
|
116
|
+
// feed these two rows the way `workflow.smart_zone_tokens` above is fed.
|
|
117
|
+
//
|
|
118
|
+
// Not added to `buildNewProjectConfig` either, and that one is deliberate
|
|
119
|
+
// rather than incidental: it writes a `hooks` object into every NEW project's
|
|
120
|
+
// config.json, which would freeze today's fire-points as an explicit
|
|
121
|
+
// per-project override everywhere — the opposite of this PR's premise that an
|
|
122
|
+
// absent key tracks the shipped default. (The manifest alone would NOT have
|
|
123
|
+
// that effect; `buildNewProjectConfig` builds its own literal. Correcting an
|
|
124
|
+
// earlier version of this comment that ran the two together.)
|
|
125
|
+
//
|
|
126
|
+
// That leaves ONE copy of 35/25 outside the hook — these two rows — and
|
|
127
|
+
// `tests/config.test.cjs` pins them against the hook's exported
|
|
128
|
+
// WARNING_THRESHOLD/CRITICAL_THRESHOLD so the copies cannot drift.
|
|
129
|
+
'hooks.context_warning_threshold': 35,
|
|
130
|
+
'hooks.context_critical_threshold': 25,
|
|
96
131
|
};
|
|
97
132
|
/**
|
|
98
133
|
* Resolve a schema-level default for an absent key (#2256). Checks the legacy
|
|
@@ -126,12 +161,15 @@ function resolveSchemaDefault(cwd, kp) {
|
|
|
126
161
|
* Centralizing emission here means masking can't be missed at a call site.
|
|
127
162
|
*/
|
|
128
163
|
function emitResolvedDefault(kp, value, raw) {
|
|
164
|
+
const resolvedValue = kp === 'planner.stall_detection_enabled'
|
|
165
|
+
? resolvePlannerStallDetectionEnabled(value)
|
|
166
|
+
: value;
|
|
129
167
|
if ((0, secrets_cjs_1.isSecretKey)(kp)) {
|
|
130
|
-
const masked = (0, secrets_cjs_1.maskSecret)(
|
|
168
|
+
const masked = (0, secrets_cjs_1.maskSecret)(resolvedValue);
|
|
131
169
|
output(masked, raw, masked);
|
|
132
170
|
return;
|
|
133
171
|
}
|
|
134
|
-
output(
|
|
172
|
+
output(resolvedValue, raw, String(resolvedValue));
|
|
135
173
|
}
|
|
136
174
|
// ─── Validation helpers ───────────────────────────────────────────────────────
|
|
137
175
|
function validateKnownConfigKeyPath(keyPath) {
|
|
@@ -308,6 +346,7 @@ function buildNewProjectConfig(userChoices) {
|
|
|
308
346
|
human_verify_mode: 'end-of-phase',
|
|
309
347
|
context_guard_mode: 'warn',
|
|
310
348
|
text_mode: false,
|
|
349
|
+
compact_content: false,
|
|
311
350
|
research_before_questions: false,
|
|
312
351
|
discuss_mode: 'discuss',
|
|
313
352
|
skip_discuss: false,
|
|
@@ -515,7 +554,7 @@ function _setNestedValue(config, keyPath, parsedValue) {
|
|
|
515
554
|
}
|
|
516
555
|
/**
|
|
517
556
|
* Deletes a value from the config object, allowing nested values via dot
|
|
518
|
-
* notation (e.g., "review.models.
|
|
557
|
+
* notation (e.g., "review.models.codex"). Mirrors `_setNestedValue`'s
|
|
519
558
|
* prototype-pollution guard on every path segment (including intermediates).
|
|
520
559
|
*
|
|
521
560
|
* Unlike `_setNestedValue`, this NEVER creates missing intermediate objects —
|
|
@@ -564,19 +603,35 @@ function _unsetNestedValue(config, keyPath) {
|
|
|
564
603
|
* Does not call `output()`, so can be used as one step in a command without triggering `exit(0)` in
|
|
565
604
|
* the happy path. But note that `error()` will still `exit(1)` out of the process.
|
|
566
605
|
*/
|
|
606
|
+
/**
|
|
607
|
+
* Loads `.planning/config.json` as a plain object, or `{}` if the file does
|
|
608
|
+
* not exist. A parse failure calls `error()` (process-exiting) rather than
|
|
609
|
+
* throwing, matching every caller's existing behavior.
|
|
610
|
+
*
|
|
611
|
+
* Single source for this load+parse step — `setConfigValue`,
|
|
612
|
+
* `unsetConfigValue`, `setConfigValues`, `previewConfigValue`, and
|
|
613
|
+
* `previewUnsetConfigValue` all delegate here instead of each repeating the
|
|
614
|
+
* same try/catch (CLAUDE.md's "Generative Fix Divergence" known-defect
|
|
615
|
+
* pattern: independently-guessed copies of the same logic can silently
|
|
616
|
+
* drift apart).
|
|
617
|
+
*/
|
|
618
|
+
function loadConfigJson(cwd) {
|
|
619
|
+
const configPath = node_path_1.default.join(planningDir(cwd), 'config.json');
|
|
620
|
+
let config = {};
|
|
621
|
+
try {
|
|
622
|
+
if (node_fs_1.default.existsSync(configPath)) {
|
|
623
|
+
config = JSON.parse(node_fs_1.default.readFileSync(configPath, 'utf-8'));
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
catch (err) {
|
|
627
|
+
error('Failed to read config.json: ' + err.message, ERROR_REASON.CONFIG_PARSE_FAILED);
|
|
628
|
+
}
|
|
629
|
+
return config;
|
|
630
|
+
}
|
|
567
631
|
function unsetConfigValue(cwd, keyPath) {
|
|
568
632
|
const configPath = node_path_1.default.join(planningDir(cwd), 'config.json');
|
|
569
633
|
return withPlanningLock(cwd, () => {
|
|
570
|
-
|
|
571
|
-
let config = {};
|
|
572
|
-
try {
|
|
573
|
-
if (node_fs_1.default.existsSync(configPath)) {
|
|
574
|
-
config = JSON.parse(node_fs_1.default.readFileSync(configPath, 'utf-8'));
|
|
575
|
-
}
|
|
576
|
-
}
|
|
577
|
-
catch (err) {
|
|
578
|
-
error('Failed to read config.json: ' + err.message, ERROR_REASON.CONFIG_PARSE_FAILED);
|
|
579
|
-
}
|
|
634
|
+
const config = loadConfigJson(cwd);
|
|
580
635
|
const { previousValue, existed } = _unsetNestedValue(config, keyPath);
|
|
581
636
|
// Write back
|
|
582
637
|
try {
|
|
@@ -598,16 +653,7 @@ function unsetConfigValue(cwd, keyPath) {
|
|
|
598
653
|
function setConfigValue(cwd, keyPath, parsedValue) {
|
|
599
654
|
const configPath = node_path_1.default.join(planningDir(cwd), 'config.json');
|
|
600
655
|
return withPlanningLock(cwd, () => {
|
|
601
|
-
|
|
602
|
-
let config = {};
|
|
603
|
-
try {
|
|
604
|
-
if (node_fs_1.default.existsSync(configPath)) {
|
|
605
|
-
config = JSON.parse(node_fs_1.default.readFileSync(configPath, 'utf-8'));
|
|
606
|
-
}
|
|
607
|
-
}
|
|
608
|
-
catch (err) {
|
|
609
|
-
error('Failed to read config.json: ' + err.message, ERROR_REASON.CONFIG_PARSE_FAILED);
|
|
610
|
-
}
|
|
656
|
+
const config = loadConfigJson(cwd);
|
|
611
657
|
const previousValue = _setNestedValue(config, keyPath, parsedValue);
|
|
612
658
|
// Write back
|
|
613
659
|
try {
|
|
@@ -619,6 +665,29 @@ function setConfigValue(cwd, keyPath, parsedValue) {
|
|
|
619
665
|
}
|
|
620
666
|
});
|
|
621
667
|
}
|
|
668
|
+
/**
|
|
669
|
+
* #4444: read-only preview counterpart to `setConfigValue` — loads config
|
|
670
|
+
* exactly like the real setter and reuses `_setNestedValue` (the SAME
|
|
671
|
+
* traversal/creation logic, including its prototype-pollution guards) on a
|
|
672
|
+
* throwaway in-memory copy that is NEVER written back to disk. This is what
|
|
673
|
+
* makes the dry-run preview provably identical to what the real write would
|
|
674
|
+
* compute, rather than a second, hand-maintained traversal that could drift
|
|
675
|
+
* from the real one.
|
|
676
|
+
*/
|
|
677
|
+
function previewConfigValue(cwd, keyPath, parsedValue) {
|
|
678
|
+
const config = loadConfigJson(cwd);
|
|
679
|
+
const previousValue = _setNestedValue(config, keyPath, parsedValue);
|
|
680
|
+
return { key: keyPath, value: parsedValue, previousValue };
|
|
681
|
+
}
|
|
682
|
+
/**
|
|
683
|
+
* #4444: read-only preview counterpart to `unsetConfigValue` — same pattern
|
|
684
|
+
* as `previewConfigValue`, reusing `_unsetNestedValue` on a throwaway copy.
|
|
685
|
+
*/
|
|
686
|
+
function previewUnsetConfigValue(cwd, keyPath) {
|
|
687
|
+
const config = loadConfigJson(cwd);
|
|
688
|
+
const { previousValue, existed } = _unsetNestedValue(config, keyPath);
|
|
689
|
+
return { key: keyPath, value: null, previousValue, existed };
|
|
690
|
+
}
|
|
622
691
|
/**
|
|
623
692
|
* Batched sibling of setConfigValue: apply multiple key-path writes in a
|
|
624
693
|
* single load → set-all → write cycle inside ONE withPlanningLock call.
|
|
@@ -635,16 +704,7 @@ function setConfigValues(cwd, entries) {
|
|
|
635
704
|
}
|
|
636
705
|
const configPath = node_path_1.default.join(planningDir(cwd), 'config.json');
|
|
637
706
|
return withPlanningLock(cwd, () => {
|
|
638
|
-
|
|
639
|
-
let config = {};
|
|
640
|
-
try {
|
|
641
|
-
if (node_fs_1.default.existsSync(configPath)) {
|
|
642
|
-
config = JSON.parse(node_fs_1.default.readFileSync(configPath, 'utf-8'));
|
|
643
|
-
}
|
|
644
|
-
}
|
|
645
|
-
catch (err) {
|
|
646
|
-
error('Failed to read config.json: ' + err.message, ERROR_REASON.CONFIG_PARSE_FAILED);
|
|
647
|
-
}
|
|
707
|
+
const config = loadConfigJson(cwd);
|
|
648
708
|
const results = [];
|
|
649
709
|
for (const entry of entries) {
|
|
650
710
|
const previousValue = _setNestedValue(config, entry.keyPath, entry.value);
|
|
@@ -676,14 +736,8 @@ function assertEnumValue(parsedValue, rawVal, allowed, label) {
|
|
|
676
736
|
error(`Invalid ${label} '${rawVal}'. Valid values: ${allowed.join(', ')}`);
|
|
677
737
|
}
|
|
678
738
|
}
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
* "workflow.research").
|
|
682
|
-
*
|
|
683
|
-
* Note that this exits the process (via `output()`) even in the happy path; use `setConfigValue()`
|
|
684
|
-
* directly if you need to avoid this.
|
|
685
|
-
*/
|
|
686
|
-
function cmdConfigSet(cwd, keyPath, value, raw) {
|
|
739
|
+
function cmdConfigSet(cwd, keyPath, value, raw, options = {}) {
|
|
740
|
+
const dryRun = options.dryRun === true;
|
|
687
741
|
if (!keyPath) {
|
|
688
742
|
error('Usage: config-set <key.path> <value>', ERROR_REASON.USAGE);
|
|
689
743
|
}
|
|
@@ -733,6 +787,18 @@ function cmdConfigSet(cwd, keyPath, value, raw) {
|
|
|
733
787
|
// present, truthy-adjacent value that consumers must special-case — worst for
|
|
734
788
|
// secret keys where a leftover value can be passed as a real credential.
|
|
735
789
|
if (parsedValue === null) {
|
|
790
|
+
if (dryRun) {
|
|
791
|
+
const preview = previewUnsetConfigValue(cwd, kp);
|
|
792
|
+
if ((0, secrets_cjs_1.isSecretKey)(kp)) {
|
|
793
|
+
const maskedPrev = preview.previousValue === undefined
|
|
794
|
+
? undefined
|
|
795
|
+
: (0, secrets_cjs_1.maskSecret)(preview.previousValue);
|
|
796
|
+
output({ dry_run: true, would_unset: true, key: kp, value: null, previousValue: maskedPrev, masked: true }, raw, `${kp} unset (dry run)`);
|
|
797
|
+
return;
|
|
798
|
+
}
|
|
799
|
+
output({ dry_run: true, would_unset: true, key: kp, value: null, previousValue: preview.previousValue }, raw, `${kp} unset (dry run)`);
|
|
800
|
+
return;
|
|
801
|
+
}
|
|
736
802
|
const unsetResult = unsetConfigValue(cwd, kp);
|
|
737
803
|
if ((0, secrets_cjs_1.isSecretKey)(kp)) {
|
|
738
804
|
const maskedPrev = unsetResult.previousValue === undefined
|
|
@@ -752,6 +818,9 @@ function cmdConfigSet(cwd, keyPath, value, raw) {
|
|
|
752
818
|
const VALID_CONTEXT_VALUES = ['dev', 'research', 'review'];
|
|
753
819
|
if (kp === 'context')
|
|
754
820
|
assertEnumValue(parsedValue, val, VALID_CONTEXT_VALUES, 'context value');
|
|
821
|
+
if (kp === 'phase_id_convention') {
|
|
822
|
+
assertEnumValue(parsedValue, val, VALID_PHASE_ID_CONVENTIONS, 'phase_id_convention');
|
|
823
|
+
}
|
|
755
824
|
// Codebase drift detector (#2003)
|
|
756
825
|
const VALID_DRIFT_ACTIONS = ['warn', 'auto-remap'];
|
|
757
826
|
if (kp === 'workflow.drift_action')
|
|
@@ -787,12 +856,25 @@ function cmdConfigSet(cwd, keyPath, value, raw) {
|
|
|
787
856
|
error(`Invalid workflow.post_planning_gaps '${val}'. Must be a boolean (true or false).`);
|
|
788
857
|
}
|
|
789
858
|
}
|
|
859
|
+
// Compact Content mode gate (#4139)
|
|
860
|
+
if (kp === 'workflow.compact_content') {
|
|
861
|
+
if (typeof parsedValue !== 'boolean') {
|
|
862
|
+
error(`Invalid workflow.compact_content '${val}'. Must be a boolean (true or false).`);
|
|
863
|
+
}
|
|
864
|
+
}
|
|
790
865
|
// Per-plan executor routing via agent_hint frontmatter (#1689)
|
|
791
866
|
if (kp === 'workflow.agent_hint_routing') {
|
|
792
867
|
if (typeof parsedValue !== 'boolean') {
|
|
793
868
|
error(`Invalid workflow.agent_hint_routing '${val}'. Must be a boolean (true or false).`);
|
|
794
869
|
}
|
|
795
870
|
}
|
|
871
|
+
// Planner watchdog opt-out (#4570) — only a real boolean may change the
|
|
872
|
+
// default-on policy. In particular, string "false" must not disable it.
|
|
873
|
+
if (kp === 'planner.stall_detection_enabled') {
|
|
874
|
+
if (typeof parsedValue !== 'boolean') {
|
|
875
|
+
error(`Invalid planner.stall_detection_enabled '${val}'. Must be a boolean (true or false).`);
|
|
876
|
+
}
|
|
877
|
+
}
|
|
796
878
|
// #3086 — git.create_tag: boolean only
|
|
797
879
|
if (kp === 'git.create_tag') {
|
|
798
880
|
if (typeof parsedValue !== 'boolean') {
|
|
@@ -835,6 +917,48 @@ function cmdConfigSet(cwd, keyPath, value, raw) {
|
|
|
835
917
|
error(`Invalid statusline.show_git '${val}'. Must be a boolean (true or false).`);
|
|
836
918
|
}
|
|
837
919
|
}
|
|
920
|
+
// Context-monitor fire-points (#4285) — a percentage of the context window
|
|
921
|
+
// REMAINING, so the domain is 0-100 and the hook compares them against
|
|
922
|
+
// `remaining_percentage`. Rejecting an out-of-domain value here keeps accept
|
|
923
|
+
// and honour in agreement ON THE DOMAIN: the hook falls back to its default
|
|
924
|
+
// for a value outside it, so reporting success would be a lie. That agreement
|
|
925
|
+
// is per-key and no wider — a value accepted here can still be superseded at
|
|
926
|
+
// read time by the hook's pair check, and a scoped write (GSD_PROJECT /
|
|
927
|
+
// GSD_WORKSTREAM) lands in a config the hook does not read at all. The PAIR
|
|
928
|
+
// (critical < warning) is deliberately NOT enforced here: config-set writes
|
|
929
|
+
// one key per call, so a two-step retune can be transiently inconsistent on
|
|
930
|
+
// disk and a check here would reject that intermediate write.
|
|
931
|
+
if (kp === 'hooks.context_warning_threshold' || kp === 'hooks.context_critical_threshold') {
|
|
932
|
+
if (typeof parsedValue !== 'number' || !Number.isFinite(parsedValue) || parsedValue < 0 || parsedValue > 100) {
|
|
933
|
+
error(`Invalid ${kp} '${val}'. Must be a number between 0 and 100 (percent of context window remaining).`);
|
|
934
|
+
}
|
|
935
|
+
// The two ENDPOINTS that are in range but can never form a valid pair are
|
|
936
|
+
// refused here rather than stored (#4285 review). `critical < warning` must
|
|
937
|
+
// hold at read time and BOTH sides are clamped to 0-100, so `warning: 0`
|
|
938
|
+
// has no legal partner (nothing is below 0) and `critical: 100` has none
|
|
939
|
+
// either (nothing above 100). Either one is silently discarded by the hook
|
|
940
|
+
// for EVERY value of the other key — verified: both resolve to the 35/25
|
|
941
|
+
// defaults against a present, absent, or extreme partner, while 0.001 and
|
|
942
|
+
// 99.999 are honoured.
|
|
943
|
+
//
|
|
944
|
+
// Storing a value the reader can never honour is exactly the
|
|
945
|
+
// accept-then-discard shape this codebase refuses elsewhere, so this fails
|
|
946
|
+
// at write time where the operator can see it. The pair itself is still NOT
|
|
947
|
+
// checked here — config-set writes one key per call, so a two-step retune
|
|
948
|
+
// is legitimately inconsistent on disk in between.
|
|
949
|
+
if (kp === 'hooks.context_warning_threshold' && parsedValue === 0) {
|
|
950
|
+
error(`Invalid ${kp} '${val}'. 0 is in range but unusable: the monitor requires `
|
|
951
|
+
+ `hooks.context_critical_threshold < hooks.context_warning_threshold, and no valid `
|
|
952
|
+
+ `critical value is below 0, so a warning of 0 would always fall back to the 35/25 `
|
|
953
|
+
+ `defaults. Use a value above 0.`);
|
|
954
|
+
}
|
|
955
|
+
if (kp === 'hooks.context_critical_threshold' && parsedValue === 100) {
|
|
956
|
+
error(`Invalid ${kp} '${val}'. 100 is in range but unusable: the monitor requires `
|
|
957
|
+
+ `hooks.context_critical_threshold < hooks.context_warning_threshold, and no valid `
|
|
958
|
+
+ `warning value is above 100, so a critical of 100 would always fall back to the `
|
|
959
|
+
+ `35/25 defaults. Use a value below 100.`);
|
|
960
|
+
}
|
|
961
|
+
}
|
|
838
962
|
// Fallow scope + profile enum validation (#3424)
|
|
839
963
|
const VALID_FALLOW_SCOPES = ['phase', 'repo'];
|
|
840
964
|
if (kp === 'code_quality.fallow.scope')
|
|
@@ -921,6 +1045,19 @@ function cmdConfigSet(cwd, keyPath, value, raw) {
|
|
|
921
1045
|
}
|
|
922
1046
|
}
|
|
923
1047
|
}
|
|
1048
|
+
if (dryRun) {
|
|
1049
|
+
const preview = previewConfigValue(cwd, kp, parsedValue);
|
|
1050
|
+
if ((0, secrets_cjs_1.isSecretKey)(kp)) {
|
|
1051
|
+
const masked = (0, secrets_cjs_1.maskSecret)(parsedValue);
|
|
1052
|
+
const maskedPrev = preview.previousValue === undefined
|
|
1053
|
+
? undefined
|
|
1054
|
+
: (0, secrets_cjs_1.maskSecret)(preview.previousValue);
|
|
1055
|
+
output({ dry_run: true, would_update: true, key: kp, value: masked, previousValue: maskedPrev, masked: true }, raw, `${kp}=${masked} (dry run)`);
|
|
1056
|
+
return;
|
|
1057
|
+
}
|
|
1058
|
+
output({ dry_run: true, would_update: true, key: kp, value: parsedValue, previousValue: preview.previousValue }, raw, `${kp}=${String(parsedValue)} (dry run)`);
|
|
1059
|
+
return;
|
|
1060
|
+
}
|
|
924
1061
|
const setConfigValueResult = setConfigValue(cwd, kp, parsedValue);
|
|
925
1062
|
// Mask secrets in both JSON and text output. The plaintext is written
|
|
926
1063
|
// to config.json (that's where secrets live on disk); the CLI output
|
|
@@ -1046,6 +1183,9 @@ function cmdConfigGet(cwd, keyPath, raw, defaultValue) {
|
|
|
1046
1183
|
}
|
|
1047
1184
|
error(`Key not found: ${kp}`, ERROR_REASON.CONFIG_KEY_NOT_FOUND);
|
|
1048
1185
|
}
|
|
1186
|
+
if (kp === 'planner.stall_detection_enabled') {
|
|
1187
|
+
current = resolvePlannerStallDetectionEnabled(current);
|
|
1188
|
+
}
|
|
1049
1189
|
// Never echo plaintext for sensitive keys via config-get. Plaintext lives
|
|
1050
1190
|
// in config.json on disk; the CLI surface always shows the masked form.
|
|
1051
1191
|
if ((0, secrets_cjs_1.isSecretKey)(kp)) {
|
|
@@ -1075,7 +1215,7 @@ function resolveFromRootConfig(cwd, kp) {
|
|
|
1075
1215
|
// diverges from planningRoot without a workstream and loadConfigResolved does NOT
|
|
1076
1216
|
// inherit root — matching the runtime's own `if (ws)` gate keeps the two surfaces
|
|
1077
1217
|
// from diverging on the project-scoped (non-workstream) case.
|
|
1078
|
-
if (!
|
|
1218
|
+
if (!resolveEnvWorkstream())
|
|
1079
1219
|
return { found: false, value: undefined };
|
|
1080
1220
|
const root = planningRoot(cwd);
|
|
1081
1221
|
const rootConfigPath = node_path_1.default.join(root, 'config.json');
|
|
@@ -1182,7 +1322,7 @@ function cmdConfigPath(cwd, _raw, workstreamContext = null) {
|
|
|
1182
1322
|
* (caller uses `await` which is safe on a sync return value).
|
|
1183
1323
|
*/
|
|
1184
1324
|
function cmdMigrateConfig(cwd, raw) {
|
|
1185
|
-
const ws =
|
|
1325
|
+
const ws = resolveEnvWorkstream();
|
|
1186
1326
|
// #3749: resolve the migration target through the project-aware resolver so
|
|
1187
1327
|
// GSD_PROJECT scopes the write; migrateOnDisk itself cannot (see its
|
|
1188
1328
|
// configPathOverride note).
|
|
@@ -1230,6 +1370,7 @@ function cmdMigrateConfig(cwd, raw) {
|
|
|
1230
1370
|
}
|
|
1231
1371
|
module.exports = {
|
|
1232
1372
|
VALID_CONFIG_KEYS,
|
|
1373
|
+
VALID_PHASE_ID_CONVENTIONS,
|
|
1233
1374
|
cmdConfigEnsureSection,
|
|
1234
1375
|
cmdConfigSet,
|
|
1235
1376
|
cmdConfigGet,
|
|
@@ -435,7 +435,7 @@ function cmdClassify(cwd, options = {}, raw) {
|
|
|
435
435
|
}
|
|
436
436
|
let resolvedPath;
|
|
437
437
|
try {
|
|
438
|
-
resolvedPath = (0, security_cjs_1.requireSafePath)(filePath, cwd, 'SUMMARY file',
|
|
438
|
+
resolvedPath = (0, security_cjs_1.requireSafePath)(filePath, cwd, 'SUMMARY file', security_cjs_1.PathAcceptance.AbsoluteInsideRoot);
|
|
439
439
|
}
|
|
440
440
|
catch (e) {
|
|
441
441
|
// Emit a structured command error instead of leaking a raw stack trace.
|