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,216 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for QueryRegistry, extractField, and createRegistry factory.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
6
|
+
import { QueryRegistry, extractField, resolveQueryArgv } from './registry.js';
|
|
7
|
+
import { createRegistry, QUERY_MUTATION_COMMANDS } from './index.js';
|
|
8
|
+
import type { QueryResult } from './utils.js';
|
|
9
|
+
|
|
10
|
+
// ─── extractField ──────────────────────────────────────────────────────────
|
|
11
|
+
|
|
12
|
+
describe('extractField', () => {
|
|
13
|
+
it('extracts nested value with dot notation', () => {
|
|
14
|
+
expect(extractField({ a: { b: 1 } }, 'a.b')).toBe(1);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('extracts top-level value', () => {
|
|
18
|
+
expect(extractField({ slug: 'my-phase' }, 'slug')).toBe('my-phase');
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('extracts array element with bracket notation', () => {
|
|
22
|
+
expect(extractField({ items: [10, 20, 30] }, 'items[1]')).toBe(20);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('extracts array element with negative index', () => {
|
|
26
|
+
expect(extractField({ items: [10, 20, 30] }, 'items[-1]')).toBe(30);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('returns undefined for null input', () => {
|
|
30
|
+
expect(extractField(null, 'a')).toBeUndefined();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('returns undefined for undefined input', () => {
|
|
34
|
+
expect(extractField(undefined, 'a')).toBeUndefined();
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('returns undefined for missing nested path', () => {
|
|
38
|
+
expect(extractField({ a: 1 }, 'b.c')).toBeUndefined();
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('returns undefined when bracket access targets non-array', () => {
|
|
42
|
+
expect(extractField({ items: 'not-array' }, 'items[0]')).toBeUndefined();
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('handles deeply nested paths', () => {
|
|
46
|
+
expect(extractField({ a: { b: { c: { d: 42 } } } }, 'a.b.c.d')).toBe(42);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('handles mixed dot and bracket notation', () => {
|
|
50
|
+
expect(extractField({ data: { items: [{ name: 'x' }] } }, 'data.items[0].name')).toBe('x');
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// ─── QueryRegistry ─────────────────────────────────────────────────────────
|
|
55
|
+
|
|
56
|
+
describe('QueryRegistry', () => {
|
|
57
|
+
it('register makes has() return true', () => {
|
|
58
|
+
const registry = new QueryRegistry();
|
|
59
|
+
const handler = async () => ({ data: 'test' });
|
|
60
|
+
registry.register('test-cmd', handler);
|
|
61
|
+
|
|
62
|
+
expect(registry.has('test-cmd')).toBe(true);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('has() returns false for unregistered command', () => {
|
|
66
|
+
const registry = new QueryRegistry();
|
|
67
|
+
|
|
68
|
+
expect(registry.has('nonexistent')).toBe(false);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('dispatch calls registered handler', async () => {
|
|
72
|
+
const registry = new QueryRegistry();
|
|
73
|
+
const handler = vi.fn(async (args: string[], _projectDir: string): Promise<QueryResult> => {
|
|
74
|
+
return { data: { value: args[0] } };
|
|
75
|
+
});
|
|
76
|
+
registry.register('test-cmd', handler);
|
|
77
|
+
|
|
78
|
+
const result = await registry.dispatch('test-cmd', ['arg1'], '/tmp');
|
|
79
|
+
|
|
80
|
+
expect(handler).toHaveBeenCalledWith(['arg1'], '/tmp');
|
|
81
|
+
expect(result).toEqual({ data: { value: 'arg1' } });
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('dispatch throws GSDError for unregistered command', async () => {
|
|
85
|
+
const registry = new QueryRegistry();
|
|
86
|
+
// Bridge removed in v3.0 — unknown commands throw, not fallback
|
|
87
|
+
await expect(registry.dispatch('unknown-cmd', ['arg1'], '/tmp/project'))
|
|
88
|
+
.rejects.toThrow('Unknown command: "unknown-cmd"');
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('commands() returns all registered command names', () => {
|
|
92
|
+
const registry = new QueryRegistry();
|
|
93
|
+
registry.register('alpha', async () => ({ data: 1 }));
|
|
94
|
+
registry.register('beta', async () => ({ data: 2 }));
|
|
95
|
+
expect(registry.commands().sort()).toEqual(['alpha', 'beta']);
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
// ─── QUERY_MUTATION_COMMANDS vs registry ───────────────────────────────────
|
|
100
|
+
|
|
101
|
+
describe('QUERY_MUTATION_COMMANDS', () => {
|
|
102
|
+
it('has a registered handler for every mutation command name', () => {
|
|
103
|
+
const registry = createRegistry();
|
|
104
|
+
const missing: string[] = [];
|
|
105
|
+
for (const cmd of QUERY_MUTATION_COMMANDS) {
|
|
106
|
+
if (!registry.has(cmd)) missing.push(cmd);
|
|
107
|
+
}
|
|
108
|
+
expect(missing).toEqual([]);
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
// ─── createRegistry ────────────────────────────────────────────────────────
|
|
113
|
+
|
|
114
|
+
describe('createRegistry', () => {
|
|
115
|
+
it('returns a QueryRegistry instance', () => {
|
|
116
|
+
const registry = createRegistry();
|
|
117
|
+
|
|
118
|
+
expect(registry).toBeInstanceOf(QueryRegistry);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it('has generate-slug registered', () => {
|
|
122
|
+
const registry = createRegistry();
|
|
123
|
+
|
|
124
|
+
expect(registry.has('generate-slug')).toBe(true);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it('has current-timestamp registered', () => {
|
|
128
|
+
const registry = createRegistry();
|
|
129
|
+
|
|
130
|
+
expect(registry.has('current-timestamp')).toBe(true);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it('has summary-extract dash alias (PR #2179 / workflows)', () => {
|
|
134
|
+
const registry = createRegistry();
|
|
135
|
+
expect(registry.has('summary-extract')).toBe(true);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it('has context-history registered', () => {
|
|
139
|
+
const registry = createRegistry();
|
|
140
|
+
expect(registry.has('context-history')).toBe(true);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it('has plan.execution-route registered', () => {
|
|
144
|
+
const registry = createRegistry();
|
|
145
|
+
expect(registry.has('plan.execution-route')).toBe(true);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it('has failure.capture-phase registered', () => {
|
|
149
|
+
const registry = createRegistry();
|
|
150
|
+
expect(registry.has('failure.capture-phase')).toBe(true);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it('has runtime.health registered', () => {
|
|
154
|
+
const registry = createRegistry();
|
|
155
|
+
expect(registry.has('runtime.health')).toBe(true);
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it('has sdk.health alias registered', () => {
|
|
159
|
+
const registry = createRegistry();
|
|
160
|
+
expect(registry.has('sdk.health')).toBe(true);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it('has failure.preflight registered', () => {
|
|
164
|
+
const registry = createRegistry();
|
|
165
|
+
expect(registry.has('failure.preflight')).toBe(true);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it('has failure.promote-phase registered', () => {
|
|
169
|
+
const registry = createRegistry();
|
|
170
|
+
expect(registry.has('failure.promote-phase')).toBe(true);
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
it('can dispatch generate-slug', async () => {
|
|
174
|
+
const registry = createRegistry();
|
|
175
|
+
const result = await registry.dispatch('generate-slug', ['My Phase'], '/tmp');
|
|
176
|
+
|
|
177
|
+
expect(result).toEqual({ data: { slug: 'my-phase' } });
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
// ─── resolveQueryArgv ───────────────────────────────────────────────────────
|
|
182
|
+
|
|
183
|
+
describe('resolveQueryArgv', () => {
|
|
184
|
+
it('matches longest dotted prefix (state.update + args)', () => {
|
|
185
|
+
const registry = createRegistry();
|
|
186
|
+
const m = resolveQueryArgv(['state', 'update', 'status', 'X'], registry);
|
|
187
|
+
expect(m).toEqual({ cmd: 'state.update', args: ['status', 'X'] });
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
it('matches longest prefix (phase.add wins over phase when both registered)', () => {
|
|
191
|
+
const registry = createRegistry();
|
|
192
|
+
const m = resolveQueryArgv(['phase', 'add', 'desc'], registry);
|
|
193
|
+
expect(m).toEqual({ cmd: 'phase.add', args: ['desc'] });
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it('prefers longer match over shorter', () => {
|
|
197
|
+
const registry = createRegistry();
|
|
198
|
+
const m = resolveQueryArgv(['state', 'load'], registry);
|
|
199
|
+
expect(m?.cmd).toBe('state.load');
|
|
200
|
+
expect(m?.args).toEqual([]);
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
it('returns null when no prefix matches', () => {
|
|
204
|
+
const registry = createRegistry();
|
|
205
|
+
const m = resolveQueryArgv(['totally-unknown', 'x'], registry);
|
|
206
|
+
expect(m).toBeNull();
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
it('matches a single dotted command token', () => {
|
|
210
|
+
const registry = createRegistry();
|
|
211
|
+
expect(resolveQueryArgv(['init.new-project'], registry)).toEqual({
|
|
212
|
+
cmd: 'init.new-project',
|
|
213
|
+
args: [],
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
});
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Query command registry — routes commands to native SDK handlers.
|
|
3
|
+
*
|
|
4
|
+
* The registry is a flat `Map<string, QueryHandler>` that maps command names
|
|
5
|
+
* to handler functions. Unknown keys passed to `dispatch()` throw `GSDError`.
|
|
6
|
+
* The `gsd-remix-sdk query` CLI resolves argv with `resolveQueryArgv()` before dispatch;
|
|
7
|
+
* there is no automatic delegation to `gsd-tools.cjs`.
|
|
8
|
+
*
|
|
9
|
+
* Also exports `extractField` — a TypeScript port of the `--pick` field
|
|
10
|
+
* extraction logic from gsd-tools.cjs (lines 365-382).
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* import { QueryRegistry, extractField } from './registry.js';
|
|
15
|
+
*
|
|
16
|
+
* const registry = new QueryRegistry();
|
|
17
|
+
* registry.register('generate-slug', generateSlug);
|
|
18
|
+
* const result = await registry.dispatch('generate-slug', ['My Phase'], '/project');
|
|
19
|
+
* const slug = extractField(result.data, 'slug'); // 'my-phase'
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import type { QueryResult, QueryHandler } from './utils.js';
|
|
24
|
+
import { GSDError, ErrorClassification } from '../errors.js';
|
|
25
|
+
|
|
26
|
+
// ─── extractField ──────────────────────────────────────────────────────────
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Extract a nested field from an object using dot-notation and bracket syntax.
|
|
30
|
+
*
|
|
31
|
+
* Direct port of `extractField()` from gsd-tools.cjs (lines 365-382).
|
|
32
|
+
* Supports `a.b.c` dot paths, `items[0]` array indexing, and `items[-1]`
|
|
33
|
+
* negative indexing.
|
|
34
|
+
*
|
|
35
|
+
* @param obj - The object to extract from
|
|
36
|
+
* @param fieldPath - Dot-separated path with optional bracket notation
|
|
37
|
+
* @returns The extracted value, or undefined if the path doesn't resolve
|
|
38
|
+
*/
|
|
39
|
+
export function extractField(obj: unknown, fieldPath: string): unknown {
|
|
40
|
+
const parts = fieldPath.split('.');
|
|
41
|
+
let current: unknown = obj;
|
|
42
|
+
for (const part of parts) {
|
|
43
|
+
if (current === null || current === undefined) return undefined;
|
|
44
|
+
const bracketMatch = part.match(/^(.+?)\[(-?\d+)]$/);
|
|
45
|
+
if (bracketMatch) {
|
|
46
|
+
const key = bracketMatch[1];
|
|
47
|
+
const index = parseInt(bracketMatch[2], 10);
|
|
48
|
+
current = (current as Record<string, unknown>)[key];
|
|
49
|
+
if (!Array.isArray(current)) return undefined;
|
|
50
|
+
current = index < 0 ? current[current.length + index] : current[index];
|
|
51
|
+
} else {
|
|
52
|
+
current = (current as Record<string, unknown>)[part];
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return current;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// ─── QueryRegistry ─────────────────────────────────────────────────────────
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Flat command registry that routes query commands to native handlers.
|
|
62
|
+
*
|
|
63
|
+
* `dispatch()` throws `GSDError` for unknown command keys. The `gsd-remix-sdk query`
|
|
64
|
+
* CLI uses `resolveQueryArgv()` first; when no handler matches, it may shell out
|
|
65
|
+
* to `gsd-tools.cjs` (see `cli.ts` and `QUERY-HANDLERS.md` fallback policy).
|
|
66
|
+
*/
|
|
67
|
+
export class QueryRegistry {
|
|
68
|
+
private handlers = new Map<string, QueryHandler>();
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Register a native handler for a command name.
|
|
72
|
+
*
|
|
73
|
+
* @param command - The command name (e.g., 'generate-slug', 'state.load')
|
|
74
|
+
* @param handler - The handler function to invoke
|
|
75
|
+
*/
|
|
76
|
+
register(command: string, handler: QueryHandler): void {
|
|
77
|
+
this.handlers.set(command, handler);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Check if a command has a registered native handler.
|
|
82
|
+
*
|
|
83
|
+
* @param command - The command name to check
|
|
84
|
+
* @returns True if the command has a native handler
|
|
85
|
+
*/
|
|
86
|
+
has(command: string): boolean {
|
|
87
|
+
return this.handlers.has(command);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* List all registered command names (for tooling, pipelines, and tests).
|
|
92
|
+
*/
|
|
93
|
+
commands(): string[] {
|
|
94
|
+
return Array.from(this.handlers.keys());
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Get the handler for a command without dispatching.
|
|
99
|
+
*
|
|
100
|
+
* @param command - The command name to look up
|
|
101
|
+
* @returns The handler function, or undefined if not registered
|
|
102
|
+
*/
|
|
103
|
+
getHandler(command: string): QueryHandler | undefined {
|
|
104
|
+
return this.handlers.get(command);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Dispatch a command to its registered native handler.
|
|
109
|
+
*
|
|
110
|
+
* @param command - The command name to dispatch
|
|
111
|
+
* @param args - Arguments to pass to the handler
|
|
112
|
+
* @param projectDir - The project directory for context
|
|
113
|
+
* @returns The query result from the handler
|
|
114
|
+
* @throws GSDError if no handler is registered for the command
|
|
115
|
+
*/
|
|
116
|
+
async dispatch(command: string, args: string[], projectDir: string): Promise<QueryResult> {
|
|
117
|
+
const handler = this.handlers.get(command);
|
|
118
|
+
if (!handler) {
|
|
119
|
+
throw new GSDError(
|
|
120
|
+
`Unknown command: "${command}". No native handler registered.`,
|
|
121
|
+
ErrorClassification.Validation,
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
return handler(args, projectDir);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function expandSingleDottedToken(tokens: string[]): string[] {
|
|
129
|
+
if (tokens.length !== 1 || tokens[0].startsWith('--')) {
|
|
130
|
+
return tokens;
|
|
131
|
+
}
|
|
132
|
+
const t = tokens[0];
|
|
133
|
+
if (!t.includes('.')) {
|
|
134
|
+
return tokens;
|
|
135
|
+
}
|
|
136
|
+
return t.split('.');
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function matchRegisteredPrefix(
|
|
140
|
+
tokens: string[],
|
|
141
|
+
registry: QueryRegistry,
|
|
142
|
+
): { cmd: string; args: string[] } | null {
|
|
143
|
+
for (let i = tokens.length; i >= 1; i--) {
|
|
144
|
+
const head = tokens.slice(0, i);
|
|
145
|
+
const dotted = head.join('.');
|
|
146
|
+
const spaced = head.join(' ');
|
|
147
|
+
if (registry.has(dotted)) {
|
|
148
|
+
return { cmd: dotted, args: tokens.slice(i) };
|
|
149
|
+
}
|
|
150
|
+
if (registry.has(spaced)) {
|
|
151
|
+
return { cmd: spaced, args: tokens.slice(i) };
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Map argv after `gsd-remix-sdk query` to a registered handler key and remaining args.
|
|
159
|
+
* Longest-prefix match on dotted (`a.b.c`) and spaced (`a b c`) keys; if no match,
|
|
160
|
+
* expands a single dotted token (`state.validate` → `state`, `validate`) and retries.
|
|
161
|
+
*/
|
|
162
|
+
export function resolveQueryArgv(
|
|
163
|
+
tokens: string[],
|
|
164
|
+
registry: QueryRegistry,
|
|
165
|
+
): { cmd: string; args: string[] } | null {
|
|
166
|
+
let matched = matchRegisteredPrefix(tokens, registry);
|
|
167
|
+
if (!matched) {
|
|
168
|
+
const expanded = expandSingleDottedToken(tokens);
|
|
169
|
+
if (expanded !== tokens) {
|
|
170
|
+
matched = matchRegisteredPrefix(expanded, registry);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return matched;
|
|
174
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for requirements.extract-from-plans.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
6
|
+
import { mkdtemp, writeFile, mkdir, rm } from 'node:fs/promises';
|
|
7
|
+
import { join } from 'node:path';
|
|
8
|
+
import { tmpdir } from 'node:os';
|
|
9
|
+
import { requirementsExtractFromPlans } from './requirements-extract-from-plans.js';
|
|
10
|
+
|
|
11
|
+
const P1 = `---
|
|
12
|
+
phase: 09-foundation
|
|
13
|
+
requirements:
|
|
14
|
+
- REQ-A
|
|
15
|
+
- REQ-B
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
<objective>
|
|
19
|
+
O
|
|
20
|
+
</objective>
|
|
21
|
+
<tasks><task type="auto"><name>T</name></task></tasks>
|
|
22
|
+
`;
|
|
23
|
+
|
|
24
|
+
const P2 = `---
|
|
25
|
+
phase: 09-foundation
|
|
26
|
+
requirements:
|
|
27
|
+
- REQ-B
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
<objective>
|
|
31
|
+
O2
|
|
32
|
+
</objective>
|
|
33
|
+
<tasks><task type="auto"><name>T</name></task></tasks>
|
|
34
|
+
`;
|
|
35
|
+
|
|
36
|
+
let tmpDir: string;
|
|
37
|
+
|
|
38
|
+
beforeEach(async () => {
|
|
39
|
+
tmpDir = await mkdtemp(join(tmpdir(), 'gsd-req-'));
|
|
40
|
+
const phaseDir = join(tmpDir, '.planning', 'phases', '09-foundation');
|
|
41
|
+
await mkdir(phaseDir, { recursive: true });
|
|
42
|
+
await writeFile(join(phaseDir, '09-01-PLAN.md'), P1);
|
|
43
|
+
await writeFile(join(phaseDir, '09-02-PLAN.md'), P2);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
afterEach(async () => {
|
|
47
|
+
await rm(tmpDir, { recursive: true, force: true });
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
describe('requirementsExtractFromPlans', () => {
|
|
51
|
+
it('dedupes requirements across plans', async () => {
|
|
52
|
+
const r = await requirementsExtractFromPlans(['9'], tmpDir);
|
|
53
|
+
const d = r.data as { requirements: string[]; by_plan: Record<string, string[]> };
|
|
54
|
+
expect(d.requirements.sort()).toEqual(['REQ-A', 'REQ-B']);
|
|
55
|
+
expect(d.by_plan['09-01'].sort()).toEqual(['REQ-A', 'REQ-B']);
|
|
56
|
+
expect(d.by_plan['09-02']).toEqual(['REQ-B']);
|
|
57
|
+
});
|
|
58
|
+
});
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* requirements.extract-from-plans — aggregate `requirements` frontmatter across all plans in a phase.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { readFile, readdir } from 'node:fs/promises';
|
|
6
|
+
import { join } from 'node:path';
|
|
7
|
+
import { GSDError, ErrorClassification } from '../errors.js';
|
|
8
|
+
import { extractFrontmatter } from './frontmatter.js';
|
|
9
|
+
import {
|
|
10
|
+
normalizePhaseName,
|
|
11
|
+
comparePhaseNum,
|
|
12
|
+
phaseTokenMatches,
|
|
13
|
+
planningPaths,
|
|
14
|
+
} from './helpers.js';
|
|
15
|
+
import type { QueryHandler } from './utils.js';
|
|
16
|
+
|
|
17
|
+
async function resolvePhaseDir(phase: string, projectDir: string): Promise<string | null> {
|
|
18
|
+
const phasesDir = planningPaths(projectDir).phases;
|
|
19
|
+
const normalized = normalizePhaseName(phase);
|
|
20
|
+
try {
|
|
21
|
+
const entries = await readdir(phasesDir, { withFileTypes: true });
|
|
22
|
+
const dirs = entries
|
|
23
|
+
.filter(e => e.isDirectory())
|
|
24
|
+
.map(e => e.name)
|
|
25
|
+
.sort((a, b) => comparePhaseNum(a, b));
|
|
26
|
+
const match = dirs.find(d => phaseTokenMatches(d, normalized));
|
|
27
|
+
return match ? join(phasesDir, match) : null;
|
|
28
|
+
} catch {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function normalizeReqList(v: unknown): string[] {
|
|
34
|
+
if (!v) return [];
|
|
35
|
+
if (Array.isArray(v)) return v.map((x) => String(x));
|
|
36
|
+
if (typeof v === 'string') return [v];
|
|
37
|
+
return [];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Args: `<phase>`
|
|
42
|
+
*/
|
|
43
|
+
export const requirementsExtractFromPlans: QueryHandler = async (args, projectDir) => {
|
|
44
|
+
const phase = args[0];
|
|
45
|
+
if (!phase) {
|
|
46
|
+
throw new GSDError('phase required', ErrorClassification.Validation);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const normalized = normalizePhaseName(phase);
|
|
50
|
+
const phaseDir = await resolvePhaseDir(phase, projectDir);
|
|
51
|
+
if (!phaseDir) {
|
|
52
|
+
return {
|
|
53
|
+
data: {
|
|
54
|
+
phase: normalized,
|
|
55
|
+
requirements: [] as string[],
|
|
56
|
+
by_plan: {} as Record<string, string[]>,
|
|
57
|
+
error: 'Phase not found',
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const files = await readdir(phaseDir);
|
|
63
|
+
const planFiles = files.filter(f => f.endsWith('-PLAN.md') || f === 'PLAN.md').sort();
|
|
64
|
+
const byPlan: Record<string, string[]> = {};
|
|
65
|
+
const seen = new Set<string>();
|
|
66
|
+
|
|
67
|
+
for (const planFile of planFiles) {
|
|
68
|
+
const planId =
|
|
69
|
+
planFile === 'PLAN.md' ? 'PLAN' : planFile.replace(/-PLAN\.md$/i, '').replace(/PLAN\.md$/i, '');
|
|
70
|
+
const content = await readFile(join(phaseDir, planFile), 'utf-8');
|
|
71
|
+
const fm = extractFrontmatter(content) as Record<string, unknown>;
|
|
72
|
+
const list = normalizeReqList(fm.requirements);
|
|
73
|
+
byPlan[planId] = list;
|
|
74
|
+
for (const r of list) {
|
|
75
|
+
seen.add(r);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return {
|
|
80
|
+
data: {
|
|
81
|
+
phase: normalized,
|
|
82
|
+
requirements: [...seen].sort(),
|
|
83
|
+
by_plan: byPlan,
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
};
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* roadmap.update-plan-progress — sync ROADMAP.md progress table + plan checkboxes
|
|
3
|
+
* from on-disk PLAN/SUMMARY counts for a phase.
|
|
4
|
+
*
|
|
5
|
+
* Port of `cmdRoadmapUpdatePlanProgress` from get-shit-done/bin/lib/roadmap.cjs
|
|
6
|
+
* (lines 257–354). Uses `findPhase` for disk stats and `readModifyWriteRoadmapMd`
|
|
7
|
+
* for atomic writes (same pattern as `phase.complete`).
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { findPhase } from './phase.js';
|
|
11
|
+
import { readModifyWriteRoadmapMd, replaceInCurrentMilestone } from './phase-lifecycle.js';
|
|
12
|
+
import { existsSync } from 'node:fs';
|
|
13
|
+
import { escapeRegex, planningPaths } from './helpers.js';
|
|
14
|
+
import { GSDError, ErrorClassification } from '../errors.js';
|
|
15
|
+
import type { QueryHandler } from './utils.js';
|
|
16
|
+
|
|
17
|
+
export const roadmapUpdatePlanProgress: QueryHandler = async (args, projectDir) => {
|
|
18
|
+
const phaseNum = args[0];
|
|
19
|
+
if (!phaseNum) {
|
|
20
|
+
throw new GSDError('phase number required for roadmap update-plan-progress', ErrorClassification.Validation);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const phaseResult = await findPhase([phaseNum], projectDir);
|
|
24
|
+
const info = phaseResult.data as {
|
|
25
|
+
found: boolean;
|
|
26
|
+
plans: string[];
|
|
27
|
+
summaries: string[];
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
if (!info.found) {
|
|
31
|
+
throw new GSDError(`Phase ${phaseNum} not found`, ErrorClassification.Validation);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const planCount = info.plans.length;
|
|
35
|
+
const summaryCount = info.summaries.length;
|
|
36
|
+
|
|
37
|
+
if (planCount === 0) {
|
|
38
|
+
return {
|
|
39
|
+
data: {
|
|
40
|
+
updated: false,
|
|
41
|
+
reason: 'No plans found',
|
|
42
|
+
plan_count: 0,
|
|
43
|
+
summary_count: 0,
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const isComplete = summaryCount >= planCount;
|
|
49
|
+
const status = isComplete ? 'Complete' : summaryCount > 0 ? 'In Progress' : 'Planned';
|
|
50
|
+
const today = new Date().toISOString().split('T')[0]!;
|
|
51
|
+
|
|
52
|
+
const roadmapPath = planningPaths(projectDir).roadmap;
|
|
53
|
+
if (!existsSync(roadmapPath)) {
|
|
54
|
+
return {
|
|
55
|
+
data: {
|
|
56
|
+
updated: false,
|
|
57
|
+
reason: 'ROADMAP.md not found',
|
|
58
|
+
plan_count: planCount,
|
|
59
|
+
summary_count: summaryCount,
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
await readModifyWriteRoadmapMd(projectDir, (roadmapContent) => {
|
|
65
|
+
const phaseEscaped = escapeRegex(phaseNum);
|
|
66
|
+
|
|
67
|
+
const tableRowPattern = new RegExp(
|
|
68
|
+
`^(\\|\\s*${phaseEscaped}\\.?\\s[^|]*(?:\\|[^\\n]*))$`,
|
|
69
|
+
'im',
|
|
70
|
+
);
|
|
71
|
+
const dateField = isComplete ? ` ${today} ` : ' ';
|
|
72
|
+
roadmapContent = roadmapContent.replace(tableRowPattern, (fullRow) => {
|
|
73
|
+
const cells = fullRow.split('|').slice(1, -1);
|
|
74
|
+
if (cells.length === 5) {
|
|
75
|
+
cells[2] = ` ${summaryCount}/${planCount} `;
|
|
76
|
+
cells[3] = ` ${status.padEnd(11)}`;
|
|
77
|
+
cells[4] = dateField;
|
|
78
|
+
} else if (cells.length === 4) {
|
|
79
|
+
cells[1] = ` ${summaryCount}/${planCount} `;
|
|
80
|
+
cells[2] = ` ${status.padEnd(11)}`;
|
|
81
|
+
cells[3] = dateField;
|
|
82
|
+
}
|
|
83
|
+
return '|' + cells.join('|') + '|';
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
const planCountPattern = new RegExp(
|
|
87
|
+
`(#{2,4}\\s*Phase\\s+${phaseEscaped}[\\s\\S]*?\\*\\*Plans:\\*\\*\\s*)[^\\n]+`,
|
|
88
|
+
'i',
|
|
89
|
+
);
|
|
90
|
+
const planCountText = isComplete
|
|
91
|
+
? `${summaryCount}/${planCount} plans complete`
|
|
92
|
+
: `${summaryCount}/${planCount} plans executed`;
|
|
93
|
+
roadmapContent = replaceInCurrentMilestone(roadmapContent, planCountPattern, `$1${planCountText}`);
|
|
94
|
+
|
|
95
|
+
if (isComplete) {
|
|
96
|
+
const checkboxPattern = new RegExp(
|
|
97
|
+
`(-\\s*\\[)[ ](\\]\\s*.*Phase\\s+${phaseEscaped}[:\\s][^\\n]*)`,
|
|
98
|
+
'i',
|
|
99
|
+
);
|
|
100
|
+
roadmapContent = replaceInCurrentMilestone(
|
|
101
|
+
roadmapContent,
|
|
102
|
+
checkboxPattern,
|
|
103
|
+
`$1x$2 (completed ${today})`,
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const summaries = info.summaries;
|
|
108
|
+
for (const summaryFile of summaries) {
|
|
109
|
+
const planId = summaryFile.replace('-SUMMARY.md', '').replace('SUMMARY.md', '');
|
|
110
|
+
if (!planId) continue;
|
|
111
|
+
const planEscaped = escapeRegex(planId);
|
|
112
|
+
const planCheckboxPattern = new RegExp(
|
|
113
|
+
`(-\\s*\\[) (\\]\\s*(?:\\*\\*)?${planEscaped}(?:\\*\\*)?)`,
|
|
114
|
+
'i',
|
|
115
|
+
);
|
|
116
|
+
roadmapContent = roadmapContent.replace(planCheckboxPattern, '$1x$2');
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return roadmapContent;
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
return {
|
|
123
|
+
data: {
|
|
124
|
+
updated: true,
|
|
125
|
+
phase: phaseNum,
|
|
126
|
+
plan_count: planCount,
|
|
127
|
+
summary_count: summaryCount,
|
|
128
|
+
status,
|
|
129
|
+
complete: isComplete,
|
|
130
|
+
},
|
|
131
|
+
};
|
|
132
|
+
};
|