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,656 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for validation query handlers — verifyKeyLinks, validateConsistency, validateHealth.
|
|
3
|
+
*
|
|
4
|
+
* Uses temp directories with fixture files to test verification logic.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
8
|
+
import { mkdtemp, writeFile, mkdir, rm, readFile } from 'node:fs/promises';
|
|
9
|
+
import { join } from 'node:path';
|
|
10
|
+
import { tmpdir, homedir } from 'node:os';
|
|
11
|
+
import { GSDError } from '../errors.js';
|
|
12
|
+
|
|
13
|
+
import { verifyKeyLinks, validateConsistency, validateHealth, regexForKeyLinkPattern } from './validate.js';
|
|
14
|
+
|
|
15
|
+
// ─── regexForKeyLinkPattern ────────────────────────────────────────────────
|
|
16
|
+
|
|
17
|
+
describe('regexForKeyLinkPattern', () => {
|
|
18
|
+
it('preserves normal regex patterns used in key_links', () => {
|
|
19
|
+
const re = regexForKeyLinkPattern('import.*foo.*from.*target');
|
|
20
|
+
expect(re.test("import { foo } from './target.js';")).toBe(true);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('falls back to literal match for nested-quantifier patterns', () => {
|
|
24
|
+
const re = regexForKeyLinkPattern('(a+)+');
|
|
25
|
+
expect(re.source).toContain('\\');
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
// ─── verifyKeyLinks ────────────────────────────────────────────────────────
|
|
30
|
+
|
|
31
|
+
describe('verifyKeyLinks', () => {
|
|
32
|
+
let tmpDir: string;
|
|
33
|
+
|
|
34
|
+
beforeEach(async () => {
|
|
35
|
+
tmpDir = await mkdtemp(join(tmpdir(), 'gsd-validate-'));
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
afterEach(async () => {
|
|
39
|
+
await rm(tmpDir, { recursive: true, force: true });
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('throws GSDError Validation when no args', async () => {
|
|
43
|
+
let caught: unknown;
|
|
44
|
+
try {
|
|
45
|
+
await verifyKeyLinks([], tmpDir);
|
|
46
|
+
} catch (err) {
|
|
47
|
+
caught = err;
|
|
48
|
+
}
|
|
49
|
+
expect(caught).toBeInstanceOf(GSDError);
|
|
50
|
+
expect((caught as GSDError).classification).toBe('validation');
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('returns all_verified true when pattern found in source', async () => {
|
|
54
|
+
// Create source file with an import statement
|
|
55
|
+
await writeFile(join(tmpDir, 'source.ts'), "import { foo } from './target.js';");
|
|
56
|
+
await writeFile(join(tmpDir, 'target.ts'), 'export const foo = 1;');
|
|
57
|
+
|
|
58
|
+
// Create plan with key_links
|
|
59
|
+
const planContent = `---
|
|
60
|
+
phase: 01
|
|
61
|
+
plan: 01
|
|
62
|
+
type: execute
|
|
63
|
+
wave: 1
|
|
64
|
+
depends_on: []
|
|
65
|
+
files_modified: []
|
|
66
|
+
autonomous: true
|
|
67
|
+
|
|
68
|
+
must_haves:
|
|
69
|
+
key_links:
|
|
70
|
+
- from: source.ts
|
|
71
|
+
to: target.ts
|
|
72
|
+
via: "import foo"
|
|
73
|
+
pattern: "import.*foo.*from.*target"
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
# Plan
|
|
77
|
+
`;
|
|
78
|
+
await writeFile(join(tmpDir, 'plan.md'), planContent);
|
|
79
|
+
|
|
80
|
+
const result = await verifyKeyLinks(['plan.md'], tmpDir);
|
|
81
|
+
const data = result.data as Record<string, unknown>;
|
|
82
|
+
expect(data.all_verified).toBe(true);
|
|
83
|
+
expect(data.verified).toBe(1);
|
|
84
|
+
expect(data.total).toBe(1);
|
|
85
|
+
const links = data.links as Array<Record<string, unknown>>;
|
|
86
|
+
expect(links[0].detail).toBe('Pattern found in source');
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('returns verified true with "Pattern found in target" when not in source but in target', async () => {
|
|
90
|
+
await writeFile(join(tmpDir, 'source.ts'), 'const x = 1;');
|
|
91
|
+
await writeFile(join(tmpDir, 'target.ts'), "import { foo } from './other.js';");
|
|
92
|
+
|
|
93
|
+
const planContent = `---
|
|
94
|
+
phase: 01
|
|
95
|
+
plan: 01
|
|
96
|
+
type: execute
|
|
97
|
+
wave: 1
|
|
98
|
+
depends_on: []
|
|
99
|
+
files_modified: []
|
|
100
|
+
autonomous: true
|
|
101
|
+
|
|
102
|
+
must_haves:
|
|
103
|
+
key_links:
|
|
104
|
+
- from: source.ts
|
|
105
|
+
to: target.ts
|
|
106
|
+
via: "import foo"
|
|
107
|
+
pattern: "import.*foo"
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
# Plan
|
|
111
|
+
`;
|
|
112
|
+
await writeFile(join(tmpDir, 'plan.md'), planContent);
|
|
113
|
+
|
|
114
|
+
const result = await verifyKeyLinks(['plan.md'], tmpDir);
|
|
115
|
+
const data = result.data as Record<string, unknown>;
|
|
116
|
+
const links = data.links as Array<Record<string, unknown>>;
|
|
117
|
+
expect(links[0].verified).toBe(true);
|
|
118
|
+
expect(links[0].detail).toBe('Pattern found in target');
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it('returns verified false when pattern not found in source or target', async () => {
|
|
122
|
+
await writeFile(join(tmpDir, 'source.ts'), 'const x = 1;');
|
|
123
|
+
await writeFile(join(tmpDir, 'target.ts'), 'const y = 2;');
|
|
124
|
+
|
|
125
|
+
const planContent = `---
|
|
126
|
+
phase: 01
|
|
127
|
+
plan: 01
|
|
128
|
+
type: execute
|
|
129
|
+
wave: 1
|
|
130
|
+
depends_on: []
|
|
131
|
+
files_modified: []
|
|
132
|
+
autonomous: true
|
|
133
|
+
|
|
134
|
+
must_haves:
|
|
135
|
+
key_links:
|
|
136
|
+
- from: source.ts
|
|
137
|
+
to: target.ts
|
|
138
|
+
via: "import foo"
|
|
139
|
+
pattern: "import.*foo"
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
# Plan
|
|
143
|
+
`;
|
|
144
|
+
await writeFile(join(tmpDir, 'plan.md'), planContent);
|
|
145
|
+
|
|
146
|
+
const result = await verifyKeyLinks(['plan.md'], tmpDir);
|
|
147
|
+
const data = result.data as Record<string, unknown>;
|
|
148
|
+
expect(data.all_verified).toBe(false);
|
|
149
|
+
const links = data.links as Array<Record<string, unknown>>;
|
|
150
|
+
expect(links[0].verified).toBe(false);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it('returns Source file not found when source missing', async () => {
|
|
154
|
+
await writeFile(join(tmpDir, 'target.ts'), 'export const foo = 1;');
|
|
155
|
+
|
|
156
|
+
const planContent = `---
|
|
157
|
+
phase: 01
|
|
158
|
+
plan: 01
|
|
159
|
+
type: execute
|
|
160
|
+
wave: 1
|
|
161
|
+
depends_on: []
|
|
162
|
+
files_modified: []
|
|
163
|
+
autonomous: true
|
|
164
|
+
|
|
165
|
+
must_haves:
|
|
166
|
+
key_links:
|
|
167
|
+
- from: missing.ts
|
|
168
|
+
to: target.ts
|
|
169
|
+
via: "import"
|
|
170
|
+
pattern: "import"
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
# Plan
|
|
174
|
+
`;
|
|
175
|
+
await writeFile(join(tmpDir, 'plan.md'), planContent);
|
|
176
|
+
|
|
177
|
+
const result = await verifyKeyLinks(['plan.md'], tmpDir);
|
|
178
|
+
const data = result.data as Record<string, unknown>;
|
|
179
|
+
const links = data.links as Array<Record<string, unknown>>;
|
|
180
|
+
expect(links[0].detail).toBe('Source file not found');
|
|
181
|
+
expect(links[0].verified).toBe(false);
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
it('checks target reference in source when no pattern specified', async () => {
|
|
185
|
+
await writeFile(join(tmpDir, 'source.ts'), "import { foo } from './target.ts';");
|
|
186
|
+
await writeFile(join(tmpDir, 'target.ts'), 'export const foo = 1;');
|
|
187
|
+
|
|
188
|
+
const planContent = `---
|
|
189
|
+
phase: 01
|
|
190
|
+
plan: 01
|
|
191
|
+
type: execute
|
|
192
|
+
wave: 1
|
|
193
|
+
depends_on: []
|
|
194
|
+
files_modified: []
|
|
195
|
+
autonomous: true
|
|
196
|
+
|
|
197
|
+
must_haves:
|
|
198
|
+
key_links:
|
|
199
|
+
- from: source.ts
|
|
200
|
+
to: target.ts
|
|
201
|
+
via: "import"
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
# Plan
|
|
205
|
+
`;
|
|
206
|
+
await writeFile(join(tmpDir, 'plan.md'), planContent);
|
|
207
|
+
|
|
208
|
+
const result = await verifyKeyLinks(['plan.md'], tmpDir);
|
|
209
|
+
const data = result.data as Record<string, unknown>;
|
|
210
|
+
const links = data.links as Array<Record<string, unknown>>;
|
|
211
|
+
expect(links[0].verified).toBe(true);
|
|
212
|
+
expect(links[0].detail).toBe('Target referenced in source');
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
it('reports invalid regex like gsd-tools.cjs (try/catch on new RegExp)', async () => {
|
|
216
|
+
await writeFile(join(tmpDir, 'source.ts'), 'const x = 1;');
|
|
217
|
+
await writeFile(join(tmpDir, 'target.ts'), 'const y = 2;');
|
|
218
|
+
|
|
219
|
+
const planContent = `---
|
|
220
|
+
phase: 01
|
|
221
|
+
plan: 01
|
|
222
|
+
type: execute
|
|
223
|
+
wave: 1
|
|
224
|
+
depends_on: []
|
|
225
|
+
files_modified: []
|
|
226
|
+
autonomous: true
|
|
227
|
+
|
|
228
|
+
must_haves:
|
|
229
|
+
key_links:
|
|
230
|
+
- from: source.ts
|
|
231
|
+
to: target.ts
|
|
232
|
+
via: "bad regex"
|
|
233
|
+
pattern: "[invalid"
|
|
234
|
+
---
|
|
235
|
+
|
|
236
|
+
# Plan
|
|
237
|
+
`;
|
|
238
|
+
await writeFile(join(tmpDir, 'plan.md'), planContent);
|
|
239
|
+
|
|
240
|
+
const result = await verifyKeyLinks(['plan.md'], tmpDir);
|
|
241
|
+
const data = result.data as Record<string, unknown>;
|
|
242
|
+
const links = data.links as Array<Record<string, unknown>>;
|
|
243
|
+
expect(links[0].verified).toBe(false);
|
|
244
|
+
expect((links[0].detail as string)).toMatch(/Invalid regex pattern/);
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
it('returns error when no must_haves.key_links in plan', async () => {
|
|
248
|
+
const planContent = `---
|
|
249
|
+
phase: 01
|
|
250
|
+
plan: 01
|
|
251
|
+
type: execute
|
|
252
|
+
wave: 1
|
|
253
|
+
depends_on: []
|
|
254
|
+
files_modified: []
|
|
255
|
+
autonomous: true
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
# Plan
|
|
259
|
+
`;
|
|
260
|
+
await writeFile(join(tmpDir, 'plan.md'), planContent);
|
|
261
|
+
|
|
262
|
+
const result = await verifyKeyLinks(['plan.md'], tmpDir);
|
|
263
|
+
const data = result.data as Record<string, unknown>;
|
|
264
|
+
expect(data.error).toBe('No must_haves.key_links found in frontmatter');
|
|
265
|
+
});
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
// ─── validateConsistency ──────────────────────────────────────────────────
|
|
269
|
+
|
|
270
|
+
describe('validateConsistency', () => {
|
|
271
|
+
let tmpDir: string;
|
|
272
|
+
|
|
273
|
+
beforeEach(async () => {
|
|
274
|
+
tmpDir = await mkdtemp(join(tmpdir(), 'gsd-consistency-'));
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
afterEach(async () => {
|
|
278
|
+
await rm(tmpDir, { recursive: true, force: true });
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
/** Helper: create a .planning directory structure */
|
|
282
|
+
async function createPlanning(opts: {
|
|
283
|
+
roadmap?: string;
|
|
284
|
+
phases?: Array<{ dir: string; plans?: string[]; summaries?: string[]; planContents?: Record<string, string> }>;
|
|
285
|
+
config?: Record<string, unknown>;
|
|
286
|
+
}): Promise<void> {
|
|
287
|
+
const planning = join(tmpDir, '.planning');
|
|
288
|
+
await mkdir(planning, { recursive: true });
|
|
289
|
+
|
|
290
|
+
if (opts.roadmap !== undefined) {
|
|
291
|
+
await writeFile(join(planning, 'ROADMAP.md'), opts.roadmap);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
if (opts.config) {
|
|
295
|
+
await writeFile(join(planning, 'config.json'), JSON.stringify(opts.config));
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
if (opts.phases) {
|
|
299
|
+
const phasesDir = join(planning, 'phases');
|
|
300
|
+
await mkdir(phasesDir, { recursive: true });
|
|
301
|
+
for (const phase of opts.phases) {
|
|
302
|
+
const phaseDir = join(phasesDir, phase.dir);
|
|
303
|
+
await mkdir(phaseDir, { recursive: true });
|
|
304
|
+
if (phase.plans) {
|
|
305
|
+
for (const plan of phase.plans) {
|
|
306
|
+
const content = phase.planContents?.[plan] ?? `---\nphase: ${phase.dir}\nplan: 01\ntype: execute\nwave: 1\ndepends_on: []\nfiles_modified: []\nautonomous: true\n---\n\n# Plan\n`;
|
|
307
|
+
await writeFile(join(phaseDir, plan), content);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
if (phase.summaries) {
|
|
311
|
+
for (const summary of phase.summaries) {
|
|
312
|
+
await writeFile(join(phaseDir, summary), '# Summary\n');
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
it('returns passed true when ROADMAP phases match disk', async () => {
|
|
320
|
+
await createPlanning({
|
|
321
|
+
roadmap: '# Roadmap\n\n## Phase 1: Foundation\n\nGoal here.\n\n## Phase 2: Features\n\nMore goals.\n',
|
|
322
|
+
phases: [
|
|
323
|
+
{ dir: '01-foundation', plans: ['01-01-PLAN.md'], summaries: ['01-01-SUMMARY.md'] },
|
|
324
|
+
{ dir: '02-features', plans: ['02-01-PLAN.md'], summaries: ['02-01-SUMMARY.md'] },
|
|
325
|
+
],
|
|
326
|
+
config: { phase_naming: 'sequential' },
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
const result = await validateConsistency([], tmpDir);
|
|
330
|
+
const data = result.data as Record<string, unknown>;
|
|
331
|
+
expect(data.passed).toBe(true);
|
|
332
|
+
expect((data.errors as string[]).length).toBe(0);
|
|
333
|
+
expect((data.warnings as string[]).length).toBe(0);
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
it('warns when phase in ROADMAP but not on disk', async () => {
|
|
337
|
+
await createPlanning({
|
|
338
|
+
roadmap: '# Roadmap\n\n## Phase 1: Foundation\n\n## Phase 2: Features\n\n## Phase 3: Polish\n',
|
|
339
|
+
phases: [
|
|
340
|
+
{ dir: '01-foundation', plans: ['01-01-PLAN.md'] },
|
|
341
|
+
{ dir: '02-features', plans: ['02-01-PLAN.md'] },
|
|
342
|
+
],
|
|
343
|
+
config: { phase_naming: 'sequential' },
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
const result = await validateConsistency([], tmpDir);
|
|
347
|
+
const data = result.data as Record<string, unknown>;
|
|
348
|
+
const warnings = data.warnings as string[];
|
|
349
|
+
expect(warnings.some(w => w.includes('Phase 3') && w.includes('ROADMAP') && w.includes('no directory'))).toBe(true);
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
it('warns when phase on disk but not in ROADMAP', async () => {
|
|
353
|
+
await createPlanning({
|
|
354
|
+
roadmap: '# Roadmap\n\n## Phase 1: Foundation\n',
|
|
355
|
+
phases: [
|
|
356
|
+
{ dir: '01-foundation', plans: ['01-01-PLAN.md'] },
|
|
357
|
+
{ dir: '02-features', plans: ['02-01-PLAN.md'] },
|
|
358
|
+
],
|
|
359
|
+
config: { phase_naming: 'sequential' },
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
const result = await validateConsistency([], tmpDir);
|
|
363
|
+
const data = result.data as Record<string, unknown>;
|
|
364
|
+
const warnings = data.warnings as string[];
|
|
365
|
+
expect(warnings.some(w => w.includes('02') && w.includes('disk') && w.includes('not in ROADMAP'))).toBe(true);
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
it('warns on gap in sequential phase numbering', async () => {
|
|
369
|
+
await createPlanning({
|
|
370
|
+
roadmap: '# Roadmap\n\n## Phase 1: Foundation\n\n## Phase 3: Polish\n',
|
|
371
|
+
phases: [
|
|
372
|
+
{ dir: '01-foundation', plans: ['01-01-PLAN.md'] },
|
|
373
|
+
{ dir: '03-polish', plans: ['03-01-PLAN.md'] },
|
|
374
|
+
],
|
|
375
|
+
config: { phase_naming: 'sequential' },
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
const result = await validateConsistency([], tmpDir);
|
|
379
|
+
const data = result.data as Record<string, unknown>;
|
|
380
|
+
const warnings = data.warnings as string[];
|
|
381
|
+
expect(warnings.some(w => w.includes('Gap in phase numbering'))).toBe(true);
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
it('warns on plan numbering gap within phase', async () => {
|
|
385
|
+
await createPlanning({
|
|
386
|
+
roadmap: '# Roadmap\n\n## Phase 1: Foundation\n',
|
|
387
|
+
phases: [
|
|
388
|
+
{ dir: '01-foundation', plans: ['01-01-PLAN.md', '01-03-PLAN.md'] },
|
|
389
|
+
],
|
|
390
|
+
config: { phase_naming: 'sequential' },
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
const result = await validateConsistency([], tmpDir);
|
|
394
|
+
const data = result.data as Record<string, unknown>;
|
|
395
|
+
const warnings = data.warnings as string[];
|
|
396
|
+
expect(warnings.some(w => w.includes('Gap in plan numbering'))).toBe(true);
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
it('warns on summary without matching plan', async () => {
|
|
400
|
+
await createPlanning({
|
|
401
|
+
roadmap: '# Roadmap\n\n## Phase 1: Foundation\n',
|
|
402
|
+
phases: [
|
|
403
|
+
{ dir: '01-foundation', plans: ['01-01-PLAN.md'], summaries: ['01-01-SUMMARY.md', '01-02-SUMMARY.md'] },
|
|
404
|
+
],
|
|
405
|
+
config: { phase_naming: 'sequential' },
|
|
406
|
+
});
|
|
407
|
+
|
|
408
|
+
const result = await validateConsistency([], tmpDir);
|
|
409
|
+
const data = result.data as Record<string, unknown>;
|
|
410
|
+
const warnings = data.warnings as string[];
|
|
411
|
+
expect(warnings.some(w => w.includes('Summary') && w.includes('no matching PLAN'))).toBe(true);
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
it('warns when plan missing wave in frontmatter', async () => {
|
|
415
|
+
const noWavePlan = `---\nphase: 01\nplan: 01\ntype: execute\ndepends_on: []\nfiles_modified: []\nautonomous: true\n---\n\n# Plan\n`;
|
|
416
|
+
await createPlanning({
|
|
417
|
+
roadmap: '# Roadmap\n\n## Phase 1: Foundation\n',
|
|
418
|
+
phases: [
|
|
419
|
+
{ dir: '01-foundation', plans: ['01-01-PLAN.md'], planContents: { '01-01-PLAN.md': noWavePlan } },
|
|
420
|
+
],
|
|
421
|
+
config: { phase_naming: 'sequential' },
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
const result = await validateConsistency([], tmpDir);
|
|
425
|
+
const data = result.data as Record<string, unknown>;
|
|
426
|
+
const warnings = data.warnings as string[];
|
|
427
|
+
expect(warnings.some(w => w.includes('wave') && w.includes('frontmatter'))).toBe(true);
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
it('returns passed false with error when ROADMAP.md missing', async () => {
|
|
431
|
+
await createPlanning({
|
|
432
|
+
phases: [{ dir: '01-foundation', plans: ['01-01-PLAN.md'] }],
|
|
433
|
+
config: { phase_naming: 'sequential' },
|
|
434
|
+
});
|
|
435
|
+
|
|
436
|
+
const result = await validateConsistency([], tmpDir);
|
|
437
|
+
const data = result.data as Record<string, unknown>;
|
|
438
|
+
expect(data.passed).toBe(false);
|
|
439
|
+
expect((data.errors as string[])).toContain('ROADMAP.md not found');
|
|
440
|
+
});
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
// ─── validateHealth ─────────────────────────────────────────────────────────
|
|
444
|
+
|
|
445
|
+
describe('validateHealth', () => {
|
|
446
|
+
let tmpDir: string;
|
|
447
|
+
|
|
448
|
+
beforeEach(async () => {
|
|
449
|
+
tmpDir = await mkdtemp(join(tmpdir(), 'gsd-health-'));
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
afterEach(async () => {
|
|
453
|
+
await rm(tmpDir, { recursive: true, force: true });
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
/** Helper: create a healthy .planning directory structure */
|
|
457
|
+
async function createHealthyPlanning(): Promise<void> {
|
|
458
|
+
const planning = join(tmpDir, '.planning');
|
|
459
|
+
await mkdir(join(planning, 'phases', '01-foundation'), { recursive: true });
|
|
460
|
+
|
|
461
|
+
await writeFile(join(planning, 'PROJECT.md'), '# Project\n\n## What This Is\n\nA project.\n\n## Core Value\n\nValue here.\n\n## Requirements\n\n- Req 1\n');
|
|
462
|
+
await writeFile(join(planning, 'ROADMAP.md'), '# Roadmap\n\n## Phase 1: Foundation\n\nGoals.\n');
|
|
463
|
+
await writeFile(join(planning, 'STATE.md'), '---\nstatus: executing\n---\n\n# State\n\n**Current Phase:** 1\n**Status:** executing\n');
|
|
464
|
+
await writeFile(join(planning, 'config.json'), JSON.stringify({
|
|
465
|
+
model_profile: 'balanced',
|
|
466
|
+
workflow: { nyquist_validation: true },
|
|
467
|
+
}, null, 2));
|
|
468
|
+
|
|
469
|
+
await writeFile(join(planning, 'phases', '01-foundation', '01-01-PLAN.md'), '---\nphase: 01\nplan: 01\ntype: execute\nwave: 1\ndepends_on: []\nfiles_modified: []\nautonomous: true\n---\n\n# Plan\n');
|
|
470
|
+
await writeFile(join(planning, 'phases', '01-foundation', '01-01-SUMMARY.md'), '# Summary\n');
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
it('returns healthy status when all files present', async () => {
|
|
474
|
+
await createHealthyPlanning();
|
|
475
|
+
|
|
476
|
+
const result = await validateHealth([], tmpDir);
|
|
477
|
+
const data = result.data as Record<string, unknown>;
|
|
478
|
+
expect(data.status).toBe('healthy');
|
|
479
|
+
expect((data.errors as unknown[]).length).toBe(0);
|
|
480
|
+
expect((data.warnings as unknown[]).length).toBe(0);
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
it('returns broken with E001 when no .planning/ directory', async () => {
|
|
484
|
+
// tmpDir has no .planning/ — already the case
|
|
485
|
+
|
|
486
|
+
const result = await validateHealth([], tmpDir);
|
|
487
|
+
const data = result.data as Record<string, unknown>;
|
|
488
|
+
expect(data.status).toBe('broken');
|
|
489
|
+
const errors = data.errors as Array<Record<string, unknown>>;
|
|
490
|
+
expect(errors.some(e => e.code === 'E001')).toBe(true);
|
|
491
|
+
});
|
|
492
|
+
|
|
493
|
+
it('returns error E002 when PROJECT.md missing', async () => {
|
|
494
|
+
await createHealthyPlanning();
|
|
495
|
+
const { unlink } = await import('node:fs/promises');
|
|
496
|
+
await unlink(join(tmpDir, '.planning', 'PROJECT.md'));
|
|
497
|
+
|
|
498
|
+
const result = await validateHealth([], tmpDir);
|
|
499
|
+
const data = result.data as Record<string, unknown>;
|
|
500
|
+
const errors = data.errors as Array<Record<string, unknown>>;
|
|
501
|
+
expect(errors.some(e => e.code === 'E002')).toBe(true);
|
|
502
|
+
});
|
|
503
|
+
|
|
504
|
+
it('returns error E003 when ROADMAP.md missing', async () => {
|
|
505
|
+
await createHealthyPlanning();
|
|
506
|
+
const { unlink } = await import('node:fs/promises');
|
|
507
|
+
await unlink(join(tmpDir, '.planning', 'ROADMAP.md'));
|
|
508
|
+
|
|
509
|
+
const result = await validateHealth([], tmpDir);
|
|
510
|
+
const data = result.data as Record<string, unknown>;
|
|
511
|
+
const errors = data.errors as Array<Record<string, unknown>>;
|
|
512
|
+
expect(errors.some(e => e.code === 'E003')).toBe(true);
|
|
513
|
+
});
|
|
514
|
+
|
|
515
|
+
it('returns error E004 when STATE.md missing (repairable)', async () => {
|
|
516
|
+
await createHealthyPlanning();
|
|
517
|
+
const { unlink } = await import('node:fs/promises');
|
|
518
|
+
await unlink(join(tmpDir, '.planning', 'STATE.md'));
|
|
519
|
+
|
|
520
|
+
const result = await validateHealth([], tmpDir);
|
|
521
|
+
const data = result.data as Record<string, unknown>;
|
|
522
|
+
const errors = data.errors as Array<Record<string, unknown>>;
|
|
523
|
+
const e004 = errors.find(e => e.code === 'E004');
|
|
524
|
+
expect(e004).toBeDefined();
|
|
525
|
+
expect(e004!.repairable).toBe(true);
|
|
526
|
+
});
|
|
527
|
+
|
|
528
|
+
it('returns error E005 when config.json has invalid JSON (repairable)', async () => {
|
|
529
|
+
await createHealthyPlanning();
|
|
530
|
+
await writeFile(join(tmpDir, '.planning', 'config.json'), '{invalid json!!!');
|
|
531
|
+
|
|
532
|
+
const result = await validateHealth([], tmpDir);
|
|
533
|
+
const data = result.data as Record<string, unknown>;
|
|
534
|
+
const errors = data.errors as Array<Record<string, unknown>>;
|
|
535
|
+
const e005 = errors.find(e => e.code === 'E005');
|
|
536
|
+
expect(e005).toBeDefined();
|
|
537
|
+
expect(e005!.repairable).toBe(true);
|
|
538
|
+
});
|
|
539
|
+
|
|
540
|
+
it('returns warning W003 when config.json missing (repairable)', async () => {
|
|
541
|
+
await createHealthyPlanning();
|
|
542
|
+
const { unlink } = await import('node:fs/promises');
|
|
543
|
+
await unlink(join(tmpDir, '.planning', 'config.json'));
|
|
544
|
+
|
|
545
|
+
const result = await validateHealth([], tmpDir);
|
|
546
|
+
const data = result.data as Record<string, unknown>;
|
|
547
|
+
const warnings = data.warnings as Array<Record<string, unknown>>;
|
|
548
|
+
const w003 = warnings.find(w => w.code === 'W003');
|
|
549
|
+
expect(w003).toBeDefined();
|
|
550
|
+
expect(w003!.repairable).toBe(true);
|
|
551
|
+
});
|
|
552
|
+
|
|
553
|
+
it('returns warning W005 for bad phase directory naming', async () => {
|
|
554
|
+
await createHealthyPlanning();
|
|
555
|
+
await mkdir(join(tmpDir, '.planning', 'phases', 'bad_name'), { recursive: true });
|
|
556
|
+
|
|
557
|
+
const result = await validateHealth([], tmpDir);
|
|
558
|
+
const data = result.data as Record<string, unknown>;
|
|
559
|
+
const warnings = data.warnings as Array<Record<string, unknown>>;
|
|
560
|
+
expect(warnings.some(w => w.code === 'W005')).toBe(true);
|
|
561
|
+
});
|
|
562
|
+
|
|
563
|
+
it('returns early with E010 when CWD equals home directory', async () => {
|
|
564
|
+
const result = await validateHealth([], homedir());
|
|
565
|
+
const data = result.data as Record<string, unknown>;
|
|
566
|
+
expect(data.status).toBe('error');
|
|
567
|
+
const errors = data.errors as Array<Record<string, unknown>>;
|
|
568
|
+
expect(errors.some(e => e.code === 'E010')).toBe(true);
|
|
569
|
+
});
|
|
570
|
+
|
|
571
|
+
it('returns warning W008 when config.json missing workflow.nyquist_validation', async () => {
|
|
572
|
+
await createHealthyPlanning();
|
|
573
|
+
await writeFile(join(tmpDir, '.planning', 'config.json'), JSON.stringify({
|
|
574
|
+
model_profile: 'balanced',
|
|
575
|
+
workflow: { research: true },
|
|
576
|
+
}, null, 2));
|
|
577
|
+
|
|
578
|
+
const result = await validateHealth([], tmpDir);
|
|
579
|
+
const data = result.data as Record<string, unknown>;
|
|
580
|
+
const warnings = data.warnings as Array<Record<string, unknown>>;
|
|
581
|
+
expect(warnings.some(w => w.code === 'W008')).toBe(true);
|
|
582
|
+
});
|
|
583
|
+
|
|
584
|
+
it('derives status from errors (broken), warnings (degraded), none (healthy)', async () => {
|
|
585
|
+
// broken: no .planning/
|
|
586
|
+
const r1 = await validateHealth([], tmpDir);
|
|
587
|
+
expect((r1.data as Record<string, unknown>).status).toBe('broken');
|
|
588
|
+
|
|
589
|
+
// degraded: missing config.json (warning only, not error)
|
|
590
|
+
await createHealthyPlanning();
|
|
591
|
+
const { unlink } = await import('node:fs/promises');
|
|
592
|
+
await unlink(join(tmpDir, '.planning', 'config.json'));
|
|
593
|
+
const r2 = await validateHealth([], tmpDir);
|
|
594
|
+
expect((r2.data as Record<string, unknown>).status).toBe('degraded');
|
|
595
|
+
|
|
596
|
+
// healthy: all present
|
|
597
|
+
await writeFile(join(tmpDir, '.planning', 'config.json'), JSON.stringify({
|
|
598
|
+
model_profile: 'balanced',
|
|
599
|
+
workflow: { nyquist_validation: true },
|
|
600
|
+
}, null, 2));
|
|
601
|
+
const r3 = await validateHealth([], tmpDir);
|
|
602
|
+
expect((r3.data as Record<string, unknown>).status).toBe('healthy');
|
|
603
|
+
});
|
|
604
|
+
|
|
605
|
+
// ─── Repair tests ───────────────────────────────────────────────────────
|
|
606
|
+
|
|
607
|
+
it('--repair with missing config.json creates config.json with defaults', async () => {
|
|
608
|
+
await createHealthyPlanning();
|
|
609
|
+
const { unlink } = await import('node:fs/promises');
|
|
610
|
+
await unlink(join(tmpDir, '.planning', 'config.json'));
|
|
611
|
+
|
|
612
|
+
const result = await validateHealth(['--repair'], tmpDir);
|
|
613
|
+
const data = result.data as Record<string, unknown>;
|
|
614
|
+
expect(data.repairs_performed).toBeDefined();
|
|
615
|
+
const repairs = data.repairs_performed as Array<Record<string, unknown>>;
|
|
616
|
+
expect(repairs.some(r => r.action === 'createConfig' && r.success === true)).toBe(true);
|
|
617
|
+
|
|
618
|
+
// Verify file was created
|
|
619
|
+
const config = JSON.parse(await readFile(join(tmpDir, '.planning', 'config.json'), 'utf-8'));
|
|
620
|
+
expect(config.model_profile).toBe('balanced');
|
|
621
|
+
expect(config.workflow.nyquist_validation).toBe(true);
|
|
622
|
+
});
|
|
623
|
+
|
|
624
|
+
it('--repair with missing STATE.md generates minimal STATE.md', async () => {
|
|
625
|
+
await createHealthyPlanning();
|
|
626
|
+
const { unlink } = await import('node:fs/promises');
|
|
627
|
+
await unlink(join(tmpDir, '.planning', 'STATE.md'));
|
|
628
|
+
|
|
629
|
+
const result = await validateHealth(['--repair'], tmpDir);
|
|
630
|
+
const data = result.data as Record<string, unknown>;
|
|
631
|
+
const repairs = data.repairs_performed as Array<Record<string, unknown>>;
|
|
632
|
+
expect(repairs.some(r => r.action === 'regenerateState' && r.success === true)).toBe(true);
|
|
633
|
+
|
|
634
|
+
// Verify file was created
|
|
635
|
+
const stateContent = await readFile(join(tmpDir, '.planning', 'STATE.md'), 'utf-8');
|
|
636
|
+
expect(stateContent).toContain('# Session State');
|
|
637
|
+
expect(stateContent).toContain('regenerated by');
|
|
638
|
+
});
|
|
639
|
+
|
|
640
|
+
it('--repair with missing nyquist key adds workflow.nyquist_validation', async () => {
|
|
641
|
+
await createHealthyPlanning();
|
|
642
|
+
await writeFile(join(tmpDir, '.planning', 'config.json'), JSON.stringify({
|
|
643
|
+
model_profile: 'balanced',
|
|
644
|
+
workflow: { research: true },
|
|
645
|
+
}, null, 2));
|
|
646
|
+
|
|
647
|
+
const result = await validateHealth(['--repair'], tmpDir);
|
|
648
|
+
const data = result.data as Record<string, unknown>;
|
|
649
|
+
const repairs = data.repairs_performed as Array<Record<string, unknown>>;
|
|
650
|
+
expect(repairs.some(r => r.action === 'addNyquistKey' && r.success === true)).toBe(true);
|
|
651
|
+
|
|
652
|
+
// Verify key was added
|
|
653
|
+
const config = JSON.parse(await readFile(join(tmpDir, '.planning', 'config.json'), 'utf-8'));
|
|
654
|
+
expect(config.workflow.nyquist_validation).toBe(true);
|
|
655
|
+
});
|
|
656
|
+
});
|