gsd-remix 1.0.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/LICENSE +21 -0
- package/README.md +939 -0
- package/README.zh-CN.md +876 -0
- package/agents/gsd-advisor-researcher.md +127 -0
- package/agents/gsd-ai-researcher.md +133 -0
- package/agents/gsd-assumptions-analyzer.md +105 -0
- package/agents/gsd-code-fixer.md +517 -0
- package/agents/gsd-code-reviewer.md +371 -0
- package/agents/gsd-codebase-mapper.md +781 -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 +615 -0
- package/agents/gsd-domain-researcher.md +153 -0
- package/agents/gsd-eval-auditor.md +191 -0
- package/agents/gsd-eval-planner.md +154 -0
- package/agents/gsd-executor.md +603 -0
- package/agents/gsd-framework-selector.md +160 -0
- package/agents/gsd-integration-checker.md +470 -0
- package/agents/gsd-intel-updater.md +334 -0
- package/agents/gsd-nyquist-auditor.md +203 -0
- package/agents/gsd-pattern-mapper.md +335 -0
- package/agents/gsd-phase-researcher.md +841 -0
- package/agents/gsd-plan-checker.md +978 -0
- package/agents/gsd-planner.md +1251 -0
- package/agents/gsd-project-researcher.md +677 -0
- package/agents/gsd-research-synthesizer.md +247 -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 +380 -0
- package/agents/gsd-user-profiler.md +171 -0
- package/agents/gsd-verifier.md +830 -0
- package/bin/install.js +7062 -0
- package/commands/gsd/add-backlog.md +79 -0
- package/commands/gsd/add-phase.md +43 -0
- package/commands/gsd/add-tests.md +41 -0
- package/commands/gsd/add-todo.md +47 -0
- package/commands/gsd/ai-integration-phase.md +36 -0
- package/commands/gsd/analyze-dependencies.md +34 -0
- package/commands/gsd/audit-fix.md +33 -0
- package/commands/gsd/audit-milestone.md +36 -0
- package/commands/gsd/audit-uat.md +24 -0
- package/commands/gsd/autonomous.md +46 -0
- package/commands/gsd/check-todos.md +45 -0
- package/commands/gsd/cleanup.md +23 -0
- package/commands/gsd/code-review-fix.md +52 -0
- package/commands/gsd/code-review.md +55 -0
- package/commands/gsd/complete-milestone.md +136 -0
- package/commands/gsd/debug.md +263 -0
- package/commands/gsd/discuss-phase.md +69 -0
- package/commands/gsd/do.md +30 -0
- package/commands/gsd/docs-update.md +48 -0
- package/commands/gsd/eval-review.md +32 -0
- package/commands/gsd/execute-phase.md +63 -0
- package/commands/gsd/explore.md +27 -0
- package/commands/gsd/extract_learnings.md +22 -0
- package/commands/gsd/fast.md +30 -0
- package/commands/gsd/forensics.md +56 -0
- package/commands/gsd/from-gsd2.md +47 -0
- package/commands/gsd/graphify.md +201 -0
- package/commands/gsd/health.md +22 -0
- package/commands/gsd/help.md +24 -0
- package/commands/gsd/import.md +37 -0
- package/commands/gsd/inbox.md +38 -0
- package/commands/gsd/ingest-docs.md +42 -0
- package/commands/gsd/insert-phase.md +32 -0
- package/commands/gsd/intel.md +179 -0
- package/commands/gsd/join-discord.md +19 -0
- package/commands/gsd/list-phase-assumptions.md +46 -0
- package/commands/gsd/list-workspaces.md +19 -0
- package/commands/gsd/manager.md +40 -0
- package/commands/gsd/map-codebase.md +71 -0
- package/commands/gsd/milestone-summary.md +51 -0
- package/commands/gsd/new-milestone.md +44 -0
- package/commands/gsd/new-project.md +46 -0
- package/commands/gsd/new-workspace.md +44 -0
- package/commands/gsd/next.md +28 -0
- package/commands/gsd/note.md +34 -0
- package/commands/gsd/pause-work.md +38 -0
- package/commands/gsd/plan-milestone-gaps.md +34 -0
- package/commands/gsd/plan-phase.md +52 -0
- package/commands/gsd/plan-review-convergence.md +52 -0
- package/commands/gsd/plant-seed.md +28 -0
- package/commands/gsd/pr-branch.md +25 -0
- package/commands/gsd/profile-user.md +46 -0
- package/commands/gsd/progress.md +25 -0
- package/commands/gsd/quick.md +173 -0
- package/commands/gsd/reapply-patches.md +331 -0
- package/commands/gsd/remove-phase.md +31 -0
- package/commands/gsd/remove-workspace.md +26 -0
- package/commands/gsd/research-phase.md +195 -0
- package/commands/gsd/resume-work.md +40 -0
- package/commands/gsd/review-backlog.md +62 -0
- package/commands/gsd/review.md +40 -0
- package/commands/gsd/scan.md +26 -0
- package/commands/gsd/secure-phase.md +35 -0
- package/commands/gsd/session-report.md +19 -0
- package/commands/gsd/set-profile.md +12 -0
- package/commands/gsd/settings.md +36 -0
- package/commands/gsd/ship.md +23 -0
- package/commands/gsd/sketch-wrap-up.md +31 -0
- package/commands/gsd/sketch.md +49 -0
- package/commands/gsd/spec-phase.md +62 -0
- package/commands/gsd/spike-wrap-up.md +31 -0
- package/commands/gsd/spike.md +46 -0
- package/commands/gsd/stats.md +18 -0
- package/commands/gsd/sync-skills.md +19 -0
- package/commands/gsd/thread.md +227 -0
- package/commands/gsd/ui-phase.md +34 -0
- package/commands/gsd/ui-review.md +32 -0
- package/commands/gsd/ultraplan-phase.md +33 -0
- package/commands/gsd/undo.md +34 -0
- package/commands/gsd/update.md +37 -0
- package/commands/gsd/validate-phase.md +35 -0
- package/commands/gsd/verify-work.md +38 -0
- package/commands/gsd/workstreams.md +69 -0
- package/get-shit-done/bin/gsd-tools.cjs +1263 -0
- package/get-shit-done/bin/lib/artifacts.cjs +52 -0
- package/get-shit-done/bin/lib/audit.cjs +757 -0
- package/get-shit-done/bin/lib/commands.cjs +1023 -0
- package/get-shit-done/bin/lib/config-schema.cjs +79 -0
- package/get-shit-done/bin/lib/config.cjs +463 -0
- package/get-shit-done/bin/lib/core.cjs +1794 -0
- package/get-shit-done/bin/lib/docs.cjs +267 -0
- package/get-shit-done/bin/lib/frontmatter.cjs +379 -0
- package/get-shit-done/bin/lib/graphify.cjs +494 -0
- package/get-shit-done/bin/lib/gsd2-import.cjs +511 -0
- package/get-shit-done/bin/lib/init.cjs +1878 -0
- package/get-shit-done/bin/lib/intel.cjs +639 -0
- package/get-shit-done/bin/lib/learnings.cjs +378 -0
- package/get-shit-done/bin/lib/milestone.cjs +283 -0
- package/get-shit-done/bin/lib/model-profiles.cjs +71 -0
- package/get-shit-done/bin/lib/phase.cjs +1058 -0
- package/get-shit-done/bin/lib/profile-output.cjs +1080 -0
- package/get-shit-done/bin/lib/profile-pipeline.cjs +539 -0
- package/get-shit-done/bin/lib/roadmap.cjs +523 -0
- package/get-shit-done/bin/lib/schema-detect.cjs +238 -0
- package/get-shit-done/bin/lib/security.cjs +504 -0
- package/get-shit-done/bin/lib/state.cjs +1649 -0
- package/get-shit-done/bin/lib/template.cjs +226 -0
- package/get-shit-done/bin/lib/uat.cjs +288 -0
- package/get-shit-done/bin/lib/verify.cjs +1184 -0
- package/get-shit-done/bin/lib/workstream.cjs +495 -0
- package/get-shit-done/bin/repair-sdk.cjs +177 -0
- package/get-shit-done/contexts/dev.md +21 -0
- package/get-shit-done/contexts/research.md +22 -0
- package/get-shit-done/contexts/review.md +22 -0
- package/get-shit-done/references/agent-contracts.md +79 -0
- package/get-shit-done/references/ai-evals.md +156 -0
- package/get-shit-done/references/ai-frameworks.md +186 -0
- package/get-shit-done/references/artifact-types.md +131 -0
- package/get-shit-done/references/autonomous-smart-discuss.md +277 -0
- package/get-shit-done/references/checkpoints.md +808 -0
- package/get-shit-done/references/common-bug-patterns.md +114 -0
- package/get-shit-done/references/context-budget.md +49 -0
- package/get-shit-done/references/continuation-format.md +253 -0
- package/get-shit-done/references/debugger-philosophy.md +76 -0
- package/get-shit-done/references/decimal-phase-calculation.md +64 -0
- package/get-shit-done/references/doc-conflict-engine.md +91 -0
- package/get-shit-done/references/domain-probes.md +125 -0
- package/get-shit-done/references/executor-examples.md +110 -0
- package/get-shit-done/references/few-shot-examples/plan-checker.md +73 -0
- package/get-shit-done/references/few-shot-examples/verifier.md +109 -0
- package/get-shit-done/references/gate-prompts.md +100 -0
- package/get-shit-done/references/gates.md +70 -0
- package/get-shit-done/references/git-integration.md +295 -0
- package/get-shit-done/references/git-planning-commit.md +40 -0
- package/get-shit-done/references/ios-scaffold.md +123 -0
- package/get-shit-done/references/mandatory-initial-read.md +2 -0
- package/get-shit-done/references/model-profile-resolution.md +38 -0
- package/get-shit-done/references/model-profiles.md +145 -0
- package/get-shit-done/references/phase-argument-parsing.md +61 -0
- package/get-shit-done/references/planner-antipatterns.md +89 -0
- package/get-shit-done/references/planner-gap-closure.md +62 -0
- package/get-shit-done/references/planner-reviews.md +39 -0
- package/get-shit-done/references/planner-revision.md +87 -0
- package/get-shit-done/references/planner-source-audit.md +73 -0
- package/get-shit-done/references/planning-config.md +460 -0
- package/get-shit-done/references/project-skills-discovery.md +19 -0
- package/get-shit-done/references/questioning.md +162 -0
- package/get-shit-done/references/revision-loop.md +97 -0
- package/get-shit-done/references/sketch-interactivity.md +41 -0
- package/get-shit-done/references/sketch-theme-system.md +94 -0
- package/get-shit-done/references/sketch-tooling.md +45 -0
- package/get-shit-done/references/sketch-variant-patterns.md +81 -0
- package/get-shit-done/references/tdd.md +330 -0
- package/get-shit-done/references/thinking-models-debug.md +44 -0
- package/get-shit-done/references/thinking-models-execution.md +50 -0
- package/get-shit-done/references/thinking-models-planning.md +62 -0
- package/get-shit-done/references/thinking-models-research.md +50 -0
- package/get-shit-done/references/thinking-models-verification.md +55 -0
- package/get-shit-done/references/thinking-partner.md +96 -0
- package/get-shit-done/references/ui-brand.md +160 -0
- package/get-shit-done/references/universal-anti-patterns.md +63 -0
- package/get-shit-done/references/user-profiling.md +681 -0
- package/get-shit-done/references/verification-overrides.md +227 -0
- package/get-shit-done/references/verification-patterns.md +612 -0
- package/get-shit-done/references/workstream-flag.md +111 -0
- package/get-shit-done/templates/AI-SPEC.md +246 -0
- package/get-shit-done/templates/DEBUG.md +169 -0
- package/get-shit-done/templates/README.md +76 -0
- package/get-shit-done/templates/SECURITY.md +61 -0
- package/get-shit-done/templates/UAT.md +265 -0
- package/get-shit-done/templates/UI-SPEC.md +100 -0
- package/get-shit-done/templates/VALIDATION.md +76 -0
- package/get-shit-done/templates/claude-md.md +145 -0
- package/get-shit-done/templates/codebase/architecture.md +255 -0
- package/get-shit-done/templates/codebase/concerns.md +310 -0
- package/get-shit-done/templates/codebase/conventions.md +307 -0
- package/get-shit-done/templates/codebase/integrations.md +280 -0
- package/get-shit-done/templates/codebase/stack.md +186 -0
- package/get-shit-done/templates/codebase/structure.md +285 -0
- package/get-shit-done/templates/codebase/testing.md +480 -0
- package/get-shit-done/templates/config.json +56 -0
- package/get-shit-done/templates/context.md +352 -0
- package/get-shit-done/templates/continue-here.md +78 -0
- package/get-shit-done/templates/copilot-instructions.md +7 -0
- package/get-shit-done/templates/debug-subagent-prompt.md +91 -0
- package/get-shit-done/templates/dev-preferences.md +21 -0
- package/get-shit-done/templates/discovery.md +146 -0
- package/get-shit-done/templates/discussion-log.md +63 -0
- package/get-shit-done/templates/milestone-archive.md +123 -0
- package/get-shit-done/templates/milestone.md +115 -0
- package/get-shit-done/templates/phase-prompt.md +610 -0
- package/get-shit-done/templates/planner-subagent-prompt.md +117 -0
- package/get-shit-done/templates/project.md +186 -0
- package/get-shit-done/templates/requirements.md +231 -0
- package/get-shit-done/templates/research-project/ARCHITECTURE.md +204 -0
- package/get-shit-done/templates/research-project/FEATURES.md +147 -0
- package/get-shit-done/templates/research-project/PITFALLS.md +200 -0
- package/get-shit-done/templates/research-project/STACK.md +120 -0
- package/get-shit-done/templates/research-project/SUMMARY.md +170 -0
- package/get-shit-done/templates/research.md +592 -0
- package/get-shit-done/templates/retrospective.md +54 -0
- package/get-shit-done/templates/roadmap.md +202 -0
- package/get-shit-done/templates/spec.md +307 -0
- package/get-shit-done/templates/state.md +184 -0
- package/get-shit-done/templates/summary-complex.md +59 -0
- package/get-shit-done/templates/summary-minimal.md +41 -0
- package/get-shit-done/templates/summary-standard.md +48 -0
- package/get-shit-done/templates/summary.md +248 -0
- package/get-shit-done/templates/user-profile.md +146 -0
- package/get-shit-done/templates/user-setup.md +311 -0
- package/get-shit-done/templates/verification-report.md +322 -0
- package/get-shit-done/workflows/add-phase.md +112 -0
- package/get-shit-done/workflows/add-tests.md +354 -0
- package/get-shit-done/workflows/add-todo.md +160 -0
- package/get-shit-done/workflows/ai-integration-phase.md +284 -0
- package/get-shit-done/workflows/analyze-dependencies.md +96 -0
- package/get-shit-done/workflows/audit-fix.md +175 -0
- package/get-shit-done/workflows/audit-milestone.md +340 -0
- package/get-shit-done/workflows/audit-uat.md +109 -0
- package/get-shit-done/workflows/autonomous.md +789 -0
- package/get-shit-done/workflows/check-todos.md +179 -0
- package/get-shit-done/workflows/cleanup.md +154 -0
- package/get-shit-done/workflows/code-review-fix.md +497 -0
- package/get-shit-done/workflows/code-review.md +515 -0
- package/get-shit-done/workflows/complete-milestone.md +847 -0
- package/get-shit-done/workflows/diagnose-issues.md +238 -0
- package/get-shit-done/workflows/discovery-phase.md +291 -0
- package/get-shit-done/workflows/discuss-phase-assumptions.md +670 -0
- package/get-shit-done/workflows/discuss-phase-power.md +308 -0
- package/get-shit-done/workflows/discuss-phase.md +1378 -0
- package/get-shit-done/workflows/do.md +110 -0
- package/get-shit-done/workflows/docs-update.md +1155 -0
- package/get-shit-done/workflows/eval-review.md +155 -0
- package/get-shit-done/workflows/execute-phase.md +1677 -0
- package/get-shit-done/workflows/execute-plan.md +533 -0
- package/get-shit-done/workflows/explore.md +141 -0
- package/get-shit-done/workflows/extract_learnings.md +242 -0
- package/get-shit-done/workflows/fast.md +105 -0
- package/get-shit-done/workflows/forensics.md +265 -0
- package/get-shit-done/workflows/graduation.md +195 -0
- package/get-shit-done/workflows/health.md +314 -0
- package/get-shit-done/workflows/help.md +667 -0
- package/get-shit-done/workflows/import.md +246 -0
- package/get-shit-done/workflows/inbox.md +387 -0
- package/get-shit-done/workflows/ingest-docs.md +328 -0
- package/get-shit-done/workflows/insert-phase.md +130 -0
- package/get-shit-done/workflows/list-phase-assumptions.md +178 -0
- package/get-shit-done/workflows/list-workspaces.md +56 -0
- package/get-shit-done/workflows/manager.md +365 -0
- package/get-shit-done/workflows/map-codebase.md +393 -0
- package/get-shit-done/workflows/milestone-summary.md +223 -0
- package/get-shit-done/workflows/new-milestone.md +611 -0
- package/get-shit-done/workflows/new-project.md +1391 -0
- package/get-shit-done/workflows/new-workspace.md +239 -0
- package/get-shit-done/workflows/next.md +220 -0
- package/get-shit-done/workflows/node-repair.md +92 -0
- package/get-shit-done/workflows/note.md +158 -0
- package/get-shit-done/workflows/pause-work.md +243 -0
- package/get-shit-done/workflows/plan-milestone-gaps.md +273 -0
- package/get-shit-done/workflows/plan-phase.md +1349 -0
- package/get-shit-done/workflows/plan-review-convergence.md +254 -0
- package/get-shit-done/workflows/plant-seed.md +172 -0
- package/get-shit-done/workflows/pr-branch.md +157 -0
- package/get-shit-done/workflows/profile-user.md +452 -0
- package/get-shit-done/workflows/progress.md +619 -0
- package/get-shit-done/workflows/quick.md +970 -0
- package/get-shit-done/workflows/remove-phase.md +155 -0
- package/get-shit-done/workflows/remove-workspace.md +92 -0
- package/get-shit-done/workflows/research-phase.md +89 -0
- package/get-shit-done/workflows/resume-project.md +326 -0
- package/get-shit-done/workflows/review.md +344 -0
- package/get-shit-done/workflows/scan.md +102 -0
- package/get-shit-done/workflows/secure-phase.md +166 -0
- package/get-shit-done/workflows/session-report.md +146 -0
- package/get-shit-done/workflows/settings.md +319 -0
- package/get-shit-done/workflows/ship.md +302 -0
- package/get-shit-done/workflows/sketch-wrap-up.md +283 -0
- package/get-shit-done/workflows/sketch.md +286 -0
- package/get-shit-done/workflows/spec-phase.md +262 -0
- package/get-shit-done/workflows/spike-wrap-up.md +281 -0
- package/get-shit-done/workflows/spike.md +362 -0
- package/get-shit-done/workflows/stats.md +60 -0
- package/get-shit-done/workflows/sync-skills.md +182 -0
- package/get-shit-done/workflows/transition.md +693 -0
- package/get-shit-done/workflows/ui-phase.md +323 -0
- package/get-shit-done/workflows/ui-review.md +190 -0
- package/get-shit-done/workflows/ultraplan-phase.md +189 -0
- package/get-shit-done/workflows/undo.md +314 -0
- package/get-shit-done/workflows/update.md +587 -0
- package/get-shit-done/workflows/validate-phase.md +176 -0
- package/get-shit-done/workflows/verify-phase.md +465 -0
- package/get-shit-done/workflows/verify-work.md +740 -0
- package/hooks/dist/gsd-check-update-worker.js +108 -0
- package/hooks/dist/gsd-check-update.js +64 -0
- package/hooks/dist/gsd-context-monitor.js +192 -0
- package/hooks/dist/gsd-phase-boundary.sh +28 -0
- package/hooks/dist/gsd-prompt-guard.js +97 -0
- package/hooks/dist/gsd-read-guard.js +82 -0
- package/hooks/dist/gsd-read-injection-scanner.js +152 -0
- package/hooks/dist/gsd-session-state.sh +34 -0
- package/hooks/dist/gsd-statusline.js +293 -0
- package/hooks/dist/gsd-validate-commit.sh +48 -0
- package/hooks/dist/gsd-workflow-guard.js +94 -0
- package/hooks/gsd-check-update-worker.js +108 -0
- package/hooks/gsd-check-update.js +64 -0
- package/hooks/gsd-context-monitor.js +192 -0
- package/hooks/gsd-phase-boundary.sh +28 -0
- package/hooks/gsd-prompt-guard.js +97 -0
- package/hooks/gsd-read-guard.js +82 -0
- package/hooks/gsd-read-injection-scanner.js +152 -0
- package/hooks/gsd-session-state.sh +34 -0
- package/hooks/gsd-statusline.js +293 -0
- package/hooks/gsd-validate-commit.sh +48 -0
- package/hooks/gsd-workflow-guard.js +94 -0
- package/package.json +59 -0
- package/scripts/base64-scan.sh +262 -0
- package/scripts/build-hooks.js +95 -0
- package/scripts/gen-inventory-manifest.cjs +109 -0
- package/scripts/prompt-injection-scan.sh +201 -0
- package/scripts/run-tests.cjs +33 -0
- package/scripts/secret-scan.sh +227 -0
- package/sdk/package-lock.json +1998 -0
- package/sdk/package.json +52 -0
- package/sdk/prompts/agents/gsd-executor.md +110 -0
- package/sdk/prompts/agents/gsd-phase-researcher.md +158 -0
- package/sdk/prompts/agents/gsd-plan-checker.md +160 -0
- package/sdk/prompts/agents/gsd-planner.md +214 -0
- package/sdk/prompts/agents/gsd-project-researcher.md +323 -0
- package/sdk/prompts/agents/gsd-research-synthesizer.md +237 -0
- package/sdk/prompts/agents/gsd-roadmapper.md +670 -0
- package/sdk/prompts/agents/gsd-verifier.md +159 -0
- package/sdk/prompts/templates/project.md +186 -0
- package/sdk/prompts/templates/requirements.md +231 -0
- package/sdk/prompts/templates/research-project/ARCHITECTURE.md +204 -0
- package/sdk/prompts/templates/research-project/FEATURES.md +147 -0
- package/sdk/prompts/templates/research-project/PITFALLS.md +200 -0
- package/sdk/prompts/templates/research-project/STACK.md +120 -0
- package/sdk/prompts/templates/research-project/SUMMARY.md +170 -0
- package/sdk/prompts/templates/roadmap.md +202 -0
- package/sdk/prompts/templates/state.md +175 -0
- package/sdk/prompts/workflows/discuss-phase.md +126 -0
- package/sdk/prompts/workflows/execute-plan.md +106 -0
- package/sdk/prompts/workflows/plan-phase.md +84 -0
- package/sdk/prompts/workflows/research-phase.md +45 -0
- package/sdk/prompts/workflows/verify-phase.md +142 -0
- package/sdk/src/assembled-prompts.test.ts +349 -0
- package/sdk/src/cli-transport.test.ts +388 -0
- package/sdk/src/cli-transport.ts +130 -0
- package/sdk/src/cli.test.ts +383 -0
- package/sdk/src/cli.ts +670 -0
- package/sdk/src/config.test.ts +168 -0
- package/sdk/src/config.ts +177 -0
- package/sdk/src/context-engine.test.ts +295 -0
- package/sdk/src/context-engine.ts +170 -0
- package/sdk/src/context-truncation.test.ts +163 -0
- package/sdk/src/context-truncation.ts +233 -0
- package/sdk/src/e2e.integration.test.ts +178 -0
- package/sdk/src/errors.ts +72 -0
- package/sdk/src/event-stream.test.ts +661 -0
- package/sdk/src/event-stream.ts +441 -0
- package/sdk/src/failure-memory.test.ts +457 -0
- package/sdk/src/failure-memory.ts +1324 -0
- package/sdk/src/golden/capture.ts +95 -0
- package/sdk/src/golden/fixtures/generate-slug.golden.json +1 -0
- package/sdk/src/golden/fixtures/profile-sample-sessions/demo-project/sample.jsonl +3 -0
- package/sdk/src/golden/fixtures/summary-extract-sample.md +26 -0
- package/sdk/src/golden/fixtures/uat-render-checkpoint-sample.md +15 -0
- package/sdk/src/golden/golden-integration-covered.ts +30 -0
- package/sdk/src/golden/golden-mutation-covered.ts +7 -0
- package/sdk/src/golden/golden-policy.test.ts +8 -0
- package/sdk/src/golden/golden-policy.ts +112 -0
- package/sdk/src/golden/golden.integration.test.ts +373 -0
- package/sdk/src/golden/init-golden-normalize.ts +15 -0
- package/sdk/src/golden/read-only-golden-rows.ts +77 -0
- package/sdk/src/golden/read-only-parity.integration.test.ts +125 -0
- package/sdk/src/golden/registry-canonical-commands.ts +31 -0
- package/sdk/src/gsd-tools.test.ts +409 -0
- package/sdk/src/gsd-tools.ts +595 -0
- package/sdk/src/headless-prompts.test.ts +159 -0
- package/sdk/src/index.ts +333 -0
- package/sdk/src/init-e2e.integration.test.ts +136 -0
- package/sdk/src/init-runner.test.ts +783 -0
- package/sdk/src/init-runner.ts +735 -0
- package/sdk/src/lifecycle-e2e.integration.test.ts +258 -0
- package/sdk/src/logger.test.ts +149 -0
- package/sdk/src/logger.ts +113 -0
- package/sdk/src/milestone-runner.test.ts +421 -0
- package/sdk/src/phase-prompt.test.ts +538 -0
- package/sdk/src/phase-prompt.ts +264 -0
- package/sdk/src/phase-runner-types.test.ts +421 -0
- package/sdk/src/phase-runner.integration.test.ts +377 -0
- package/sdk/src/phase-runner.test.ts +2333 -0
- package/sdk/src/phase-runner.ts +1203 -0
- package/sdk/src/plan-parser.test.ts +528 -0
- package/sdk/src/plan-parser.ts +427 -0
- package/sdk/src/prompt-builder.test.ts +306 -0
- package/sdk/src/prompt-builder.ts +193 -0
- package/sdk/src/prompt-sanitizer.test.ts +260 -0
- package/sdk/src/prompt-sanitizer.ts +71 -0
- package/sdk/src/query/QUERY-HANDLERS.md +317 -0
- package/sdk/src/query/audit-open.ts +722 -0
- package/sdk/src/query/check-auto-mode.test.ts +77 -0
- package/sdk/src/query/check-auto-mode.ts +50 -0
- package/sdk/src/query/check-completion.test.ts +113 -0
- package/sdk/src/query/check-completion.ts +182 -0
- package/sdk/src/query/check-gates.test.ts +103 -0
- package/sdk/src/query/check-gates.ts +112 -0
- package/sdk/src/query/check-ship-ready.test.ts +77 -0
- package/sdk/src/query/check-ship-ready.ts +103 -0
- package/sdk/src/query/check-verification-status.test.ts +143 -0
- package/sdk/src/query/check-verification-status.ts +160 -0
- package/sdk/src/query/commit.test.ts +202 -0
- package/sdk/src/query/commit.ts +301 -0
- package/sdk/src/query/config-gates.test.ts +89 -0
- package/sdk/src/query/config-gates.ts +69 -0
- package/sdk/src/query/config-mutation.test.ts +365 -0
- package/sdk/src/query/config-mutation.ts +497 -0
- package/sdk/src/query/config-query.test.ts +161 -0
- package/sdk/src/query/config-query.ts +190 -0
- package/sdk/src/query/context-history.test.ts +165 -0
- package/sdk/src/query/context-history.ts +467 -0
- package/sdk/src/query/decomposed-handlers.test.ts +365 -0
- package/sdk/src/query/detect-custom-files.ts +97 -0
- package/sdk/src/query/detect-phase-type.test.ts +105 -0
- package/sdk/src/query/detect-phase-type.ts +141 -0
- package/sdk/src/query/docs-init.ts +257 -0
- package/sdk/src/query/failure-capture.ts +58 -0
- package/sdk/src/query/frontmatter-array.test.ts +14 -0
- package/sdk/src/query/frontmatter-mutation.test.ts +259 -0
- package/sdk/src/query/frontmatter-mutation.ts +343 -0
- package/sdk/src/query/frontmatter.test.ts +281 -0
- package/sdk/src/query/frontmatter.ts +397 -0
- package/sdk/src/query/helpers.test.ts +426 -0
- package/sdk/src/query/helpers.ts +482 -0
- package/sdk/src/query/index.ts +586 -0
- package/sdk/src/query/init-complex.test.ts +232 -0
- package/sdk/src/query/init-complex.ts +578 -0
- package/sdk/src/query/init.test.ts +522 -0
- package/sdk/src/query/init.ts +1046 -0
- package/sdk/src/query/intel.test.ts +90 -0
- package/sdk/src/query/intel.ts +404 -0
- package/sdk/src/query/normalize-query-command.test.ts +50 -0
- package/sdk/src/query/normalize-query-command.ts +56 -0
- package/sdk/src/query/phase-lifecycle.test.ts +1126 -0
- package/sdk/src/query/phase-lifecycle.ts +1799 -0
- package/sdk/src/query/phase-list-queries.test.ts +88 -0
- package/sdk/src/query/phase-list-queries.ts +152 -0
- package/sdk/src/query/phase-ready.test.ts +65 -0
- package/sdk/src/query/phase-ready.ts +158 -0
- package/sdk/src/query/phase.test.ts +307 -0
- package/sdk/src/query/phase.ts +340 -0
- package/sdk/src/query/pipeline.test.ts +169 -0
- package/sdk/src/query/pipeline.ts +243 -0
- package/sdk/src/query/plan-execution-route.test.ts +166 -0
- package/sdk/src/query/plan-execution-route.ts +209 -0
- package/sdk/src/query/plan-task-structure.test.ts +65 -0
- package/sdk/src/query/plan-task-structure.ts +63 -0
- package/sdk/src/query/profile-extract-messages.ts +247 -0
- package/sdk/src/query/profile-output.ts +908 -0
- package/sdk/src/query/profile-questionnaire-data.ts +181 -0
- package/sdk/src/query/profile-sample.ts +184 -0
- package/sdk/src/query/profile-scan-sessions.ts +174 -0
- package/sdk/src/query/profile.test.ts +74 -0
- package/sdk/src/query/profile.ts +337 -0
- package/sdk/src/query/progress.test.ts +156 -0
- package/sdk/src/query/progress.ts +566 -0
- package/sdk/src/query/registry.test.ts +216 -0
- package/sdk/src/query/registry.ts +174 -0
- package/sdk/src/query/requirements-extract-from-plans.test.ts +58 -0
- package/sdk/src/query/requirements-extract-from-plans.ts +86 -0
- package/sdk/src/query/roadmap-update-plan-progress.ts +132 -0
- package/sdk/src/query/roadmap.test.ts +359 -0
- package/sdk/src/query/roadmap.ts +591 -0
- package/sdk/src/query/route-next-action.test.ts +61 -0
- package/sdk/src/query/route-next-action.ts +345 -0
- package/sdk/src/query/runtime-health.ts +7 -0
- package/sdk/src/query/schema-detect.ts +189 -0
- package/sdk/src/query/skill-manifest.ts +214 -0
- package/sdk/src/query/skills.test.ts +80 -0
- package/sdk/src/query/skills.ts +62 -0
- package/sdk/src/query/state-mutation.test.ts +450 -0
- package/sdk/src/query/state-mutation.ts +1444 -0
- package/sdk/src/query/state-project-load.ts +109 -0
- package/sdk/src/query/state.test.ts +347 -0
- package/sdk/src/query/state.ts +397 -0
- package/sdk/src/query/summary.test.ts +95 -0
- package/sdk/src/query/summary.ts +296 -0
- package/sdk/src/query/template.test.ts +180 -0
- package/sdk/src/query/template.ts +242 -0
- package/sdk/src/query/uat.test.ts +77 -0
- package/sdk/src/query/uat.ts +314 -0
- package/sdk/src/query/utils.test.ts +82 -0
- package/sdk/src/query/utils.ts +92 -0
- package/sdk/src/query/validate.test.ts +656 -0
- package/sdk/src/query/validate.ts +807 -0
- package/sdk/src/query/verify.test.ts +414 -0
- package/sdk/src/query/verify.ts +645 -0
- package/sdk/src/query/websearch.test.ts +31 -0
- package/sdk/src/query/websearch.ts +82 -0
- package/sdk/src/query/workspace.test.ts +119 -0
- package/sdk/src/query/workspace.ts +131 -0
- package/sdk/src/query/workstream.test.ts +51 -0
- package/sdk/src/query/workstream.ts +434 -0
- package/sdk/src/research-gate.test.ts +190 -0
- package/sdk/src/research-gate.ts +94 -0
- package/sdk/src/runtime-health.test.ts +176 -0
- package/sdk/src/runtime-health.ts +387 -0
- package/sdk/src/session-runner.test.ts +98 -0
- package/sdk/src/session-runner.ts +299 -0
- package/sdk/src/tool-scoping.test.ts +160 -0
- package/sdk/src/tool-scoping.ts +61 -0
- package/sdk/src/types.ts +917 -0
- package/sdk/src/workstream-utils.ts +33 -0
- package/sdk/src/ws-flag.test.ts +285 -0
- package/sdk/src/ws-transport.test.ts +161 -0
- package/sdk/src/ws-transport.ts +93 -0
- package/sdk/tsconfig.json +20 -0
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import { loadConfig, CONFIG_DEFAULTS } from './config.js';
|
|
3
|
+
import { mkdir, writeFile, rm } from 'node:fs/promises';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import { tmpdir } from 'node:os';
|
|
6
|
+
|
|
7
|
+
describe('loadConfig', () => {
|
|
8
|
+
let tmpDir: string;
|
|
9
|
+
|
|
10
|
+
beforeEach(async () => {
|
|
11
|
+
tmpDir = join(tmpdir(), `gsd-config-test-${Date.now()}-${Math.random().toString(36).slice(2)}`);
|
|
12
|
+
await mkdir(join(tmpDir, '.planning'), { recursive: true });
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
afterEach(async () => {
|
|
16
|
+
await rm(tmpDir, { recursive: true, force: true });
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('returns all defaults when config file is missing', async () => {
|
|
20
|
+
// No config.json created
|
|
21
|
+
await rm(join(tmpDir, '.planning', 'config.json'), { force: true });
|
|
22
|
+
const config = await loadConfig(tmpDir);
|
|
23
|
+
expect(config).toEqual(CONFIG_DEFAULTS);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('returns all defaults when config file is empty', async () => {
|
|
27
|
+
await writeFile(join(tmpDir, '.planning', 'config.json'), '');
|
|
28
|
+
const config = await loadConfig(tmpDir);
|
|
29
|
+
expect(config).toEqual(CONFIG_DEFAULTS);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('loads valid config and merges with defaults', async () => {
|
|
33
|
+
const userConfig = {
|
|
34
|
+
model_profile: 'fast',
|
|
35
|
+
workflow: { research: false },
|
|
36
|
+
};
|
|
37
|
+
await writeFile(
|
|
38
|
+
join(tmpDir, '.planning', 'config.json'),
|
|
39
|
+
JSON.stringify(userConfig),
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
const config = await loadConfig(tmpDir);
|
|
43
|
+
|
|
44
|
+
expect(config.model_profile).toBe('fast');
|
|
45
|
+
expect(config.workflow.research).toBe(false);
|
|
46
|
+
// Other workflow defaults preserved
|
|
47
|
+
expect(config.workflow.plan_check).toBe(true);
|
|
48
|
+
expect(config.workflow.verifier).toBe(true);
|
|
49
|
+
// Top-level defaults preserved
|
|
50
|
+
expect(config.commit_docs).toBe(true);
|
|
51
|
+
expect(config.parallelization).toBe(true);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('partial config merges correctly for nested objects', async () => {
|
|
55
|
+
const userConfig = {
|
|
56
|
+
git: { branching_strategy: 'milestone' },
|
|
57
|
+
hooks: { context_warnings: false },
|
|
58
|
+
};
|
|
59
|
+
await writeFile(
|
|
60
|
+
join(tmpDir, '.planning', 'config.json'),
|
|
61
|
+
JSON.stringify(userConfig),
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
const config = await loadConfig(tmpDir);
|
|
65
|
+
|
|
66
|
+
expect(config.git.branching_strategy).toBe('milestone');
|
|
67
|
+
// Other git defaults preserved
|
|
68
|
+
expect(config.git.phase_branch_template).toBe('gsd/phase-{phase}-{slug}');
|
|
69
|
+
expect(config.hooks.context_warnings).toBe(false);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('preserves unknown top-level keys', async () => {
|
|
73
|
+
const userConfig = { custom_key: 'custom_value' };
|
|
74
|
+
await writeFile(
|
|
75
|
+
join(tmpDir, '.planning', 'config.json'),
|
|
76
|
+
JSON.stringify(userConfig),
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
const config = await loadConfig(tmpDir);
|
|
80
|
+
expect(config.custom_key).toBe('custom_value');
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it('merges agent_skills', async () => {
|
|
84
|
+
const userConfig = {
|
|
85
|
+
agent_skills: { planner: 'custom-skill' },
|
|
86
|
+
};
|
|
87
|
+
await writeFile(
|
|
88
|
+
join(tmpDir, '.planning', 'config.json'),
|
|
89
|
+
JSON.stringify(userConfig),
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
const config = await loadConfig(tmpDir);
|
|
93
|
+
expect(config.agent_skills).toEqual({ planner: 'custom-skill' });
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
// ─── Negative tests ─────────────────────────────────────────────────────
|
|
97
|
+
|
|
98
|
+
it('throws on malformed JSON', async () => {
|
|
99
|
+
await writeFile(
|
|
100
|
+
join(tmpDir, '.planning', 'config.json'),
|
|
101
|
+
'{bad json',
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
await expect(loadConfig(tmpDir)).rejects.toThrow(/Failed to parse config/);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('throws when config is not an object (array)', async () => {
|
|
108
|
+
await writeFile(
|
|
109
|
+
join(tmpDir, '.planning', 'config.json'),
|
|
110
|
+
'[1, 2, 3]',
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
await expect(loadConfig(tmpDir)).rejects.toThrow(/must be a JSON object/);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it('throws when config is not an object (string)', async () => {
|
|
117
|
+
await writeFile(
|
|
118
|
+
join(tmpDir, '.planning', 'config.json'),
|
|
119
|
+
'"just a string"',
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
await expect(loadConfig(tmpDir)).rejects.toThrow(/must be a JSON object/);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it('ignores unknown keys without error', async () => {
|
|
126
|
+
const userConfig = {
|
|
127
|
+
totally_unknown: true,
|
|
128
|
+
another_unknown: { nested: 'value' },
|
|
129
|
+
};
|
|
130
|
+
await writeFile(
|
|
131
|
+
join(tmpDir, '.planning', 'config.json'),
|
|
132
|
+
JSON.stringify(userConfig),
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
const config = await loadConfig(tmpDir);
|
|
136
|
+
// Should load fine, with unknowns passed through
|
|
137
|
+
expect(config.model_profile).toBe('balanced');
|
|
138
|
+
expect((config as Record<string, unknown>).totally_unknown).toBe(true);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it('handles wrong value types gracefully (user sets string instead of bool)', async () => {
|
|
142
|
+
const userConfig = {
|
|
143
|
+
commit_docs: 'yes', // should be boolean but we don't validate types
|
|
144
|
+
parallelization: 0,
|
|
145
|
+
};
|
|
146
|
+
await writeFile(
|
|
147
|
+
join(tmpDir, '.planning', 'config.json'),
|
|
148
|
+
JSON.stringify(userConfig),
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
const config = await loadConfig(tmpDir);
|
|
152
|
+
// We pass through the user's values as-is — runtime code handles type mismatches
|
|
153
|
+
expect(config.commit_docs).toBe('yes');
|
|
154
|
+
expect(config.parallelization).toBe(0);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
it('does not mutate CONFIG_DEFAULTS between calls', async () => {
|
|
158
|
+
const before = structuredClone(CONFIG_DEFAULTS);
|
|
159
|
+
|
|
160
|
+
await writeFile(
|
|
161
|
+
join(tmpDir, '.planning', 'config.json'),
|
|
162
|
+
JSON.stringify({ model_profile: 'fast', workflow: { research: false } }),
|
|
163
|
+
);
|
|
164
|
+
await loadConfig(tmpDir);
|
|
165
|
+
|
|
166
|
+
expect(CONFIG_DEFAULTS).toEqual(before);
|
|
167
|
+
});
|
|
168
|
+
});
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Config reader — loads `.planning/config.json` and merges with defaults.
|
|
3
|
+
*
|
|
4
|
+
* Mirrors the default structure from `get-shit-done/bin/lib/config.cjs`
|
|
5
|
+
* `buildNewProjectConfig()`.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { readFile } from 'node:fs/promises';
|
|
9
|
+
import { join } from 'node:path';
|
|
10
|
+
import { relPlanningPath } from './workstream-utils.js';
|
|
11
|
+
|
|
12
|
+
// ─── Types ───────────────────────────────────────────────────────────────────
|
|
13
|
+
|
|
14
|
+
export interface GitConfig {
|
|
15
|
+
branching_strategy: string;
|
|
16
|
+
phase_branch_template: string;
|
|
17
|
+
milestone_branch_template: string;
|
|
18
|
+
quick_branch_template: string | null;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface WorkflowConfig {
|
|
22
|
+
research: boolean;
|
|
23
|
+
plan_check: boolean;
|
|
24
|
+
verifier: boolean;
|
|
25
|
+
nyquist_validation: boolean;
|
|
26
|
+
/** Mirrors gsd-tools flat `config.tdd_mode` (from `workflow.tdd_mode`). */
|
|
27
|
+
tdd_mode: boolean;
|
|
28
|
+
auto_advance: boolean;
|
|
29
|
+
node_repair: boolean;
|
|
30
|
+
node_repair_budget: number;
|
|
31
|
+
ui_phase: boolean;
|
|
32
|
+
ui_safety_gate: boolean;
|
|
33
|
+
text_mode: boolean;
|
|
34
|
+
research_before_questions: boolean;
|
|
35
|
+
discuss_mode: string;
|
|
36
|
+
skip_discuss: boolean;
|
|
37
|
+
/** Maximum self-discuss passes in auto/headless mode before forcing proceed. Default: 3. */
|
|
38
|
+
max_discuss_passes: number;
|
|
39
|
+
/** Subagent timeout in ms (matches `get-shit-done/bin/lib/core.cjs` default 300000). */
|
|
40
|
+
subagent_timeout: number;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface HooksConfig {
|
|
44
|
+
context_warnings: boolean;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface GSDConfig {
|
|
48
|
+
model_profile: string;
|
|
49
|
+
commit_docs: boolean;
|
|
50
|
+
parallelization: boolean;
|
|
51
|
+
search_gitignored: boolean;
|
|
52
|
+
brave_search: boolean;
|
|
53
|
+
firecrawl: boolean;
|
|
54
|
+
exa_search: boolean;
|
|
55
|
+
git: GitConfig;
|
|
56
|
+
workflow: WorkflowConfig;
|
|
57
|
+
hooks: HooksConfig;
|
|
58
|
+
agent_skills: Record<string, unknown>;
|
|
59
|
+
/** Project slug for branch templates; mirrors gsd-tools `config.project_code`. */
|
|
60
|
+
project_code?: string | null;
|
|
61
|
+
/** Interactive vs headless; mirrors gsd-tools flat `config.mode`. */
|
|
62
|
+
mode?: string;
|
|
63
|
+
/** Internal auto-chain flag; mirrors gsd-tools `config._auto_chain_active`. */
|
|
64
|
+
_auto_chain_active?: boolean;
|
|
65
|
+
[key: string]: unknown;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// ─── Defaults ────────────────────────────────────────────────────────────────
|
|
69
|
+
|
|
70
|
+
export const CONFIG_DEFAULTS: GSDConfig = {
|
|
71
|
+
model_profile: 'balanced',
|
|
72
|
+
commit_docs: true,
|
|
73
|
+
parallelization: true,
|
|
74
|
+
search_gitignored: false,
|
|
75
|
+
brave_search: false,
|
|
76
|
+
firecrawl: false,
|
|
77
|
+
exa_search: false,
|
|
78
|
+
git: {
|
|
79
|
+
branching_strategy: 'none',
|
|
80
|
+
phase_branch_template: 'gsd/phase-{phase}-{slug}',
|
|
81
|
+
milestone_branch_template: 'gsd/{milestone}-{slug}',
|
|
82
|
+
quick_branch_template: null,
|
|
83
|
+
},
|
|
84
|
+
workflow: {
|
|
85
|
+
research: true,
|
|
86
|
+
plan_check: true,
|
|
87
|
+
verifier: true,
|
|
88
|
+
nyquist_validation: true,
|
|
89
|
+
tdd_mode: false,
|
|
90
|
+
auto_advance: false,
|
|
91
|
+
node_repair: true,
|
|
92
|
+
node_repair_budget: 2,
|
|
93
|
+
ui_phase: true,
|
|
94
|
+
ui_safety_gate: true,
|
|
95
|
+
text_mode: false,
|
|
96
|
+
research_before_questions: false,
|
|
97
|
+
discuss_mode: 'discuss',
|
|
98
|
+
skip_discuss: false,
|
|
99
|
+
max_discuss_passes: 3,
|
|
100
|
+
subagent_timeout: 300000,
|
|
101
|
+
},
|
|
102
|
+
hooks: {
|
|
103
|
+
context_warnings: true,
|
|
104
|
+
},
|
|
105
|
+
agent_skills: {},
|
|
106
|
+
project_code: null,
|
|
107
|
+
mode: 'interactive',
|
|
108
|
+
_auto_chain_active: false,
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
// ─── Loader ──────────────────────────────────────────────────────────────────
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Load project config from `.planning/config.json`, merging with defaults.
|
|
115
|
+
* Returns full defaults when file is missing or empty.
|
|
116
|
+
* Throws on malformed JSON with a helpful error message.
|
|
117
|
+
*/
|
|
118
|
+
export async function loadConfig(projectDir: string, workstream?: string): Promise<GSDConfig> {
|
|
119
|
+
const configPath = join(projectDir, relPlanningPath(workstream), 'config.json');
|
|
120
|
+
const rootConfigPath = join(projectDir, '.planning', 'config.json');
|
|
121
|
+
|
|
122
|
+
let raw: string;
|
|
123
|
+
try {
|
|
124
|
+
raw = await readFile(configPath, 'utf-8');
|
|
125
|
+
} catch {
|
|
126
|
+
// If workstream config missing, fall back to root config
|
|
127
|
+
if (workstream) {
|
|
128
|
+
try {
|
|
129
|
+
raw = await readFile(rootConfigPath, 'utf-8');
|
|
130
|
+
} catch {
|
|
131
|
+
return structuredClone(CONFIG_DEFAULTS);
|
|
132
|
+
}
|
|
133
|
+
} else {
|
|
134
|
+
// File missing — normal for new projects
|
|
135
|
+
return structuredClone(CONFIG_DEFAULTS);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const trimmed = raw.trim();
|
|
140
|
+
if (trimmed === '') {
|
|
141
|
+
return structuredClone(CONFIG_DEFAULTS);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
let parsed: Record<string, unknown>;
|
|
145
|
+
try {
|
|
146
|
+
parsed = JSON.parse(trimmed);
|
|
147
|
+
} catch (err) {
|
|
148
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
149
|
+
throw new Error(`Failed to parse config at ${configPath}: ${msg}`);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
|
|
153
|
+
throw new Error(`Config at ${configPath} must be a JSON object`);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Three-level deep merge: defaults <- parsed
|
|
157
|
+
return {
|
|
158
|
+
...structuredClone(CONFIG_DEFAULTS),
|
|
159
|
+
...parsed,
|
|
160
|
+
git: {
|
|
161
|
+
...CONFIG_DEFAULTS.git,
|
|
162
|
+
...(parsed.git as Partial<GitConfig> ?? {}),
|
|
163
|
+
},
|
|
164
|
+
workflow: {
|
|
165
|
+
...CONFIG_DEFAULTS.workflow,
|
|
166
|
+
...(parsed.workflow as Partial<WorkflowConfig> ?? {}),
|
|
167
|
+
},
|
|
168
|
+
hooks: {
|
|
169
|
+
...CONFIG_DEFAULTS.hooks,
|
|
170
|
+
...(parsed.hooks as Partial<HooksConfig> ?? {}),
|
|
171
|
+
},
|
|
172
|
+
agent_skills: {
|
|
173
|
+
...CONFIG_DEFAULTS.agent_skills,
|
|
174
|
+
...(parsed.agent_skills as Record<string, unknown> ?? {}),
|
|
175
|
+
},
|
|
176
|
+
};
|
|
177
|
+
}
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
2
|
+
import { mkdtemp, mkdir, writeFile, rm } from 'node:fs/promises';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { tmpdir } from 'node:os';
|
|
5
|
+
import { ContextEngine, PHASE_FILE_MANIFEST } from './context-engine.js';
|
|
6
|
+
import { PhaseType } from './types.js';
|
|
7
|
+
import type { GSDLogger } from './logger.js';
|
|
8
|
+
|
|
9
|
+
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
10
|
+
|
|
11
|
+
async function createTempProject(): Promise<string> {
|
|
12
|
+
return mkdtemp(join(tmpdir(), 'gsd-ctx-'));
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async function createPlanningDir(projectDir: string, files: Record<string, string>): Promise<void> {
|
|
16
|
+
const planningDir = join(projectDir, '.planning');
|
|
17
|
+
await mkdir(planningDir, { recursive: true });
|
|
18
|
+
for (const [filename, content] of Object.entries(files)) {
|
|
19
|
+
await writeFile(join(planningDir, filename), content, 'utf-8');
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function makeMockLogger(): GSDLogger {
|
|
24
|
+
return {
|
|
25
|
+
debug: vi.fn(),
|
|
26
|
+
info: vi.fn(),
|
|
27
|
+
warn: vi.fn(),
|
|
28
|
+
error: vi.fn(),
|
|
29
|
+
setPhase: vi.fn(),
|
|
30
|
+
setPlan: vi.fn(),
|
|
31
|
+
setSessionId: vi.fn(),
|
|
32
|
+
} as unknown as GSDLogger;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// ─── Tests ───────────────────────────────────────────────────────────────────
|
|
36
|
+
|
|
37
|
+
describe('ContextEngine', () => {
|
|
38
|
+
let projectDir: string;
|
|
39
|
+
|
|
40
|
+
beforeEach(async () => {
|
|
41
|
+
projectDir = await createTempProject();
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
afterEach(async () => {
|
|
45
|
+
await rm(projectDir, { recursive: true, force: true });
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
describe('resolveContextFiles', () => {
|
|
49
|
+
it('returns all files for plan phase when all exist', async () => {
|
|
50
|
+
await createPlanningDir(projectDir, {
|
|
51
|
+
'STATE.md': '# State\nproject: test',
|
|
52
|
+
'ROADMAP.md': '# Roadmap\nphase 01',
|
|
53
|
+
'CONTEXT.md': '# Context\nstack: node',
|
|
54
|
+
'RESEARCH.md': '# Research\nfindings here',
|
|
55
|
+
'REQUIREMENTS.md': '# Requirements\nR1: auth',
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
const engine = new ContextEngine(projectDir);
|
|
59
|
+
const files = await engine.resolveContextFiles(PhaseType.Plan);
|
|
60
|
+
|
|
61
|
+
expect(files.state).toBe('# State\nproject: test');
|
|
62
|
+
expect(files.roadmap).toBe('# Roadmap\nphase 01');
|
|
63
|
+
expect(files.context).toBe('# Context\nstack: node');
|
|
64
|
+
expect(files.research).toBe('# Research\nfindings here');
|
|
65
|
+
expect(files.requirements).toBe('# Requirements\nR1: auth');
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('returns minimal files for execute phase', async () => {
|
|
69
|
+
await createPlanningDir(projectDir, {
|
|
70
|
+
'STATE.md': '# State',
|
|
71
|
+
'config.json': '{"model":"claude"}',
|
|
72
|
+
'ROADMAP.md': '# Roadmap — should not be read',
|
|
73
|
+
'CONTEXT.md': '# Context — should not be read',
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
const engine = new ContextEngine(projectDir);
|
|
77
|
+
const files = await engine.resolveContextFiles(PhaseType.Execute);
|
|
78
|
+
|
|
79
|
+
expect(files.state).toBe('# State');
|
|
80
|
+
expect(files.config).toBe('{"model":"claude"}');
|
|
81
|
+
expect(files.roadmap).toBeUndefined();
|
|
82
|
+
expect(files.context).toBeUndefined();
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('returns state + roadmap + context for research phase', async () => {
|
|
86
|
+
await createPlanningDir(projectDir, {
|
|
87
|
+
'STATE.md': '# State',
|
|
88
|
+
'ROADMAP.md': '# Roadmap',
|
|
89
|
+
'CONTEXT.md': '# Context',
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
const engine = new ContextEngine(projectDir);
|
|
93
|
+
const files = await engine.resolveContextFiles(PhaseType.Research);
|
|
94
|
+
|
|
95
|
+
expect(files.state).toBe('# State');
|
|
96
|
+
expect(files.roadmap).toBe('# Roadmap');
|
|
97
|
+
expect(files.context).toBe('# Context');
|
|
98
|
+
expect(files.requirements).toBeUndefined();
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('returns state + roadmap + requirements for verify phase', async () => {
|
|
102
|
+
await createPlanningDir(projectDir, {
|
|
103
|
+
'STATE.md': '# State',
|
|
104
|
+
'ROADMAP.md': '# Roadmap',
|
|
105
|
+
'REQUIREMENTS.md': '# Requirements',
|
|
106
|
+
'PLAN.md': '# Plan',
|
|
107
|
+
'SUMMARY.md': '# Summary',
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
const engine = new ContextEngine(projectDir);
|
|
111
|
+
const files = await engine.resolveContextFiles(PhaseType.Verify);
|
|
112
|
+
|
|
113
|
+
expect(files.state).toBe('# State');
|
|
114
|
+
expect(files.roadmap).toBe('# Roadmap');
|
|
115
|
+
expect(files.requirements).toBe('# Requirements');
|
|
116
|
+
expect(files.plan).toBe('# Plan');
|
|
117
|
+
expect(files.summary).toBe('# Summary');
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it('returns state + optional files for discuss phase', async () => {
|
|
121
|
+
await createPlanningDir(projectDir, {
|
|
122
|
+
'STATE.md': '# State',
|
|
123
|
+
'ROADMAP.md': '# Roadmap',
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
const engine = new ContextEngine(projectDir);
|
|
127
|
+
const files = await engine.resolveContextFiles(PhaseType.Discuss);
|
|
128
|
+
|
|
129
|
+
expect(files.state).toBe('# State');
|
|
130
|
+
expect(files.roadmap).toBe('# Roadmap');
|
|
131
|
+
expect(files.context).toBeUndefined();
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it('returns undefined for missing optional files without warning', async () => {
|
|
135
|
+
await createPlanningDir(projectDir, {
|
|
136
|
+
'STATE.md': '# State',
|
|
137
|
+
'ROADMAP.md': '# Roadmap',
|
|
138
|
+
'CONTEXT.md': '# Context',
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
const logger = makeMockLogger();
|
|
142
|
+
const engine = new ContextEngine(projectDir, logger);
|
|
143
|
+
const files = await engine.resolveContextFiles(PhaseType.Plan);
|
|
144
|
+
|
|
145
|
+
// research and requirements are optional for plan — no warning
|
|
146
|
+
expect(files.research).toBeUndefined();
|
|
147
|
+
expect(files.requirements).toBeUndefined();
|
|
148
|
+
expect(logger.warn).not.toHaveBeenCalled();
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
it('warns for missing required files', async () => {
|
|
152
|
+
// Empty .planning dir — STATE.md is required for all phases
|
|
153
|
+
await createPlanningDir(projectDir, {});
|
|
154
|
+
|
|
155
|
+
const logger = makeMockLogger();
|
|
156
|
+
const engine = new ContextEngine(projectDir, logger);
|
|
157
|
+
await engine.resolveContextFiles(PhaseType.Execute);
|
|
158
|
+
|
|
159
|
+
expect(logger.warn).toHaveBeenCalledWith(
|
|
160
|
+
expect.stringContaining('STATE.md'),
|
|
161
|
+
expect.objectContaining({ phase: PhaseType.Execute }),
|
|
162
|
+
);
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it('handles missing .planning directory gracefully', async () => {
|
|
166
|
+
// No .planning dir at all
|
|
167
|
+
const engine = new ContextEngine(projectDir);
|
|
168
|
+
const files = await engine.resolveContextFiles(PhaseType.Execute);
|
|
169
|
+
|
|
170
|
+
expect(files.state).toBeUndefined();
|
|
171
|
+
expect(files.config).toBeUndefined();
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
it('handles empty file content', async () => {
|
|
175
|
+
await createPlanningDir(projectDir, {
|
|
176
|
+
'STATE.md': '',
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
const engine = new ContextEngine(projectDir);
|
|
180
|
+
const files = await engine.resolveContextFiles(PhaseType.Execute);
|
|
181
|
+
|
|
182
|
+
// Empty string is still defined — the file exists
|
|
183
|
+
expect(files.state).toBe('');
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
describe('context truncation', () => {
|
|
188
|
+
it('truncates files exceeding maxContentLength', async () => {
|
|
189
|
+
const largeContent = Array.from({ length: 100 }, (_, i) =>
|
|
190
|
+
`## Section ${i}\n\nFirst paragraph.\n\nLong detail ${'x'.repeat(200)}.`
|
|
191
|
+
).join('\n\n');
|
|
192
|
+
|
|
193
|
+
await createPlanningDir(projectDir, {
|
|
194
|
+
'STATE.md': '# State',
|
|
195
|
+
'ROADMAP.md': '# Roadmap',
|
|
196
|
+
'CONTEXT.md': largeContent,
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
const engine = new ContextEngine(projectDir, undefined, { maxContentLength: 500 });
|
|
200
|
+
const files = await engine.resolveContextFiles(PhaseType.Plan);
|
|
201
|
+
|
|
202
|
+
// CONTEXT.md should be truncated
|
|
203
|
+
expect(files.context!.length).toBeLessThan(largeContent.length);
|
|
204
|
+
expect(files.context).toContain('[...');
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
it('does not truncate files below threshold', async () => {
|
|
208
|
+
await createPlanningDir(projectDir, {
|
|
209
|
+
'STATE.md': '# State\nproject: test',
|
|
210
|
+
'ROADMAP.md': '# Roadmap\nphase 01',
|
|
211
|
+
'CONTEXT.md': '# Context\nstack: node',
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
const engine = new ContextEngine(projectDir);
|
|
215
|
+
const files = await engine.resolveContextFiles(PhaseType.Plan);
|
|
216
|
+
|
|
217
|
+
expect(files.context).toBe('# Context\nstack: node');
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
it('never truncates STATE.md (not in truncatable list)', async () => {
|
|
221
|
+
const largeState = `# State\n\n${'x'.repeat(20000)}`;
|
|
222
|
+
await createPlanningDir(projectDir, {
|
|
223
|
+
'STATE.md': largeState,
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
const engine = new ContextEngine(projectDir, undefined, { maxContentLength: 100 });
|
|
227
|
+
const files = await engine.resolveContextFiles(PhaseType.Execute);
|
|
228
|
+
|
|
229
|
+
expect(files.state).toBe(largeState);
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
it('extracts current milestone from ROADMAP.md when state is available', async () => {
|
|
233
|
+
const roadmap = `# Roadmap
|
|
234
|
+
|
|
235
|
+
## Milestone 1: Setup
|
|
236
|
+
### Phase 01
|
|
237
|
+
Setup content.
|
|
238
|
+
|
|
239
|
+
## Milestone 2: Build
|
|
240
|
+
### Phase 02
|
|
241
|
+
Build content.`;
|
|
242
|
+
|
|
243
|
+
await createPlanningDir(projectDir, {
|
|
244
|
+
'STATE.md': 'Current Milestone: Build',
|
|
245
|
+
'ROADMAP.md': roadmap,
|
|
246
|
+
'CONTEXT.md': '# Context',
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
const engine = new ContextEngine(projectDir);
|
|
250
|
+
const files = await engine.resolveContextFiles(PhaseType.Plan);
|
|
251
|
+
|
|
252
|
+
expect(files.roadmap).toContain('## Milestone 2: Build');
|
|
253
|
+
expect(files.roadmap).not.toContain('### Phase 01');
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
it('respects custom truncation options', async () => {
|
|
257
|
+
const content = '## Heading\n\nParagraph.\n\nMore.\n' + 'x'.repeat(500);
|
|
258
|
+
await createPlanningDir(projectDir, {
|
|
259
|
+
'STATE.md': '# State',
|
|
260
|
+
'ROADMAP.md': '# Roadmap',
|
|
261
|
+
'CONTEXT.md': content,
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
// Low threshold forces truncation
|
|
265
|
+
const engine = new ContextEngine(projectDir, undefined, { maxContentLength: 50 });
|
|
266
|
+
const files = await engine.resolveContextFiles(PhaseType.Plan);
|
|
267
|
+
expect(files.context!.length).toBeLessThan(content.length);
|
|
268
|
+
});
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
describe('PHASE_FILE_MANIFEST', () => {
|
|
272
|
+
it('covers all phase types', () => {
|
|
273
|
+
for (const phase of Object.values(PhaseType)) {
|
|
274
|
+
expect(PHASE_FILE_MANIFEST[phase]).toBeDefined();
|
|
275
|
+
expect(PHASE_FILE_MANIFEST[phase].length).toBeGreaterThan(0);
|
|
276
|
+
}
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
it('execute phase has fewest files', () => {
|
|
280
|
+
const executeCount = PHASE_FILE_MANIFEST[PhaseType.Execute].length;
|
|
281
|
+
const planCount = PHASE_FILE_MANIFEST[PhaseType.Plan].length;
|
|
282
|
+
expect(executeCount).toBeLessThan(planCount);
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
it('every spec has required key, filename, and required flag', () => {
|
|
286
|
+
for (const specs of Object.values(PHASE_FILE_MANIFEST)) {
|
|
287
|
+
for (const spec of specs) {
|
|
288
|
+
expect(spec.key).toBeDefined();
|
|
289
|
+
expect(spec.filename).toBeDefined();
|
|
290
|
+
expect(typeof spec.required).toBe('boolean');
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
});
|
|
294
|
+
});
|
|
295
|
+
});
|