@therocketcode/gsd-core 1.4.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/plugin.json +23 -0
- package/GEMINI.md +53 -0
- package/LICENSE +21 -0
- package/README.ja-JP.md +125 -0
- package/README.ko-KR.md +125 -0
- package/README.md +144 -0
- package/README.pt-BR.md +125 -0
- package/README.zh-CN.md +125 -0
- package/agents/gsd-advisor-researcher.md +108 -0
- package/agents/gsd-ai-researcher.md +114 -0
- package/agents/gsd-assumptions-analyzer.md +105 -0
- package/agents/gsd-code-fixer.md +668 -0
- package/agents/gsd-code-reviewer.md +387 -0
- package/agents/gsd-codebase-mapper.md +853 -0
- package/agents/gsd-debug-session-manager.md +314 -0
- package/agents/gsd-debugger.md +1452 -0
- package/agents/gsd-doc-classifier.md +168 -0
- package/agents/gsd-doc-synthesizer.md +204 -0
- package/agents/gsd-doc-verifier.md +217 -0
- package/agents/gsd-doc-writer.md +616 -0
- package/agents/gsd-domain-researcher.md +147 -0
- package/agents/gsd-eval-auditor.md +191 -0
- package/agents/gsd-eval-planner.md +154 -0
- package/agents/gsd-executor.md +785 -0
- package/agents/gsd-framework-selector.md +160 -0
- package/agents/gsd-integration-checker.md +470 -0
- package/agents/gsd-intel-updater.md +342 -0
- package/agents/gsd-nyquist-auditor.md +203 -0
- package/agents/gsd-pattern-mapper.md +335 -0
- package/agents/gsd-phase-researcher.md +867 -0
- package/agents/gsd-plan-checker.md +978 -0
- package/agents/gsd-planner.md +1204 -0
- package/agents/gsd-project-researcher.md +611 -0
- package/agents/gsd-research-synthesizer.md +259 -0
- package/agents/gsd-roadmapper.md +688 -0
- package/agents/gsd-security-auditor.md +155 -0
- package/agents/gsd-ui-auditor.md +495 -0
- package/agents/gsd-ui-checker.md +309 -0
- package/agents/gsd-ui-researcher.md +374 -0
- package/agents/gsd-user-profiler.md +171 -0
- package/agents/gsd-verifier.md +923 -0
- package/assets/gsd-logo-2000-transparent.png +0 -0
- package/assets/gsd-logo-2000-transparent.svg +17 -0
- package/assets/gsd-logo-2000.png +0 -0
- package/assets/gsd-logo-2000.svg +21 -0
- package/assets/terminal.svg +68 -0
- package/bin/install.js +12726 -0
- package/bin/lib/ui-safety-gate.cjs +107 -0
- package/commands/gsd/add-tests.md +42 -0
- package/commands/gsd/ai-integration-phase.md +37 -0
- package/commands/gsd/audit-fix.md +34 -0
- package/commands/gsd/audit-milestone.md +37 -0
- package/commands/gsd/audit-uat.md +24 -0
- package/commands/gsd/autonomous.md +48 -0
- package/commands/gsd/capture.md +62 -0
- package/commands/gsd/cleanup.md +24 -0
- package/commands/gsd/code-review.md +59 -0
- package/commands/gsd/complete-milestone.md +143 -0
- package/commands/gsd/config.md +56 -0
- package/commands/gsd/debug.md +52 -0
- package/commands/gsd/discover-product.md +65 -0
- package/commands/gsd/discuss-phase.md +77 -0
- package/commands/gsd/docs-update.md +49 -0
- package/commands/gsd/eval-review.md +33 -0
- package/commands/gsd/execute-phase.md +66 -0
- package/commands/gsd/explore.md +27 -0
- package/commands/gsd/extract-learnings.md +23 -0
- package/commands/gsd/fast.md +31 -0
- package/commands/gsd/forensics.md +57 -0
- package/commands/gsd/graphify.md +204 -0
- package/commands/gsd/health.md +31 -0
- package/commands/gsd/help.md +28 -0
- package/commands/gsd/import.md +45 -0
- package/commands/gsd/inbox.md +39 -0
- package/commands/gsd/ingest-docs.md +42 -0
- package/commands/gsd/manager.md +45 -0
- package/commands/gsd/map-codebase.md +83 -0
- package/commands/gsd/milestone-summary.md +51 -0
- package/commands/gsd/model-domain.md +65 -0
- package/commands/gsd/mvp-phase.md +45 -0
- package/commands/gsd/new-milestone.md +45 -0
- package/commands/gsd/new-project.md +47 -0
- package/commands/gsd/ns-context.md +23 -0
- package/commands/gsd/ns-ideate.md +24 -0
- package/commands/gsd/ns-manage.md +29 -0
- package/commands/gsd/ns-project.md +22 -0
- package/commands/gsd/ns-review.md +26 -0
- package/commands/gsd/ns-workflow.md +28 -0
- package/commands/gsd/pause-work.md +43 -0
- package/commands/gsd/phase.md +56 -0
- package/commands/gsd/plan-phase.md +64 -0
- package/commands/gsd/plan-review-convergence.md +59 -0
- package/commands/gsd/pr-branch.md +26 -0
- package/commands/gsd/profile-user.md +46 -0
- package/commands/gsd/progress.md +48 -0
- package/commands/gsd/quick.md +174 -0
- package/commands/gsd/recommend-architecture.md +64 -0
- package/commands/gsd/resume-work.md +30 -0
- package/commands/gsd/review-backlog.md +63 -0
- package/commands/gsd/review.md +42 -0
- package/commands/gsd/secure-phase.md +36 -0
- package/commands/gsd/settings.md +29 -0
- package/commands/gsd/ship.md +24 -0
- package/commands/gsd/sketch.md +60 -0
- package/commands/gsd/spec-phase.md +63 -0
- package/commands/gsd/spike.md +57 -0
- package/commands/gsd/stats.md +20 -0
- package/commands/gsd/surface.md +155 -0
- package/commands/gsd/testing-strategy.md +65 -0
- package/commands/gsd/thread.md +24 -0
- package/commands/gsd/ui-phase.md +35 -0
- package/commands/gsd/ui-review.md +33 -0
- package/commands/gsd/ultraplan-phase.md +34 -0
- package/commands/gsd/undo.md +35 -0
- package/commands/gsd/update.md +49 -0
- package/commands/gsd/validate-phase.md +36 -0
- package/commands/gsd/verify-work.md +39 -0
- package/commands/gsd/workspace.md +52 -0
- package/commands/gsd/workstreams.md +70 -0
- package/gemini-extension.json +6 -0
- package/gsd-core/bin/check-latest-version.cjs +161 -0
- package/gsd-core/bin/gsd-tools.cjs +1928 -0
- package/gsd-core/bin/lib/active-workstream-store.cjs +291 -0
- package/gsd-core/bin/lib/adr-parser.cjs +399 -0
- package/gsd-core/bin/lib/agent-command-router.cjs +68 -0
- package/gsd-core/bin/lib/artifacts.cjs +51 -0
- package/gsd-core/bin/lib/audit.cjs +743 -0
- package/gsd-core/bin/lib/check-command-router.cjs +343 -0
- package/gsd-core/bin/lib/cjs-command-router-adapter.cjs +81 -0
- package/gsd-core/bin/lib/cli-exit.cjs +42 -0
- package/gsd-core/bin/lib/clock.cjs +95 -0
- package/gsd-core/bin/lib/clusters.cjs +132 -0
- package/gsd-core/bin/lib/code-review-flags.cjs +59 -0
- package/gsd-core/bin/lib/command-aliases.cjs +809 -0
- package/gsd-core/bin/lib/command-arg-projection.cjs +55 -0
- package/gsd-core/bin/lib/command-routing-hub.cjs +300 -0
- package/gsd-core/bin/lib/commands.cjs +1203 -0
- package/gsd-core/bin/lib/config-schema.cjs +29 -0
- package/gsd-core/bin/lib/config-types.cjs +19 -0
- package/gsd-core/bin/lib/config.cjs +738 -0
- package/gsd-core/bin/lib/configuration.cjs +239 -0
- package/gsd-core/bin/lib/context-utilization.cjs +48 -0
- package/gsd-core/bin/lib/core.cjs +2051 -0
- package/gsd-core/bin/lib/decisions.cjs +118 -0
- package/gsd-core/bin/lib/docs.cjs +252 -0
- package/gsd-core/bin/lib/drift.cjs +364 -0
- package/gsd-core/bin/lib/fallow-runner.cjs +115 -0
- package/gsd-core/bin/lib/frontmatter.cjs +442 -0
- package/gsd-core/bin/lib/gap-checker.cjs +257 -0
- package/gsd-core/bin/lib/graphify.cjs +496 -0
- package/gsd-core/bin/lib/gsd2-import.cjs +456 -0
- package/gsd-core/bin/lib/init-command-router.cjs +62 -0
- package/gsd-core/bin/lib/init.cjs +1815 -0
- package/gsd-core/bin/lib/install-profiles.cjs +584 -0
- package/gsd-core/bin/lib/installer-migration-authoring.cjs +122 -0
- package/gsd-core/bin/lib/installer-migration-report.cjs +350 -0
- package/gsd-core/bin/lib/installer-migrations/000-first-time-baseline.cjs +218 -0
- package/gsd-core/bin/lib/installer-migrations/001-legacy-orphan-files.cjs +48 -0
- package/gsd-core/bin/lib/installer-migrations/002-codex-legacy-hooks-json.cjs +94 -0
- package/gsd-core/bin/lib/installer-migrations/003-rename-get-shit-done-to-gsd-core.cjs +108 -0
- package/gsd-core/bin/lib/installer-migrations.cjs +823 -0
- package/gsd-core/bin/lib/intel.cjs +590 -0
- package/gsd-core/bin/lib/learnings.cjs +270 -0
- package/gsd-core/bin/lib/legacy-cleanup.cjs +253 -0
- package/gsd-core/bin/lib/milestone.cjs +373 -0
- package/gsd-core/bin/lib/model-catalog.cjs +154 -0
- package/gsd-core/bin/lib/model-profiles.cjs +24 -0
- package/gsd-core/bin/lib/observability/event.cjs +51 -0
- package/gsd-core/bin/lib/observability/logger.cjs +146 -0
- package/gsd-core/bin/lib/observability/redaction.cjs +48 -0
- package/gsd-core/bin/lib/package-identity.cjs +35 -0
- package/gsd-core/bin/lib/package-legitimacy.cjs +368 -0
- package/gsd-core/bin/lib/phase-command-router.cjs +189 -0
- package/gsd-core/bin/lib/phase-lifecycle.cjs +74 -0
- package/gsd-core/bin/lib/phase.cjs +1307 -0
- package/gsd-core/bin/lib/phases-command-router.cjs +43 -0
- package/gsd-core/bin/lib/plan-scan.cjs +91 -0
- package/gsd-core/bin/lib/planning-workspace.cjs +245 -0
- package/gsd-core/bin/lib/profile-output.cjs +1120 -0
- package/gsd-core/bin/lib/profile-pipeline.cjs +517 -0
- package/gsd-core/bin/lib/project-root.cjs +119 -0
- package/gsd-core/bin/lib/prompt-budget.cjs +305 -0
- package/gsd-core/bin/lib/research-provider.cjs +137 -0
- package/gsd-core/bin/lib/research-store.cjs +167 -0
- package/gsd-core/bin/lib/review-reviewer-selection.cjs +121 -0
- package/gsd-core/bin/lib/roadmap-command-router.cjs +166 -0
- package/gsd-core/bin/lib/roadmap-upgrade.cjs +476 -0
- package/gsd-core/bin/lib/roadmap.cjs +600 -0
- package/gsd-core/bin/lib/runtime-artifact-layout.cjs +312 -0
- package/gsd-core/bin/lib/runtime-config-adapter-registry.cjs +56 -0
- package/gsd-core/bin/lib/runtime-homes.cjs +190 -0
- package/gsd-core/bin/lib/runtime-name-policy.cjs +96 -0
- package/gsd-core/bin/lib/runtime-slash.cjs +119 -0
- package/gsd-core/bin/lib/schema-detect.cjs +159 -0
- package/gsd-core/bin/lib/secrets.cjs +34 -0
- package/gsd-core/bin/lib/security.cjs +480 -0
- package/gsd-core/bin/lib/semver-compare.cjs +42 -0
- package/gsd-core/bin/lib/shell-command-projection.cjs +533 -0
- package/gsd-core/bin/lib/state-command-router.cjs +160 -0
- package/gsd-core/bin/lib/state-document.cjs +259 -0
- package/gsd-core/bin/lib/state.cjs +2010 -0
- package/gsd-core/bin/lib/surface.cjs +449 -0
- package/gsd-core/bin/lib/task-command-router.cjs +85 -0
- package/gsd-core/bin/lib/template.cjs +237 -0
- package/gsd-core/bin/lib/uat.cjs +297 -0
- package/gsd-core/bin/lib/ui-safety-gate.cjs +98 -0
- package/gsd-core/bin/lib/update-context.cjs +218 -0
- package/gsd-core/bin/lib/validate-command-router.cjs +91 -0
- package/gsd-core/bin/lib/validate.cjs +112 -0
- package/gsd-core/bin/lib/verification-command-router.cjs +31 -0
- package/gsd-core/bin/lib/verification.cjs +193 -0
- package/gsd-core/bin/lib/verify-command-router.cjs +44 -0
- package/gsd-core/bin/lib/verify.cjs +1451 -0
- package/gsd-core/bin/lib/workstream-inventory-builder.cjs +81 -0
- package/gsd-core/bin/lib/workstream-inventory.cjs +147 -0
- package/gsd-core/bin/lib/workstream-name-policy.cjs +91 -0
- package/gsd-core/bin/lib/workstream.cjs +380 -0
- package/gsd-core/bin/lib/worktree-base-ref.cjs +325 -0
- package/gsd-core/bin/lib/worktree-safety.cjs +943 -0
- package/gsd-core/bin/shared/config-defaults.manifest.json +98 -0
- package/gsd-core/bin/shared/config-schema.manifest.json +192 -0
- package/gsd-core/bin/shared/model-catalog.json +149 -0
- package/gsd-core/bin/shared/runtime-aliases.manifest.json +75 -0
- package/gsd-core/bin/verify-reapply-patches.cjs +349 -0
- package/gsd-core/contexts/dev.md +21 -0
- package/gsd-core/contexts/research.md +22 -0
- package/gsd-core/contexts/review.md +23 -0
- package/gsd-core/references/agent-contracts.md +79 -0
- package/gsd-core/references/ai-evals.md +156 -0
- package/gsd-core/references/ai-frameworks.md +186 -0
- package/gsd-core/references/architecture-decision.md +74 -0
- package/gsd-core/references/artifact-types.md +131 -0
- package/gsd-core/references/auth-in-tests.md +91 -0
- package/gsd-core/references/autonomous-smart-discuss.md +277 -0
- package/gsd-core/references/checkpoints.md +814 -0
- package/gsd-core/references/common-bug-patterns.md +114 -0
- package/gsd-core/references/context-budget.md +85 -0
- package/gsd-core/references/continuation-format.md +253 -0
- package/gsd-core/references/db-test-isolation.md +54 -0
- package/gsd-core/references/debugger-philosophy.md +76 -0
- package/gsd-core/references/decimal-phase-calculation.md +64 -0
- package/gsd-core/references/doc-conflict-engine.md +91 -0
- package/gsd-core/references/domain-modeling.md +80 -0
- package/gsd-core/references/domain-probes.md +125 -0
- package/gsd-core/references/e2e-tiering.md +35 -0
- package/gsd-core/references/execute-mvp-tdd.md +81 -0
- package/gsd-core/references/executor-examples.md +110 -0
- package/gsd-core/references/few-shot-examples/plan-checker.md +73 -0
- package/gsd-core/references/few-shot-examples/verifier.md +109 -0
- package/gsd-core/references/flaky-test-checklist.md +22 -0
- package/gsd-core/references/gate-prompts.md +100 -0
- package/gsd-core/references/gates.md +70 -0
- package/gsd-core/references/git-integration.md +298 -0
- package/gsd-core/references/git-planning-commit.md +40 -0
- package/gsd-core/references/ios-scaffold.md +123 -0
- package/gsd-core/references/mandatory-initial-read.md +2 -0
- package/gsd-core/references/model-profile-resolution.md +38 -0
- package/gsd-core/references/model-profiles.md +245 -0
- package/gsd-core/references/mvp-concepts.md +49 -0
- package/gsd-core/references/phase-argument-parsing.md +61 -0
- package/gsd-core/references/planner-antipatterns.md +89 -0
- package/gsd-core/references/planner-chunked.md +49 -0
- package/gsd-core/references/planner-gap-closure.md +62 -0
- package/gsd-core/references/planner-graphify-auto-update.md +67 -0
- package/gsd-core/references/planner-human-verify-mode.md +57 -0
- package/gsd-core/references/planner-interface-context.md +62 -0
- package/gsd-core/references/planner-load-graph-context.md +36 -0
- package/gsd-core/references/planner-mvp-mode.md +53 -0
- package/gsd-core/references/planner-reviews.md +39 -0
- package/gsd-core/references/planner-revision.md +87 -0
- package/gsd-core/references/planner-source-audit.md +73 -0
- package/gsd-core/references/planning-config.md +473 -0
- package/gsd-core/references/product-discovery.md +49 -0
- package/gsd-core/references/project-skills-discovery.md +19 -0
- package/gsd-core/references/questioning.md +162 -0
- package/gsd-core/references/realistic-test-data.md +44 -0
- package/gsd-core/references/research-documentation-lookup.md +29 -0
- package/gsd-core/references/research-philosophy.md +29 -0
- package/gsd-core/references/research-verification-protocol.md +27 -0
- package/gsd-core/references/revision-loop.md +97 -0
- package/gsd-core/references/scout-codebase.md +51 -0
- package/gsd-core/references/skeleton-template.md +48 -0
- package/gsd-core/references/sketch-interactivity.md +41 -0
- package/gsd-core/references/sketch-theme-system.md +94 -0
- package/gsd-core/references/sketch-tooling.md +45 -0
- package/gsd-core/references/sketch-variant-patterns.md +81 -0
- package/gsd-core/references/spidr-splitting.md +69 -0
- package/gsd-core/references/tdd.md +330 -0
- package/gsd-core/references/test-containers.md +55 -0
- package/gsd-core/references/test-strategy.md +75 -0
- package/gsd-core/references/thinking-models-debug.md +44 -0
- package/gsd-core/references/thinking-models-execution.md +50 -0
- package/gsd-core/references/thinking-models-planning.md +62 -0
- package/gsd-core/references/thinking-models-research.md +50 -0
- package/gsd-core/references/thinking-models-verification.md +55 -0
- package/gsd-core/references/thinking-partner.md +96 -0
- package/gsd-core/references/ui-brand.md +162 -0
- package/gsd-core/references/universal-anti-patterns.md +63 -0
- package/gsd-core/references/user-profiling.md +681 -0
- package/gsd-core/references/user-story-template.md +58 -0
- package/gsd-core/references/verification-overrides.md +227 -0
- package/gsd-core/references/verification-patterns.md +612 -0
- package/gsd-core/references/verify-mvp-mode.md +85 -0
- package/gsd-core/references/workstream-flag.md +111 -0
- package/gsd-core/references/worktree-branch-check.md +38 -0
- package/gsd-core/references/worktree-path-safety.md +67 -0
- package/gsd-core/templates/AI-SPEC.md +246 -0
- package/gsd-core/templates/DEBUG.md +169 -0
- package/gsd-core/templates/README.md +77 -0
- package/gsd-core/templates/SECURITY.md +61 -0
- package/gsd-core/templates/UAT.md +265 -0
- package/gsd-core/templates/UI-SPEC.md +100 -0
- package/gsd-core/templates/VALIDATION.md +76 -0
- package/gsd-core/templates/adr.md +58 -0
- package/gsd-core/templates/claude-md.md +145 -0
- package/gsd-core/templates/codebase/architecture.md +255 -0
- package/gsd-core/templates/codebase/concerns.md +310 -0
- package/gsd-core/templates/codebase/conventions.md +307 -0
- package/gsd-core/templates/codebase/integrations.md +280 -0
- package/gsd-core/templates/codebase/stack.md +186 -0
- package/gsd-core/templates/codebase/structure.md +285 -0
- package/gsd-core/templates/codebase/testing.md +480 -0
- package/gsd-core/templates/config.json +62 -0
- package/gsd-core/templates/context.md +352 -0
- package/gsd-core/templates/continue-here.md +78 -0
- package/gsd-core/templates/copilot-instructions.md +7 -0
- package/gsd-core/templates/debug-subagent-prompt.md +91 -0
- package/gsd-core/templates/dev-preferences.md +21 -0
- package/gsd-core/templates/discovery.md +146 -0
- package/gsd-core/templates/discussion-log.md +63 -0
- package/gsd-core/templates/domain-model.md +54 -0
- package/gsd-core/templates/milestone-archive.md +123 -0
- package/gsd-core/templates/milestone.md +115 -0
- package/gsd-core/templates/phase-prompt.md +610 -0
- package/gsd-core/templates/planner-subagent-prompt.md +117 -0
- package/gsd-core/templates/product-brief.md +55 -0
- package/gsd-core/templates/project.md +186 -0
- package/gsd-core/templates/requirements.md +231 -0
- package/gsd-core/templates/research-project/ARCHITECTURE.md +204 -0
- package/gsd-core/templates/research-project/FEATURES.md +147 -0
- package/gsd-core/templates/research-project/PITFALLS.md +200 -0
- package/gsd-core/templates/research-project/STACK.md +120 -0
- package/gsd-core/templates/research-project/SUMMARY.md +170 -0
- package/gsd-core/templates/research.md +592 -0
- package/gsd-core/templates/retrospective.md +54 -0
- package/gsd-core/templates/roadmap.md +202 -0
- package/gsd-core/templates/spec.md +307 -0
- package/gsd-core/templates/state.md +195 -0
- package/gsd-core/templates/summary-complex.md +59 -0
- package/gsd-core/templates/summary-minimal.md +41 -0
- package/gsd-core/templates/summary-standard.md +48 -0
- package/gsd-core/templates/summary.md +248 -0
- package/gsd-core/templates/test-strategy.md +50 -0
- package/gsd-core/templates/user-profile.md +146 -0
- package/gsd-core/templates/user-setup.md +311 -0
- package/gsd-core/templates/verification-report.md +322 -0
- package/gsd-core/workflows/_runtime-launcher.snippet.sh +1 -0
- package/gsd-core/workflows/add-backlog.md +91 -0
- package/gsd-core/workflows/add-phase.md +113 -0
- package/gsd-core/workflows/add-tests.md +355 -0
- package/gsd-core/workflows/add-todo.md +161 -0
- package/gsd-core/workflows/ai-integration-phase.md +295 -0
- package/gsd-core/workflows/analyze-dependencies.md +96 -0
- package/gsd-core/workflows/audit-fix.md +178 -0
- package/gsd-core/workflows/audit-milestone.md +360 -0
- package/gsd-core/workflows/audit-uat.md +110 -0
- package/gsd-core/workflows/autonomous.md +797 -0
- package/gsd-core/workflows/check-todos.md +180 -0
- package/gsd-core/workflows/cleanup.md +195 -0
- package/gsd-core/workflows/code-review-fix.md +502 -0
- package/gsd-core/workflows/code-review.md +658 -0
- package/gsd-core/workflows/complete-milestone.md +855 -0
- package/gsd-core/workflows/debug.md +237 -0
- package/gsd-core/workflows/diagnose-issues.md +245 -0
- package/gsd-core/workflows/discover-product.md +112 -0
- package/gsd-core/workflows/discovery-phase.md +291 -0
- package/gsd-core/workflows/discuss-phase/modes/advisor.md +176 -0
- package/gsd-core/workflows/discuss-phase/modes/all.md +28 -0
- package/gsd-core/workflows/discuss-phase/modes/analyze.md +44 -0
- package/gsd-core/workflows/discuss-phase/modes/auto.md +57 -0
- package/gsd-core/workflows/discuss-phase/modes/batch.md +52 -0
- package/gsd-core/workflows/discuss-phase/modes/chain.md +98 -0
- package/gsd-core/workflows/discuss-phase/modes/default.md +141 -0
- package/gsd-core/workflows/discuss-phase/modes/power.md +44 -0
- package/gsd-core/workflows/discuss-phase/modes/text.md +55 -0
- package/gsd-core/workflows/discuss-phase/templates/checkpoint.json +18 -0
- package/gsd-core/workflows/discuss-phase/templates/context.md +136 -0
- package/gsd-core/workflows/discuss-phase/templates/discussion-log.md +50 -0
- package/gsd-core/workflows/discuss-phase-assumptions.md +675 -0
- package/gsd-core/workflows/discuss-phase-power.md +291 -0
- package/gsd-core/workflows/discuss-phase.md +499 -0
- package/gsd-core/workflows/do.md +111 -0
- package/gsd-core/workflows/docs-update.md +1176 -0
- package/gsd-core/workflows/edit-phase.md +295 -0
- package/gsd-core/workflows/eval-review.md +156 -0
- package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +95 -0
- package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +94 -0
- package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +117 -0
- package/gsd-core/workflows/execute-phase.md +1752 -0
- package/gsd-core/workflows/execute-plan.md +526 -0
- package/gsd-core/workflows/explore.md +146 -0
- package/gsd-core/workflows/extract-learnings.md +243 -0
- package/gsd-core/workflows/fast.md +124 -0
- package/gsd-core/workflows/forensics.md +279 -0
- package/gsd-core/workflows/graduation.md +196 -0
- package/gsd-core/workflows/health.md +224 -0
- package/gsd-core/workflows/help/modes/brief.md +22 -0
- package/gsd-core/workflows/help/modes/default.md +50 -0
- package/gsd-core/workflows/help/modes/full.md +789 -0
- package/gsd-core/workflows/help/modes/topic.md +74 -0
- package/gsd-core/workflows/help.md +24 -0
- package/gsd-core/workflows/import.md +256 -0
- package/gsd-core/workflows/inbox.md +387 -0
- package/gsd-core/workflows/ingest-docs.md +340 -0
- package/gsd-core/workflows/insert-phase.md +152 -0
- package/gsd-core/workflows/list-phase-assumptions.md +178 -0
- package/gsd-core/workflows/list-workspaces.md +57 -0
- package/gsd-core/workflows/manager.md +393 -0
- package/gsd-core/workflows/map-codebase.md +446 -0
- package/gsd-core/workflows/milestone-summary.md +224 -0
- package/gsd-core/workflows/model-domain.md +162 -0
- package/gsd-core/workflows/mvp-phase.md +222 -0
- package/gsd-core/workflows/new-milestone.md +635 -0
- package/gsd-core/workflows/new-project.md +1555 -0
- package/gsd-core/workflows/new-workspace.md +240 -0
- package/gsd-core/workflows/next.md +299 -0
- package/gsd-core/workflows/node-repair.md +92 -0
- package/gsd-core/workflows/note.md +158 -0
- package/gsd-core/workflows/pause-work.md +244 -0
- package/gsd-core/workflows/plan-milestone-gaps.md +281 -0
- package/gsd-core/workflows/plan-phase.md +1814 -0
- package/gsd-core/workflows/plan-review-convergence.md +346 -0
- package/gsd-core/workflows/plant-seed.md +230 -0
- package/gsd-core/workflows/pr-branch.md +157 -0
- package/gsd-core/workflows/profile-user.md +453 -0
- package/gsd-core/workflows/progress.md +699 -0
- package/gsd-core/workflows/quick.md +1017 -0
- package/gsd-core/workflows/reapply-patches.md +426 -0
- package/gsd-core/workflows/recommend-architecture.md +135 -0
- package/gsd-core/workflows/remove-phase.md +156 -0
- package/gsd-core/workflows/remove-workspace.md +108 -0
- package/gsd-core/workflows/resume-project.md +332 -0
- package/gsd-core/workflows/review.md +748 -0
- package/gsd-core/workflows/scan.md +107 -0
- package/gsd-core/workflows/secure-phase.md +182 -0
- package/gsd-core/workflows/session-report.md +146 -0
- package/gsd-core/workflows/settings-advanced.md +810 -0
- package/gsd-core/workflows/settings-integrations.md +312 -0
- package/gsd-core/workflows/settings.md +566 -0
- package/gsd-core/workflows/ship.md +405 -0
- package/gsd-core/workflows/sketch-wrap-up.md +286 -0
- package/gsd-core/workflows/sketch.md +361 -0
- package/gsd-core/workflows/spec-phase.md +263 -0
- package/gsd-core/workflows/spike-wrap-up.md +307 -0
- package/gsd-core/workflows/spike.md +453 -0
- package/gsd-core/workflows/stats.md +80 -0
- package/gsd-core/workflows/sync-skills.md +182 -0
- package/gsd-core/workflows/testing-strategy.md +122 -0
- package/gsd-core/workflows/thread.md +222 -0
- package/gsd-core/workflows/transition.md +694 -0
- package/gsd-core/workflows/ui-phase.md +328 -0
- package/gsd-core/workflows/ui-review.md +193 -0
- package/gsd-core/workflows/ultraplan-phase.md +199 -0
- package/gsd-core/workflows/undo.md +314 -0
- package/gsd-core/workflows/update.md +496 -0
- package/gsd-core/workflows/validate-phase.md +181 -0
- package/gsd-core/workflows/verify-phase.md +544 -0
- package/gsd-core/workflows/verify-work.md +781 -0
- package/hooks/dist/gsd-check-update-worker.js +108 -0
- package/hooks/dist/gsd-check-update.js +66 -0
- package/hooks/dist/gsd-config-reload.js +133 -0
- package/hooks/dist/gsd-context-monitor.js +195 -0
- package/hooks/dist/gsd-cursor-post-tool.js +75 -0
- package/hooks/dist/gsd-cursor-session-start.js +52 -0
- package/hooks/dist/gsd-graphify-update.sh +158 -0
- package/hooks/dist/gsd-phase-boundary.sh +47 -0
- package/hooks/dist/gsd-prompt-guard.js +97 -0
- package/hooks/dist/gsd-read-guard.js +101 -0
- package/hooks/dist/gsd-read-injection-scanner.js +203 -0
- package/hooks/dist/gsd-session-state.sh +59 -0
- package/hooks/dist/gsd-statusline.js +566 -0
- package/hooks/dist/gsd-update-banner.js +138 -0
- package/hooks/dist/gsd-validate-commit.sh +57 -0
- package/hooks/dist/gsd-workflow-guard.js +167 -0
- package/hooks/dist/gsd-worktree-path-guard.js +169 -0
- package/hooks/dist/lib/git-cmd.js +150 -0
- package/hooks/dist/lib/gsd-graphify-rebuild.sh +65 -0
- package/hooks/dist/managed-hooks-registry.cjs +38 -0
- package/hooks/gsd-check-update-worker.js +108 -0
- package/hooks/gsd-check-update.js +66 -0
- package/hooks/gsd-config-reload.js +133 -0
- package/hooks/gsd-context-monitor.js +195 -0
- package/hooks/gsd-cursor-post-tool.js +75 -0
- package/hooks/gsd-cursor-session-start.js +52 -0
- package/hooks/gsd-graphify-update.sh +158 -0
- package/hooks/gsd-phase-boundary.sh +47 -0
- package/hooks/gsd-prompt-guard.js +97 -0
- package/hooks/gsd-read-guard.js +101 -0
- package/hooks/gsd-read-injection-scanner.js +203 -0
- package/hooks/gsd-session-state.sh +59 -0
- package/hooks/gsd-statusline.js +566 -0
- package/hooks/gsd-update-banner.js +138 -0
- package/hooks/gsd-validate-commit.sh +57 -0
- package/hooks/gsd-workflow-guard.js +167 -0
- package/hooks/gsd-worktree-path-guard.js +169 -0
- package/hooks/hooks.json +69 -0
- package/hooks/lib/git-cmd.js +150 -0
- package/hooks/lib/gsd-graphify-rebuild.sh +65 -0
- package/hooks/managed-hooks-registry.cjs +38 -0
- package/package.json +115 -0
- package/scripts/affected-tests-lib.cjs +542 -0
- package/scripts/audit-workflow-script-paths.cjs +73 -0
- package/scripts/base64-scan.sh +351 -0
- package/scripts/build-hooks.js +247 -0
- package/scripts/changeset/README.md +129 -0
- package/scripts/changeset/cli.cjs +590 -0
- package/scripts/changeset/github-release-notes.cjs +199 -0
- package/scripts/changeset/lint.cjs +111 -0
- package/scripts/changeset/new.cjs +137 -0
- package/scripts/changeset/parse.cjs +114 -0
- package/scripts/changeset/render.cjs +34 -0
- package/scripts/changeset/serialize.cjs +130 -0
- package/scripts/check-alias-drift.cjs +114 -0
- package/scripts/check-env.cjs +312 -0
- package/scripts/check-npm-integrity.cjs +215 -0
- package/scripts/ci-guard-runner.cjs +22 -0
- package/scripts/ci-prepare-test-scope.cjs +51 -0
- package/scripts/ci-rebase-check.cjs +86 -0
- package/scripts/ci-test-scope.cjs +431 -0
- package/scripts/command-contract-helpers.cjs +64 -0
- package/scripts/diff-touches-shipped-paths.cjs +155 -0
- package/scripts/fix-slash-commands.cjs +147 -0
- package/scripts/gen-inventory-manifest.cjs +115 -0
- package/scripts/gen-research-agents.cjs +276 -0
- package/scripts/generate-package-identity.cjs +125 -0
- package/scripts/issue-dedupe.cjs +278 -0
- package/scripts/lib/allowlist-ratchet.cjs +136 -0
- package/scripts/lib/cli-exit.cjs +56 -0
- package/scripts/lint-command-contract.cjs +114 -0
- package/scripts/lint-descriptions.cjs +87 -0
- package/scripts/lint-docs-required.cjs +222 -0
- package/scripts/lint-legacy-dir-name.cjs +160 -0
- package/scripts/lint-package-identity-drift.cjs +141 -0
- package/scripts/lint-pr-check-project-dir.cjs +99 -0
- package/scripts/lint-shell-command-projection-drift.cjs +62 -0
- package/scripts/lint-skill-deps.cjs +185 -0
- package/scripts/lint-test-file-count.allowlist.json +135 -0
- package/scripts/lint-test-file-count.cjs +246 -0
- package/scripts/mutation-matrix.cjs +222 -0
- package/scripts/pr-template-policy.cjs +268 -0
- package/scripts/prompt-injection-scan.sh +207 -0
- package/scripts/release-notes/discord-release-summary.cjs +373 -0
- package/scripts/release-notes/format-github-release-notes.cjs +261 -0
- package/scripts/release-tarball-smoke.cjs +629 -0
- package/scripts/research-profiles.cjs +149 -0
- package/scripts/run-affected-tests.cjs +7 -0
- package/scripts/run-cross-platform-tests.cjs +67 -0
- package/scripts/run-tests.cjs +315 -0
- package/scripts/secret-scan-lint.sh +231 -0
- package/scripts/secret-scan.sh +358 -0
- package/scripts/setup-branch-protection.sh +236 -0
- package/scripts/strip-prose-atrefs.cjs +106 -0
- package/scripts/sync-manifest-versions.cjs +119 -0
- package/scripts/sync-rulesets.sh +34 -0
- package/scripts/sync-runtime-launcher.cjs +399 -0
- package/scripts/test-failure-reasons.cjs +34 -0
- package/scripts/verify-npm-publish.cjs +240 -0
- package/scripts/workflow-policy.cjs +450 -0
|
@@ -0,0 +1,738 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Config — Planning config CRUD operations
|
|
4
|
+
*
|
|
5
|
+
* ADR-457 build-at-publish: the hand-written bin/lib/config.cjs collapsed
|
|
6
|
+
* to a TypeScript source of truth. Behaviour is preserved byte-for-behaviour
|
|
7
|
+
* from the prior hand-written .cjs; only strict types are added.
|
|
8
|
+
*/
|
|
9
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
10
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
11
|
+
};
|
|
12
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
13
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
14
|
+
const node_os_1 = __importDefault(require("node:os"));
|
|
15
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
16
|
+
const core = require("./core.cjs");
|
|
17
|
+
const { output, error, ERROR_REASON, CONFIG_DEFAULTS } = core;
|
|
18
|
+
const shell_command_projection_cjs_1 = require("./shell-command-projection.cjs");
|
|
19
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
20
|
+
const planningWorkspace = require("./planning-workspace.cjs");
|
|
21
|
+
const { planningDir, withPlanningLock } = planningWorkspace;
|
|
22
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
23
|
+
const modelProfiles = require("./model-profiles.cjs");
|
|
24
|
+
const { VALID_PROFILES, getAgentToModelMapForProfile, formatAgentToModelMapAsTable } = modelProfiles;
|
|
25
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
26
|
+
const configSchema = require("./config-schema.cjs");
|
|
27
|
+
const { VALID_CONFIG_KEYS, isValidConfigKey } = configSchema;
|
|
28
|
+
const secrets_cjs_1 = require("./secrets.cjs");
|
|
29
|
+
const review_reviewer_selection_cjs_1 = require("./review-reviewer-selection.cjs");
|
|
30
|
+
const configuration_cjs_1 = require("./configuration.cjs");
|
|
31
|
+
// ─── Constants ────────────────────────────────────────────────────────────────
|
|
32
|
+
const CONFIG_KEY_SUGGESTIONS = {
|
|
33
|
+
'workflow.nyquist_validation_enabled': 'workflow.nyquist_validation',
|
|
34
|
+
'agents.nyquist_validation_enabled': 'workflow.nyquist_validation',
|
|
35
|
+
'nyquist.validation_enabled': 'workflow.nyquist_validation',
|
|
36
|
+
'hooks.research_questions': 'workflow.research_before_questions',
|
|
37
|
+
'workflow.research_questions': 'workflow.research_before_questions',
|
|
38
|
+
'workflow.codereview': 'workflow.code_review',
|
|
39
|
+
'workflow.review_command': 'workflow.code_review_command',
|
|
40
|
+
'workflow.review': 'workflow.code_review',
|
|
41
|
+
'workflow.code_review_level': 'workflow.code_review_depth',
|
|
42
|
+
'workflow.review_depth': 'workflow.code_review_depth',
|
|
43
|
+
'review.model': 'review.models.<cli-name>',
|
|
44
|
+
'sub_repos': 'planning.sub_repos',
|
|
45
|
+
'plan_checker': 'workflow.plan_check',
|
|
46
|
+
};
|
|
47
|
+
const SHIP_PR_BODY_SECTION_KEYS = new Set(['heading', 'enabled', 'source', 'fallback', 'template']);
|
|
48
|
+
const SHIP_PR_BODY_TEMPLATE_TOKENS = new Set([
|
|
49
|
+
'phase_number',
|
|
50
|
+
'phase_name',
|
|
51
|
+
'phase_dir',
|
|
52
|
+
'base_branch',
|
|
53
|
+
'padded_phase',
|
|
54
|
+
]);
|
|
55
|
+
const SHIP_PR_BODY_SOURCE_RE = /^(ROADMAP|PLAN|SUMMARY|VERIFICATION|STATE|REQUIREMENTS|CONTEXT)\.md\s+##\s+[^\r\n#][^\r\n]*$/;
|
|
56
|
+
/**
|
|
57
|
+
* Schema-level defaults for well-known config keys.
|
|
58
|
+
* When a key is absent from config.json and no --default flag was supplied,
|
|
59
|
+
* cmdConfigGet checks here before emitting "Key not found".
|
|
60
|
+
*/
|
|
61
|
+
const SCHEMA_DEFAULTS = {
|
|
62
|
+
'context_window': 200000,
|
|
63
|
+
'executor.stall_detect_interval_minutes': 5,
|
|
64
|
+
'executor.stall_threshold_minutes': 10,
|
|
65
|
+
'git.create_tag': true,
|
|
66
|
+
};
|
|
67
|
+
// ─── Validation helpers ───────────────────────────────────────────────────────
|
|
68
|
+
function validateKnownConfigKeyPath(keyPath) {
|
|
69
|
+
const suggested = CONFIG_KEY_SUGGESTIONS[keyPath];
|
|
70
|
+
if (suggested) {
|
|
71
|
+
error(`Unknown config key: ${keyPath}. Did you mean ${suggested}?`, ERROR_REASON.CONFIG_INVALID_KEY);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
function validateShipPrBodySections(value) {
|
|
75
|
+
if (!Array.isArray(value)) {
|
|
76
|
+
error('Invalid ship.pr_body_sections value. Expected a JSON array of section objects.');
|
|
77
|
+
}
|
|
78
|
+
value.forEach((section, index) => {
|
|
79
|
+
const prefix = `Invalid ship.pr_body_sections[${index}]`;
|
|
80
|
+
if (!section || typeof section !== 'object' || Array.isArray(section)) {
|
|
81
|
+
error(`${prefix}. Expected an object.`);
|
|
82
|
+
}
|
|
83
|
+
const sectionObj = section;
|
|
84
|
+
const unknownKeys = Object.keys(sectionObj).filter((key) => !SHIP_PR_BODY_SECTION_KEYS.has(key));
|
|
85
|
+
if (unknownKeys.length > 0) {
|
|
86
|
+
error(`${prefix}. Unknown field(s): ${unknownKeys.join(', ')}.`);
|
|
87
|
+
}
|
|
88
|
+
if (typeof sectionObj['heading'] !== 'string' || sectionObj['heading'].trim() === '') {
|
|
89
|
+
error(`${prefix}. heading must be a non-empty string.`);
|
|
90
|
+
}
|
|
91
|
+
if (/[\r\n]/.test(sectionObj['heading'])) {
|
|
92
|
+
error(`${prefix}. heading must be a single line.`);
|
|
93
|
+
}
|
|
94
|
+
if ('enabled' in sectionObj && typeof sectionObj['enabled'] !== 'boolean') {
|
|
95
|
+
error(`${prefix}. enabled must be true or false.`);
|
|
96
|
+
}
|
|
97
|
+
for (const field of ['source', 'fallback', 'template']) {
|
|
98
|
+
if (field in sectionObj && typeof sectionObj[field] !== 'string') {
|
|
99
|
+
error(`${prefix}. ${field} must be a string.`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
const hasContent = ['source', 'fallback', 'template'].some((field) => {
|
|
103
|
+
const v = sectionObj[field];
|
|
104
|
+
return typeof v === 'string' && v.trim() !== '';
|
|
105
|
+
});
|
|
106
|
+
if (!hasContent) {
|
|
107
|
+
error(`${prefix}. Provide at least one of source, fallback, or template.`);
|
|
108
|
+
}
|
|
109
|
+
if (typeof sectionObj['source'] === 'string' && sectionObj['source'].trim() !== '') {
|
|
110
|
+
const selectors = sectionObj['source'].split('||').map((selector) => selector.trim()).filter(Boolean);
|
|
111
|
+
if (selectors.length === 0 || selectors.some((selector) => !SHIP_PR_BODY_SOURCE_RE.test(selector))) {
|
|
112
|
+
error(`${prefix}. source must use selectors like "PLAN.md ## Risks", separated with "||".`);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
if (typeof sectionObj['template'] === 'string') {
|
|
116
|
+
const tokens = sectionObj['template'].matchAll(/\{([a-zA-Z][a-zA-Z0-9_]*)\}/g);
|
|
117
|
+
for (const match of tokens) {
|
|
118
|
+
if (!SHIP_PR_BODY_TEMPLATE_TOKENS.has(match[1])) {
|
|
119
|
+
error(`${prefix}. Unsupported template token: {${match[1]}}.`);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
// ─── Core config operations ───────────────────────────────────────────────────
|
|
126
|
+
/**
|
|
127
|
+
* Build a fully-materialized config object for a new project.
|
|
128
|
+
*
|
|
129
|
+
* Merges (increasing priority):
|
|
130
|
+
* 1. Hardcoded defaults — every key that loadConfig() resolves, plus mode/granularity
|
|
131
|
+
* 2. User-level defaults from ~/.gsd/defaults.json (if present)
|
|
132
|
+
* 3. userChoices — the settings the user explicitly selected during /gsd:new-project
|
|
133
|
+
*
|
|
134
|
+
* Uses the canonical `git` namespace for branching keys (consistent with VALID_CONFIG_KEYS
|
|
135
|
+
* and the settings workflow). loadConfig() handles both flat and nested formats, so this
|
|
136
|
+
* is backward-compatible with existing projects that have flat keys.
|
|
137
|
+
*
|
|
138
|
+
* Returns a plain object — does NOT write any files.
|
|
139
|
+
*/
|
|
140
|
+
function buildNewProjectConfig(userChoices) {
|
|
141
|
+
const choices = userChoices || {};
|
|
142
|
+
const homedir = node_os_1.default.homedir();
|
|
143
|
+
// Detect API key availability
|
|
144
|
+
const braveKeyFile = node_path_1.default.join(homedir, '.gsd', 'brave_api_key');
|
|
145
|
+
const hasBraveSearch = !!(process.env['BRAVE_API_KEY'] || node_fs_1.default.existsSync(braveKeyFile));
|
|
146
|
+
const firecrawlKeyFile = node_path_1.default.join(homedir, '.gsd', 'firecrawl_api_key');
|
|
147
|
+
const hasFirecrawl = !!(process.env['FIRECRAWL_API_KEY'] || node_fs_1.default.existsSync(firecrawlKeyFile));
|
|
148
|
+
const exaKeyFile = node_path_1.default.join(homedir, '.gsd', 'exa_api_key');
|
|
149
|
+
const hasExaSearch = !!(process.env['EXA_API_KEY'] || node_fs_1.default.existsSync(exaKeyFile));
|
|
150
|
+
const tavilyKeyFile = node_path_1.default.join(homedir, '.gsd', 'tavily_api_key');
|
|
151
|
+
const hasTavilySearch = !!(process.env['TAVILY_API_KEY'] || node_fs_1.default.existsSync(tavilyKeyFile));
|
|
152
|
+
const refKeyFile = node_path_1.default.join(homedir, '.gsd', 'ref_api_key');
|
|
153
|
+
const hasRefSearch = !!(process.env['REF_API_KEY'] || node_fs_1.default.existsSync(refKeyFile));
|
|
154
|
+
const perplexityKeyFile = node_path_1.default.join(homedir, '.gsd', 'perplexity_api_key');
|
|
155
|
+
const hasPerplexity = !!(process.env['PERPLEXITY_API_KEY'] || node_fs_1.default.existsSync(perplexityKeyFile));
|
|
156
|
+
const jinaKeyFile = node_path_1.default.join(homedir, '.gsd', 'jina_api_key');
|
|
157
|
+
const hasJina = !!(process.env['JINA_API_KEY'] || node_fs_1.default.existsSync(jinaKeyFile));
|
|
158
|
+
// Load user-level defaults from ~/.gsd/defaults.json if available
|
|
159
|
+
const globalDefaultsPath = node_path_1.default.join(homedir, '.gsd', 'defaults.json');
|
|
160
|
+
let userDefaults = {};
|
|
161
|
+
try {
|
|
162
|
+
if (node_fs_1.default.existsSync(globalDefaultsPath)) {
|
|
163
|
+
userDefaults = JSON.parse(node_fs_1.default.readFileSync(globalDefaultsPath, 'utf-8'));
|
|
164
|
+
// Migrate deprecated "depth" key to "granularity"
|
|
165
|
+
if ('depth' in userDefaults && !('granularity' in userDefaults)) {
|
|
166
|
+
const depthToGranularity = { quick: 'coarse', standard: 'standard', comprehensive: 'fine' };
|
|
167
|
+
userDefaults['granularity'] = depthToGranularity[userDefaults['depth']] || userDefaults['depth'];
|
|
168
|
+
delete userDefaults['depth'];
|
|
169
|
+
try {
|
|
170
|
+
(0, shell_command_projection_cjs_1.platformWriteSync)(globalDefaultsPath, JSON.stringify(userDefaults, null, 2));
|
|
171
|
+
}
|
|
172
|
+
catch { /* intentionally empty */ }
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
catch {
|
|
177
|
+
// Ignore malformed global defaults
|
|
178
|
+
}
|
|
179
|
+
const hardcoded = {
|
|
180
|
+
model_profile: CONFIG_DEFAULTS.model_profile,
|
|
181
|
+
commit_docs: CONFIG_DEFAULTS.commit_docs,
|
|
182
|
+
parallelization: CONFIG_DEFAULTS.parallelization,
|
|
183
|
+
search_gitignored: CONFIG_DEFAULTS.search_gitignored,
|
|
184
|
+
brave_search: hasBraveSearch,
|
|
185
|
+
firecrawl: hasFirecrawl,
|
|
186
|
+
exa_search: hasExaSearch,
|
|
187
|
+
tavily_search: hasTavilySearch,
|
|
188
|
+
ref_search: hasRefSearch,
|
|
189
|
+
perplexity: hasPerplexity,
|
|
190
|
+
jina: hasJina,
|
|
191
|
+
git: {
|
|
192
|
+
branching_strategy: CONFIG_DEFAULTS.branching_strategy,
|
|
193
|
+
create_tag: true,
|
|
194
|
+
phase_branch_template: CONFIG_DEFAULTS.phase_branch_template,
|
|
195
|
+
milestone_branch_template: CONFIG_DEFAULTS.milestone_branch_template,
|
|
196
|
+
quick_branch_template: CONFIG_DEFAULTS.quick_branch_template,
|
|
197
|
+
},
|
|
198
|
+
workflow: {
|
|
199
|
+
research: true,
|
|
200
|
+
plan_check: true,
|
|
201
|
+
verifier: true,
|
|
202
|
+
nyquist_validation: true,
|
|
203
|
+
auto_advance: false,
|
|
204
|
+
node_repair: true,
|
|
205
|
+
node_repair_budget: 2,
|
|
206
|
+
ui_phase: true,
|
|
207
|
+
ui_safety_gate: true,
|
|
208
|
+
ai_integration_phase: true,
|
|
209
|
+
tdd_mode: false,
|
|
210
|
+
human_verify_mode: 'end-of-phase',
|
|
211
|
+
text_mode: false,
|
|
212
|
+
research_before_questions: false,
|
|
213
|
+
discuss_mode: 'discuss',
|
|
214
|
+
skip_discuss: false,
|
|
215
|
+
code_review: true,
|
|
216
|
+
code_review_depth: 'standard',
|
|
217
|
+
code_review_command: null,
|
|
218
|
+
pattern_mapper: true,
|
|
219
|
+
plan_bounce: false,
|
|
220
|
+
plan_bounce_script: null,
|
|
221
|
+
plan_bounce_passes: 2,
|
|
222
|
+
auto_prune_state: false,
|
|
223
|
+
post_planning_gaps: CONFIG_DEFAULTS.post_planning_gaps,
|
|
224
|
+
security_enforcement: CONFIG_DEFAULTS.security_enforcement,
|
|
225
|
+
security_asvs_level: CONFIG_DEFAULTS.security_asvs_level,
|
|
226
|
+
security_block_on: CONFIG_DEFAULTS.security_block_on,
|
|
227
|
+
},
|
|
228
|
+
ship: {
|
|
229
|
+
pr_body_sections: [],
|
|
230
|
+
},
|
|
231
|
+
hooks: {
|
|
232
|
+
context_warnings: true,
|
|
233
|
+
},
|
|
234
|
+
project_code: null,
|
|
235
|
+
phase_naming: 'sequential',
|
|
236
|
+
agent_skills: {},
|
|
237
|
+
claude_md_path: './CLAUDE.md',
|
|
238
|
+
plan_review: {
|
|
239
|
+
source_grounding: true,
|
|
240
|
+
source_grounding_authority: 'grep',
|
|
241
|
+
},
|
|
242
|
+
};
|
|
243
|
+
const ud = userDefaults;
|
|
244
|
+
const ch = choices;
|
|
245
|
+
const hd = hardcoded;
|
|
246
|
+
// Three-level deep merge: hardcoded <- userDefaults <- choices
|
|
247
|
+
const config = {
|
|
248
|
+
...hardcoded,
|
|
249
|
+
...userDefaults,
|
|
250
|
+
...choices,
|
|
251
|
+
git: {
|
|
252
|
+
...hd['git'],
|
|
253
|
+
...(ud['git'] || {}),
|
|
254
|
+
...(ch['git'] || {}),
|
|
255
|
+
},
|
|
256
|
+
workflow: {
|
|
257
|
+
...hd['workflow'],
|
|
258
|
+
...(ud['workflow'] || {}),
|
|
259
|
+
...(ch['workflow'] || {}),
|
|
260
|
+
},
|
|
261
|
+
ship: {
|
|
262
|
+
...hd['ship'],
|
|
263
|
+
...(ud['ship'] || {}),
|
|
264
|
+
...(ch['ship'] || {}),
|
|
265
|
+
},
|
|
266
|
+
hooks: {
|
|
267
|
+
...hd['hooks'],
|
|
268
|
+
...(ud['hooks'] || {}),
|
|
269
|
+
...(ch['hooks'] || {}),
|
|
270
|
+
},
|
|
271
|
+
agent_skills: {
|
|
272
|
+
...hd['agent_skills'],
|
|
273
|
+
...(ud['agent_skills'] || {}),
|
|
274
|
+
...(ch['agent_skills'] || {}),
|
|
275
|
+
},
|
|
276
|
+
plan_review: {
|
|
277
|
+
...hd['plan_review'],
|
|
278
|
+
...(ud['plan_review'] || {}),
|
|
279
|
+
...(ch['plan_review'] || {}),
|
|
280
|
+
},
|
|
281
|
+
};
|
|
282
|
+
validateShipPrBodySections(config['ship']['pr_body_sections']);
|
|
283
|
+
return config;
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Command: create a fully-materialized .planning/config.json for a new project.
|
|
287
|
+
*
|
|
288
|
+
* Accepts user-chosen settings as a JSON string (the keys the user explicitly
|
|
289
|
+
* configured during /gsd:new-project). All remaining keys are filled from
|
|
290
|
+
* hardcoded defaults and optional ~/.gsd/defaults.json.
|
|
291
|
+
*
|
|
292
|
+
* Idempotent: if config.json already exists, returns { created: false }.
|
|
293
|
+
*/
|
|
294
|
+
function cmdConfigNewProject(cwd, choicesJson, raw) {
|
|
295
|
+
const planningBase = planningDir(cwd);
|
|
296
|
+
const configPath = node_path_1.default.join(planningBase, 'config.json');
|
|
297
|
+
// Idempotent: don't overwrite existing config
|
|
298
|
+
if (node_fs_1.default.existsSync(configPath)) {
|
|
299
|
+
output({ created: false, reason: 'already_exists' }, raw, 'exists');
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
// Parse user choices
|
|
303
|
+
let userChoices = {};
|
|
304
|
+
if (choicesJson && choicesJson.trim() !== '') {
|
|
305
|
+
try {
|
|
306
|
+
userChoices = JSON.parse(choicesJson);
|
|
307
|
+
}
|
|
308
|
+
catch (err) {
|
|
309
|
+
error('Invalid JSON for config-new-project: ' + err.message);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
// Ensure .planning directory exists
|
|
313
|
+
try {
|
|
314
|
+
(0, shell_command_projection_cjs_1.platformEnsureDir)(planningBase);
|
|
315
|
+
}
|
|
316
|
+
catch (err) {
|
|
317
|
+
error('Failed to create .planning directory: ' + err.message);
|
|
318
|
+
}
|
|
319
|
+
const config = buildNewProjectConfig(userChoices);
|
|
320
|
+
try {
|
|
321
|
+
(0, shell_command_projection_cjs_1.platformWriteSync)(configPath, JSON.stringify(config, null, 2));
|
|
322
|
+
output({ created: true, path: '.planning/config.json' }, raw, 'created');
|
|
323
|
+
}
|
|
324
|
+
catch (err) {
|
|
325
|
+
error('Failed to write config.json: ' + err.message);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* Ensures the config file exists (creates it if needed).
|
|
330
|
+
*
|
|
331
|
+
* Does not call `output()`, so can be used as one step in a command without triggering `exit(0)` in
|
|
332
|
+
* the happy path. But note that `error()` will still `exit(1)` out of the process.
|
|
333
|
+
*/
|
|
334
|
+
function ensureConfigFile(cwd) {
|
|
335
|
+
const planningBase = planningDir(cwd);
|
|
336
|
+
const configPath = node_path_1.default.join(planningBase, 'config.json');
|
|
337
|
+
// Ensure .planning directory exists
|
|
338
|
+
try {
|
|
339
|
+
(0, shell_command_projection_cjs_1.platformEnsureDir)(planningBase);
|
|
340
|
+
}
|
|
341
|
+
catch (err) {
|
|
342
|
+
error('Failed to create .planning directory: ' + err.message);
|
|
343
|
+
}
|
|
344
|
+
// Check if config already exists
|
|
345
|
+
if (node_fs_1.default.existsSync(configPath)) {
|
|
346
|
+
return { created: false, reason: 'already_exists' };
|
|
347
|
+
}
|
|
348
|
+
const config = buildNewProjectConfig({});
|
|
349
|
+
try {
|
|
350
|
+
(0, shell_command_projection_cjs_1.platformWriteSync)(configPath, JSON.stringify(config, null, 2));
|
|
351
|
+
return { created: true, path: '.planning/config.json' };
|
|
352
|
+
}
|
|
353
|
+
catch (err) {
|
|
354
|
+
error('Failed to create config.json: ' + err.message);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
/**
|
|
358
|
+
* Command to ensure the config file exists (creates it if needed).
|
|
359
|
+
*
|
|
360
|
+
* Note that this exits the process (via `output()`) even in the happy path; use
|
|
361
|
+
* `ensureConfigFile()` directly if you need to avoid this.
|
|
362
|
+
*/
|
|
363
|
+
function cmdConfigEnsureSection(cwd, raw) {
|
|
364
|
+
const ensureConfigFileResult = ensureConfigFile(cwd);
|
|
365
|
+
if (ensureConfigFileResult && ensureConfigFileResult.created) {
|
|
366
|
+
output(ensureConfigFileResult, raw, 'created');
|
|
367
|
+
}
|
|
368
|
+
else {
|
|
369
|
+
output(ensureConfigFileResult, raw, 'exists');
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* Sets a value in the config file, allowing nested values via dot notation (e.g.,
|
|
374
|
+
* "workflow.research").
|
|
375
|
+
*
|
|
376
|
+
* Does not call `output()`, so can be used as one step in a command without triggering `exit(0)` in
|
|
377
|
+
* the happy path. But note that `error()` will still `exit(1)` out of the process.
|
|
378
|
+
*/
|
|
379
|
+
function setConfigValue(cwd, keyPath, parsedValue) {
|
|
380
|
+
const configPath = node_path_1.default.join(planningDir(cwd), 'config.json');
|
|
381
|
+
return withPlanningLock(cwd, () => {
|
|
382
|
+
// Load existing config or start with empty object
|
|
383
|
+
let config = {};
|
|
384
|
+
try {
|
|
385
|
+
if (node_fs_1.default.existsSync(configPath)) {
|
|
386
|
+
config = JSON.parse(node_fs_1.default.readFileSync(configPath, 'utf-8'));
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
catch (err) {
|
|
390
|
+
error('Failed to read config.json: ' + err.message, ERROR_REASON.CONFIG_PARSE_FAILED);
|
|
391
|
+
}
|
|
392
|
+
// Set nested value using dot notation (e.g., "workflow.research").
|
|
393
|
+
// Prototype-pollution guard: reject dangerous segments via inline literal
|
|
394
|
+
// comparisons on the exact key used to index `current`, immediately before
|
|
395
|
+
// each write. The inline comparison is the barrier CodeQL's
|
|
396
|
+
// js/prototype-pollution-utility query recognises — the previous Set-based
|
|
397
|
+
// pre-loop check was functionally correct but not traced through, so
|
|
398
|
+
// code-scanning alert #26 kept firing. Behaviour is unchanged from #663.
|
|
399
|
+
const keys = keyPath.split('.');
|
|
400
|
+
let current = config;
|
|
401
|
+
for (let i = 0; i < keys.length - 1; i++) {
|
|
402
|
+
const key = keys[i];
|
|
403
|
+
if (key === '__proto__' || key === 'prototype' || key === 'constructor') {
|
|
404
|
+
error('Invalid config key (prototype pollution guard): ' + keyPath, ERROR_REASON.CONFIG_PARSE_FAILED);
|
|
405
|
+
}
|
|
406
|
+
if (current[key] === undefined || typeof current[key] !== 'object') {
|
|
407
|
+
current[key] = {};
|
|
408
|
+
}
|
|
409
|
+
current = current[key];
|
|
410
|
+
}
|
|
411
|
+
const lastKey = keys[keys.length - 1];
|
|
412
|
+
if (lastKey === '__proto__' || lastKey === 'prototype' || lastKey === 'constructor') {
|
|
413
|
+
error('Invalid config key (prototype pollution guard): ' + keyPath, ERROR_REASON.CONFIG_PARSE_FAILED);
|
|
414
|
+
}
|
|
415
|
+
const previousValue = current[lastKey]; // Capture previous value before overwriting
|
|
416
|
+
current[lastKey] = parsedValue;
|
|
417
|
+
// Write back
|
|
418
|
+
try {
|
|
419
|
+
(0, shell_command_projection_cjs_1.platformWriteSync)(configPath, JSON.stringify(config, null, 2));
|
|
420
|
+
return { updated: true, key: keyPath, value: parsedValue, previousValue };
|
|
421
|
+
}
|
|
422
|
+
catch (err) {
|
|
423
|
+
error('Failed to write config.json: ' + err.message);
|
|
424
|
+
}
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
428
|
+
* Command to set a value in the config file, allowing nested values via dot notation (e.g.,
|
|
429
|
+
* "workflow.research").
|
|
430
|
+
*
|
|
431
|
+
* Note that this exits the process (via `output()`) even in the happy path; use `setConfigValue()`
|
|
432
|
+
* directly if you need to avoid this.
|
|
433
|
+
*/
|
|
434
|
+
function cmdConfigSet(cwd, keyPath, value, raw) {
|
|
435
|
+
if (!keyPath) {
|
|
436
|
+
error('Usage: config-set <key.path> <value>', ERROR_REASON.USAGE);
|
|
437
|
+
}
|
|
438
|
+
// #3593: reject the "key without value" form (e.g. `config-set
|
|
439
|
+
// model_profile` with args[2] === undefined). Without this guard the
|
|
440
|
+
// value passes through as undefined, the number/boolean/json branches
|
|
441
|
+
// all fall through, and the write either silently strips the key
|
|
442
|
+
// (JSON.stringify drops undefined values) or writes a corrupt entry.
|
|
443
|
+
// Typed reason so the negative-matrix test can assert on it instead
|
|
444
|
+
// of greppinng prose.
|
|
445
|
+
if (value === undefined) {
|
|
446
|
+
error('Usage: config-set <key.path> <value>', ERROR_REASON.USAGE);
|
|
447
|
+
}
|
|
448
|
+
// After the two error() guards above, keyPath and value are narrowed to string.
|
|
449
|
+
// TypeScript doesn't always infer never-return narrowing through error(), so we assert.
|
|
450
|
+
const kp = keyPath;
|
|
451
|
+
const val = value;
|
|
452
|
+
validateKnownConfigKeyPath(kp);
|
|
453
|
+
if (!isValidConfigKey(kp)) {
|
|
454
|
+
error(`Unknown config key: "${kp}". Valid keys: ${[...VALID_CONFIG_KEYS].sort().join(', ')}, agent_skills.<agent-type>, features.<feature_name>`, ERROR_REASON.CONFIG_INVALID_KEY);
|
|
455
|
+
}
|
|
456
|
+
// Parse value (handle booleans, numbers, and JSON arrays/objects)
|
|
457
|
+
let parsedValue = val;
|
|
458
|
+
if (val === 'true')
|
|
459
|
+
parsedValue = true;
|
|
460
|
+
else if (val === 'false')
|
|
461
|
+
parsedValue = false;
|
|
462
|
+
else if (!isNaN(Number(val)) && val !== '')
|
|
463
|
+
parsedValue = Number(val);
|
|
464
|
+
else if (typeof val === 'string' && (val.startsWith('[') || val.startsWith('{'))) {
|
|
465
|
+
try {
|
|
466
|
+
parsedValue = JSON.parse(val);
|
|
467
|
+
}
|
|
468
|
+
catch { /* keep as string */ }
|
|
469
|
+
}
|
|
470
|
+
const VALID_CONTEXT_VALUES = ['dev', 'research', 'review'];
|
|
471
|
+
if (kp === 'context' && !VALID_CONTEXT_VALUES.includes(String(parsedValue))) {
|
|
472
|
+
error(`Invalid context value '${val}'. Valid values: ${VALID_CONTEXT_VALUES.join(', ')}`);
|
|
473
|
+
}
|
|
474
|
+
// Codebase drift detector (#2003)
|
|
475
|
+
const VALID_DRIFT_ACTIONS = ['warn', 'auto-remap'];
|
|
476
|
+
if (kp === 'workflow.drift_action' && !VALID_DRIFT_ACTIONS.includes(String(parsedValue))) {
|
|
477
|
+
error(`Invalid workflow.drift_action '${val}'. Valid values: ${VALID_DRIFT_ACTIONS.join(', ')}`);
|
|
478
|
+
}
|
|
479
|
+
if (kp === 'workflow.drift_threshold') {
|
|
480
|
+
if (typeof parsedValue !== 'number' || !Number.isInteger(parsedValue) || parsedValue < 1) {
|
|
481
|
+
error(`Invalid workflow.drift_threshold '${val}'. Must be a positive integer.`);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
// Post-planning gap checker (#2493)
|
|
485
|
+
if (kp === 'workflow.post_planning_gaps') {
|
|
486
|
+
if (typeof parsedValue !== 'boolean') {
|
|
487
|
+
error(`Invalid workflow.post_planning_gaps '${val}'. Must be a boolean (true or false).`);
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
// #3086 — git.create_tag: boolean only
|
|
491
|
+
if (kp === 'git.create_tag') {
|
|
492
|
+
if (typeof parsedValue !== 'boolean') {
|
|
493
|
+
error(`Invalid git.create_tag '${val}'. Must be a boolean (true or false).`);
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
if (kp === 'ship.pr_body_sections') {
|
|
497
|
+
validateShipPrBodySections(parsedValue);
|
|
498
|
+
}
|
|
499
|
+
// Human verification checkpoint mode (#3309)
|
|
500
|
+
const VALID_HUMAN_VERIFY_MODES = ['mid-flight', 'end-of-phase'];
|
|
501
|
+
if (kp === 'workflow.human_verify_mode' && !VALID_HUMAN_VERIFY_MODES.includes(String(parsedValue))) {
|
|
502
|
+
error(`Invalid workflow.human_verify_mode '${val}'. Valid values: ${VALID_HUMAN_VERIFY_MODES.join(', ')}`);
|
|
503
|
+
}
|
|
504
|
+
// Context position enum validation (#2937)
|
|
505
|
+
const VALID_CONTEXT_POSITIONS = ['front', 'end'];
|
|
506
|
+
if (kp === 'statusline.context_position' && !VALID_CONTEXT_POSITIONS.includes(String(parsedValue))) {
|
|
507
|
+
error(`Invalid statusline.context_position '${val}'. Valid values: ${VALID_CONTEXT_POSITIONS.join(', ')}`);
|
|
508
|
+
}
|
|
509
|
+
// Fallow scope + profile enum validation (#3424)
|
|
510
|
+
const VALID_FALLOW_SCOPES = ['phase', 'repo'];
|
|
511
|
+
if (kp === 'code_quality.fallow.scope' && !VALID_FALLOW_SCOPES.includes(String(parsedValue))) {
|
|
512
|
+
error(`Invalid code_quality.fallow.scope '${val}'. Valid values: ${VALID_FALLOW_SCOPES.join(', ')}`);
|
|
513
|
+
}
|
|
514
|
+
const VALID_FALLOW_PROFILES = ['minimal', 'standard', 'strict'];
|
|
515
|
+
if (kp === 'code_quality.fallow.profile' && !VALID_FALLOW_PROFILES.includes(String(parsedValue))) {
|
|
516
|
+
error(`Invalid code_quality.fallow.profile '${val}'. Valid values: ${VALID_FALLOW_PROFILES.join(', ')}`);
|
|
517
|
+
}
|
|
518
|
+
// plan_review.source_grounding (#22) — boolean only
|
|
519
|
+
if (kp === 'plan_review.source_grounding') {
|
|
520
|
+
if (typeof parsedValue !== 'boolean') {
|
|
521
|
+
error(`Invalid plan_review.source_grounding '${val}'. Must be a boolean (true or false).`);
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
// plan_review.source_grounding_authority (#22) — enum
|
|
525
|
+
const VALID_SOURCE_GROUNDING_AUTHORITIES = ['grep', 'intel', 'treesitter', 'lsp', 'scip'];
|
|
526
|
+
if (kp === 'plan_review.source_grounding_authority' && !VALID_SOURCE_GROUNDING_AUTHORITIES.includes(String(parsedValue))) {
|
|
527
|
+
error(`Invalid plan_review.source_grounding_authority '${val}'. Valid values: ${VALID_SOURCE_GROUNDING_AUTHORITIES.join(', ')}`);
|
|
528
|
+
}
|
|
529
|
+
if (kp === 'review.default_reviewers') {
|
|
530
|
+
const normalized = (0, review_reviewer_selection_cjs_1.normalizeConfiguredDefaultReviewers)(parsedValue);
|
|
531
|
+
if (normalized.errors.length > 0) {
|
|
532
|
+
error(normalized.errors[0]);
|
|
533
|
+
}
|
|
534
|
+
parsedValue = normalized.values;
|
|
535
|
+
}
|
|
536
|
+
const setConfigValueResult = setConfigValue(cwd, kp, parsedValue);
|
|
537
|
+
// Mask secrets in both JSON and text output. The plaintext is written
|
|
538
|
+
// to config.json (that's where secrets live on disk); the CLI output
|
|
539
|
+
// must never echo it. See lib/secrets.cjs.
|
|
540
|
+
if ((0, secrets_cjs_1.isSecretKey)(kp)) {
|
|
541
|
+
// parsedValue is unknown at this point; maskSecret accepts MaskableValue
|
|
542
|
+
const masked = (0, secrets_cjs_1.maskSecret)(parsedValue);
|
|
543
|
+
const maskedPrev = setConfigValueResult.previousValue === undefined
|
|
544
|
+
? undefined
|
|
545
|
+
: (0, secrets_cjs_1.maskSecret)(setConfigValueResult.previousValue);
|
|
546
|
+
const maskedResult = {
|
|
547
|
+
...setConfigValueResult,
|
|
548
|
+
value: masked,
|
|
549
|
+
previousValue: maskedPrev,
|
|
550
|
+
masked: true,
|
|
551
|
+
};
|
|
552
|
+
output(maskedResult, raw, `${kp}=${masked}`);
|
|
553
|
+
return;
|
|
554
|
+
}
|
|
555
|
+
output(setConfigValueResult, raw, `${kp}=${String(parsedValue)}`);
|
|
556
|
+
}
|
|
557
|
+
function cmdConfigGet(cwd, keyPath, raw, defaultValue) {
|
|
558
|
+
const configPath = node_path_1.default.join(planningDir(cwd), 'config.json');
|
|
559
|
+
const hasDefault = defaultValue !== undefined;
|
|
560
|
+
if (!keyPath) {
|
|
561
|
+
error('Usage: config-get <key.path> [--default <value>]');
|
|
562
|
+
}
|
|
563
|
+
// After the error() guard, keyPath is narrowed to string.
|
|
564
|
+
const kp = keyPath;
|
|
565
|
+
let config = {};
|
|
566
|
+
try {
|
|
567
|
+
if (node_fs_1.default.existsSync(configPath)) {
|
|
568
|
+
config = JSON.parse(node_fs_1.default.readFileSync(configPath, 'utf-8'));
|
|
569
|
+
}
|
|
570
|
+
else if (hasDefault) {
|
|
571
|
+
// eslint-disable-next-line @typescript-eslint/no-base-to-string
|
|
572
|
+
output(defaultValue, raw, String(defaultValue));
|
|
573
|
+
return;
|
|
574
|
+
}
|
|
575
|
+
else if (Object.prototype.hasOwnProperty.call(SCHEMA_DEFAULTS, kp)) {
|
|
576
|
+
const def = SCHEMA_DEFAULTS[kp];
|
|
577
|
+
output(def, raw, String(def));
|
|
578
|
+
return;
|
|
579
|
+
}
|
|
580
|
+
else {
|
|
581
|
+
error('No config.json found at ' + configPath, ERROR_REASON.CONFIG_NO_FILE);
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
catch (err) {
|
|
585
|
+
if (err.message.startsWith('No config.json'))
|
|
586
|
+
throw err;
|
|
587
|
+
error('Failed to read config.json: ' + err.message, ERROR_REASON.CONFIG_PARSE_FAILED);
|
|
588
|
+
}
|
|
589
|
+
// Traverse dot-notation path (e.g., "workflow.auto_advance")
|
|
590
|
+
const keys = kp.split('.');
|
|
591
|
+
let current = config;
|
|
592
|
+
for (const key of keys) {
|
|
593
|
+
if (current === undefined || current === null || typeof current !== 'object') {
|
|
594
|
+
// eslint-disable-next-line @typescript-eslint/no-base-to-string
|
|
595
|
+
if (hasDefault) {
|
|
596
|
+
output(defaultValue, raw, String(defaultValue));
|
|
597
|
+
return;
|
|
598
|
+
}
|
|
599
|
+
if (Object.prototype.hasOwnProperty.call(SCHEMA_DEFAULTS, kp)) {
|
|
600
|
+
const def = SCHEMA_DEFAULTS[kp];
|
|
601
|
+
output(def, raw, String(def));
|
|
602
|
+
return;
|
|
603
|
+
}
|
|
604
|
+
error(`Key not found: ${kp}`, ERROR_REASON.CONFIG_KEY_NOT_FOUND);
|
|
605
|
+
}
|
|
606
|
+
current = current[key];
|
|
607
|
+
}
|
|
608
|
+
if (current === undefined) {
|
|
609
|
+
// eslint-disable-next-line @typescript-eslint/no-base-to-string
|
|
610
|
+
if (hasDefault) {
|
|
611
|
+
output(defaultValue, raw, String(defaultValue));
|
|
612
|
+
return;
|
|
613
|
+
}
|
|
614
|
+
if (Object.prototype.hasOwnProperty.call(SCHEMA_DEFAULTS, kp)) {
|
|
615
|
+
const def = SCHEMA_DEFAULTS[kp];
|
|
616
|
+
output(def, raw, String(def));
|
|
617
|
+
return;
|
|
618
|
+
}
|
|
619
|
+
error(`Key not found: ${kp}`, ERROR_REASON.CONFIG_KEY_NOT_FOUND);
|
|
620
|
+
}
|
|
621
|
+
// Never echo plaintext for sensitive keys via config-get. Plaintext lives
|
|
622
|
+
// in config.json on disk; the CLI surface always shows the masked form.
|
|
623
|
+
if ((0, secrets_cjs_1.isSecretKey)(kp)) {
|
|
624
|
+
const masked = (0, secrets_cjs_1.maskSecret)(current);
|
|
625
|
+
output(masked, raw, masked);
|
|
626
|
+
return;
|
|
627
|
+
}
|
|
628
|
+
output(current, raw, String(current));
|
|
629
|
+
}
|
|
630
|
+
/**
|
|
631
|
+
* Command to set the model profile in the config file.
|
|
632
|
+
*
|
|
633
|
+
* Note that this exits the process (via `output()`) even in the happy path.
|
|
634
|
+
*/
|
|
635
|
+
function cmdConfigSetModelProfile(cwd, profile, raw) {
|
|
636
|
+
if (!profile) {
|
|
637
|
+
error(`Usage: config-set-model-profile <${VALID_PROFILES.join('|')}>`);
|
|
638
|
+
}
|
|
639
|
+
const normalizedProfile = profile.toLowerCase().trim();
|
|
640
|
+
if (!VALID_PROFILES.includes(normalizedProfile)) {
|
|
641
|
+
error(`Invalid profile '${String(profile)}'. Valid profiles: ${VALID_PROFILES.join(', ')}`);
|
|
642
|
+
}
|
|
643
|
+
// Ensure config exists (create if needed)
|
|
644
|
+
ensureConfigFile(cwd);
|
|
645
|
+
// Set the model profile in the config
|
|
646
|
+
const { previousValue } = setConfigValue(cwd, 'model_profile', normalizedProfile);
|
|
647
|
+
const previousProfile = typeof previousValue === 'string' ? previousValue : 'balanced';
|
|
648
|
+
// Build result value / message and return
|
|
649
|
+
const agentToModelMap = getAgentToModelMapForProfile(normalizedProfile);
|
|
650
|
+
const result = {
|
|
651
|
+
updated: true,
|
|
652
|
+
profile: normalizedProfile,
|
|
653
|
+
previousProfile,
|
|
654
|
+
agentToModelMap,
|
|
655
|
+
};
|
|
656
|
+
const rawValue = getCmdConfigSetModelProfileResultMessage(normalizedProfile, previousProfile, agentToModelMap);
|
|
657
|
+
output(result, raw, rawValue);
|
|
658
|
+
}
|
|
659
|
+
/**
|
|
660
|
+
* Returns the message to display for the result of the `config-set-model-profile` command when
|
|
661
|
+
* displaying raw output.
|
|
662
|
+
*/
|
|
663
|
+
function getCmdConfigSetModelProfileResultMessage(normalizedProfile, previousProfile, agentToModelMap) {
|
|
664
|
+
const agentToModelTable = formatAgentToModelMapAsTable(agentToModelMap);
|
|
665
|
+
const didChange = previousProfile !== normalizedProfile;
|
|
666
|
+
const paragraphs = didChange
|
|
667
|
+
? [
|
|
668
|
+
`✓ Model profile set to: ${normalizedProfile} (was: ${previousProfile})`,
|
|
669
|
+
'Agents will now use:',
|
|
670
|
+
agentToModelTable,
|
|
671
|
+
'Next spawned agents will use the new profile.',
|
|
672
|
+
]
|
|
673
|
+
: [
|
|
674
|
+
`✓ Model profile is already set to: ${normalizedProfile}`,
|
|
675
|
+
'Agents are using:',
|
|
676
|
+
agentToModelTable,
|
|
677
|
+
];
|
|
678
|
+
return paragraphs.join('\n\n');
|
|
679
|
+
}
|
|
680
|
+
/**
|
|
681
|
+
* Print the resolved config.json path (workstream-aware). Used by settings.md
|
|
682
|
+
* so the workflow writes/reads the correct file when a workstream is active (#2282).
|
|
683
|
+
*/
|
|
684
|
+
function cmdConfigPath(cwd, _raw, workstreamContext = null) {
|
|
685
|
+
// Always emit as plain text — a file path is used via shell substitution,
|
|
686
|
+
// never consumed as JSON. Passing raw=true forces plain-text output.
|
|
687
|
+
const configPath = workstreamContext && workstreamContext.configPath
|
|
688
|
+
? workstreamContext.configPath
|
|
689
|
+
: node_path_1.default.join(planningDir(cwd), 'config.json');
|
|
690
|
+
output(configPath, true, configPath);
|
|
691
|
+
}
|
|
692
|
+
/**
|
|
693
|
+
* Explicit on-disk migration of legacy config keys to canonical nested shape.
|
|
694
|
+
*
|
|
695
|
+
* Wraps the Configuration Module's migrateOnDisk() for the CLI surface. This
|
|
696
|
+
* is the Phase 2 acceptance-criteria deliverable for opt-in migration (#3536):
|
|
697
|
+
* users can run `gsd-tools migrate-config` to apply all four legacy-key
|
|
698
|
+
* migrations to their .planning/config.json without having to load any config
|
|
699
|
+
* implicitly via another command.
|
|
700
|
+
*
|
|
701
|
+
* Output: JSON object with { migrated, normalizations, wrote } or a human-readable
|
|
702
|
+
* summary when --raw is set. Exits 0 in all cases (including no-op).
|
|
703
|
+
*
|
|
704
|
+
* Note: migrateOnDisk() is synchronous; the original CJS used async for
|
|
705
|
+
* forward-compatibility but no await is needed. Dropped async per ADR-457 policy
|
|
706
|
+
* (caller uses `await` which is safe on a sync return value).
|
|
707
|
+
*/
|
|
708
|
+
function cmdMigrateConfig(cwd, raw) {
|
|
709
|
+
const ws = process.env['GSD_WORKSTREAM'] || null;
|
|
710
|
+
const report = (0, configuration_cjs_1.migrateOnDisk)(cwd, ws || undefined);
|
|
711
|
+
if (raw) {
|
|
712
|
+
if (!report.migrated) {
|
|
713
|
+
const msg = 'No legacy keys found — config is already canonical.';
|
|
714
|
+
output(msg, true, msg);
|
|
715
|
+
}
|
|
716
|
+
else {
|
|
717
|
+
const lines = [
|
|
718
|
+
`Migrated: ${String(report.wrote)}`,
|
|
719
|
+
...report.normalizations.map(n => ` ${n.from} → ${n.to}`),
|
|
720
|
+
].join('\n');
|
|
721
|
+
output(lines, true, lines);
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
else {
|
|
725
|
+
// output() JSON.stringify's its first arg when raw=false; pass the report object.
|
|
726
|
+
output(report, false, report);
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
module.exports = {
|
|
730
|
+
VALID_CONFIG_KEYS,
|
|
731
|
+
cmdConfigEnsureSection,
|
|
732
|
+
cmdConfigSet,
|
|
733
|
+
cmdConfigGet,
|
|
734
|
+
cmdConfigSetModelProfile,
|
|
735
|
+
cmdConfigNewProject,
|
|
736
|
+
cmdConfigPath,
|
|
737
|
+
cmdMigrateConfig,
|
|
738
|
+
};
|