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,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workstream utility functions for multi-workstream project support.
|
|
3
|
+
*
|
|
4
|
+
* When --ws <name> is provided, all .planning/ paths are routed to
|
|
5
|
+
* .planning/workstreams/<name>/ instead.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { posix } from 'node:path';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Validate a workstream name.
|
|
12
|
+
* Allowed: alphanumeric, hyphens, underscores, dots.
|
|
13
|
+
* Disallowed: empty, spaces, slashes, special chars, path traversal.
|
|
14
|
+
*/
|
|
15
|
+
export function validateWorkstreamName(name: string): boolean {
|
|
16
|
+
if (!name || name.length === 0) return false;
|
|
17
|
+
// Only allow alphanumeric, hyphens, underscores, dots
|
|
18
|
+
// Must not be ".." or start with ".." (path traversal)
|
|
19
|
+
if (name === '..' || name.startsWith('../')) return false;
|
|
20
|
+
return /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/.test(name);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Return the relative planning directory path.
|
|
25
|
+
*
|
|
26
|
+
* - Without workstream: `.planning`
|
|
27
|
+
* - With workstream: `.planning/workstreams/<name>`
|
|
28
|
+
*/
|
|
29
|
+
export function relPlanningPath(workstream?: string): string {
|
|
30
|
+
if (!workstream) return '.planning';
|
|
31
|
+
// Use POSIX segments so the same logical path string is used on all platforms (Windows included).
|
|
32
|
+
return posix.join('.planning', 'workstreams', workstream);
|
|
33
|
+
}
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for --ws (workstream) flag support.
|
|
3
|
+
*
|
|
4
|
+
* Validates:
|
|
5
|
+
* - CLI parsing of --ws flag
|
|
6
|
+
* - Workstream name validation
|
|
7
|
+
* - GSDOptions.workstream propagation
|
|
8
|
+
* - GSDTools workstream-aware invocation
|
|
9
|
+
* - Config path resolution with workstream
|
|
10
|
+
* - ContextEngine workstream-aware planning dir
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
14
|
+
import { mkdir, writeFile, rm } from 'node:fs/promises';
|
|
15
|
+
import { join } from 'node:path';
|
|
16
|
+
import { tmpdir } from 'node:os';
|
|
17
|
+
|
|
18
|
+
// ─── Workstream name validation ─────────────────────────────────────────────
|
|
19
|
+
|
|
20
|
+
import { validateWorkstreamName } from './workstream-utils.js';
|
|
21
|
+
|
|
22
|
+
describe('validateWorkstreamName', () => {
|
|
23
|
+
it('accepts alphanumeric names', () => {
|
|
24
|
+
expect(validateWorkstreamName('frontend')).toBe(true);
|
|
25
|
+
expect(validateWorkstreamName('backend2')).toBe(true);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('accepts names with hyphens', () => {
|
|
29
|
+
expect(validateWorkstreamName('my-feature')).toBe(true);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('accepts names with underscores', () => {
|
|
33
|
+
expect(validateWorkstreamName('my_feature')).toBe(true);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('accepts names with dots', () => {
|
|
37
|
+
expect(validateWorkstreamName('v1.0')).toBe(true);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('rejects empty strings', () => {
|
|
41
|
+
expect(validateWorkstreamName('')).toBe(false);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('rejects names with spaces', () => {
|
|
45
|
+
expect(validateWorkstreamName('my feature')).toBe(false);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('rejects names with slashes', () => {
|
|
49
|
+
expect(validateWorkstreamName('my/feature')).toBe(false);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('rejects names with special characters', () => {
|
|
53
|
+
expect(validateWorkstreamName('feat@ure')).toBe(false);
|
|
54
|
+
expect(validateWorkstreamName('feat!ure')).toBe(false);
|
|
55
|
+
expect(validateWorkstreamName('feat#ure')).toBe(false);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('rejects path traversal attempts', () => {
|
|
59
|
+
expect(validateWorkstreamName('..')).toBe(false);
|
|
60
|
+
expect(validateWorkstreamName('../etc')).toBe(false);
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
// ─── relPlanningPath helper ─────────────────────────────────────────────────
|
|
65
|
+
|
|
66
|
+
import { relPlanningPath } from './workstream-utils.js';
|
|
67
|
+
|
|
68
|
+
describe('relPlanningPath', () => {
|
|
69
|
+
it('returns .planning/ in flat mode (no workstream)', () => {
|
|
70
|
+
expect(relPlanningPath()).toBe('.planning');
|
|
71
|
+
expect(relPlanningPath(undefined)).toBe('.planning');
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('returns .planning/workstreams/<name>/ with workstream', () => {
|
|
75
|
+
expect(relPlanningPath('frontend')).toBe('.planning/workstreams/frontend');
|
|
76
|
+
expect(relPlanningPath('api-v2')).toBe('.planning/workstreams/api-v2');
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// ─── CLI --ws flag parsing ──────────────────────────────────────────────────
|
|
81
|
+
|
|
82
|
+
import { parseCliArgs } from './cli.js';
|
|
83
|
+
|
|
84
|
+
describe('parseCliArgs --ws flag', () => {
|
|
85
|
+
it('parses --ws flag', () => {
|
|
86
|
+
const result = parseCliArgs(['run', 'build auth', '--ws', 'frontend']);
|
|
87
|
+
|
|
88
|
+
expect(result.ws).toBe('frontend');
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('ws is undefined when not provided', () => {
|
|
92
|
+
const result = parseCliArgs(['run', 'build auth']);
|
|
93
|
+
|
|
94
|
+
expect(result.ws).toBeUndefined();
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('works with other flags', () => {
|
|
98
|
+
const result = parseCliArgs([
|
|
99
|
+
'run', 'build auth',
|
|
100
|
+
'--ws', 'backend',
|
|
101
|
+
'--model', 'claude-sonnet-4-6',
|
|
102
|
+
'--project-dir', '/tmp/test',
|
|
103
|
+
]);
|
|
104
|
+
|
|
105
|
+
expect(result.ws).toBe('backend');
|
|
106
|
+
expect(result.model).toBe('claude-sonnet-4-6');
|
|
107
|
+
expect(result.projectDir).toBe('/tmp/test');
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
// ─── GSDOptions.workstream ──────────────────────────────────────────────────
|
|
112
|
+
|
|
113
|
+
describe('GSDOptions.workstream', () => {
|
|
114
|
+
it('GSD class accepts workstream option', async () => {
|
|
115
|
+
// This is a compile-time check -- if the type is wrong, TS will fail
|
|
116
|
+
const { GSD } = await import('./index.js');
|
|
117
|
+
const gsd = new GSD({
|
|
118
|
+
projectDir: '/tmp/test-ws',
|
|
119
|
+
workstream: 'frontend',
|
|
120
|
+
});
|
|
121
|
+
// If we get here without a type error, the option is accepted
|
|
122
|
+
expect(gsd).toBeDefined();
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
// ─── GSDTools workstream injection ──────────────────────────────────────────
|
|
127
|
+
|
|
128
|
+
describe('GSDTools workstream injection', () => {
|
|
129
|
+
let tmpDir: string;
|
|
130
|
+
let fixtureDir: string;
|
|
131
|
+
|
|
132
|
+
beforeEach(async () => {
|
|
133
|
+
tmpDir = join(tmpdir(), `gsd-ws-test-${Date.now()}-${Math.random().toString(36).slice(2)}`);
|
|
134
|
+
fixtureDir = join(tmpDir, 'fixtures');
|
|
135
|
+
await mkdir(fixtureDir, { recursive: true });
|
|
136
|
+
await mkdir(join(tmpDir, '.planning'), { recursive: true });
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
afterEach(async () => {
|
|
140
|
+
await rm(tmpDir, { recursive: true, force: true });
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
async function createScript(name: string, code: string): Promise<string> {
|
|
144
|
+
const scriptPath = join(fixtureDir, name);
|
|
145
|
+
await writeFile(scriptPath, code, { mode: 0o755 });
|
|
146
|
+
return scriptPath;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
it('passes --ws flag to gsd-tools.cjs when workstream is set', async () => {
|
|
150
|
+
const { GSDTools } = await import('./gsd-tools.js');
|
|
151
|
+
|
|
152
|
+
// Script echoes its arguments as JSON
|
|
153
|
+
const scriptPath = await createScript(
|
|
154
|
+
'echo-args.cjs',
|
|
155
|
+
'process.stdout.write(JSON.stringify(process.argv.slice(2)));',
|
|
156
|
+
);
|
|
157
|
+
|
|
158
|
+
const tools = new GSDTools({
|
|
159
|
+
projectDir: tmpDir,
|
|
160
|
+
gsdToolsPath: scriptPath,
|
|
161
|
+
workstream: 'frontend',
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
const result = await tools.exec('state', ['load']) as string[];
|
|
165
|
+
|
|
166
|
+
// Should contain --ws frontend in the arguments
|
|
167
|
+
expect(result).toContain('--ws');
|
|
168
|
+
expect(result).toContain('frontend');
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it('does not pass --ws when workstream is undefined', async () => {
|
|
172
|
+
const { GSDTools } = await import('./gsd-tools.js');
|
|
173
|
+
|
|
174
|
+
const scriptPath = await createScript(
|
|
175
|
+
'echo-args-no-ws.cjs',
|
|
176
|
+
'process.stdout.write(JSON.stringify(process.argv.slice(2)));',
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
const tools = new GSDTools({
|
|
180
|
+
projectDir: tmpDir,
|
|
181
|
+
gsdToolsPath: scriptPath,
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
const result = await tools.exec('state', ['load']) as string[];
|
|
185
|
+
|
|
186
|
+
expect(result).not.toContain('--ws');
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
// ─── Config workstream-aware path ───────────────────────────────────────────
|
|
191
|
+
|
|
192
|
+
import { loadConfig } from './config.js';
|
|
193
|
+
|
|
194
|
+
describe('loadConfig with workstream', () => {
|
|
195
|
+
let tmpDir: string;
|
|
196
|
+
|
|
197
|
+
beforeEach(async () => {
|
|
198
|
+
tmpDir = join(tmpdir(), `gsd-config-ws-${Date.now()}-${Math.random().toString(36).slice(2)}`);
|
|
199
|
+
await mkdir(tmpDir, { recursive: true });
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
afterEach(async () => {
|
|
203
|
+
await rm(tmpDir, { recursive: true, force: true });
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
it('loads config from workstream path when workstream is provided', async () => {
|
|
207
|
+
const wsDir = join(tmpDir, '.planning', 'workstreams', 'frontend');
|
|
208
|
+
await mkdir(wsDir, { recursive: true });
|
|
209
|
+
await writeFile(
|
|
210
|
+
join(wsDir, 'config.json'),
|
|
211
|
+
JSON.stringify({ model_profile: 'performance' }),
|
|
212
|
+
);
|
|
213
|
+
|
|
214
|
+
const config = await loadConfig(tmpDir, 'frontend');
|
|
215
|
+
|
|
216
|
+
expect(config.model_profile).toBe('performance');
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
it('falls back to root config when workstream config is missing', async () => {
|
|
220
|
+
// Create root config but no workstream config
|
|
221
|
+
await mkdir(join(tmpDir, '.planning'), { recursive: true });
|
|
222
|
+
await writeFile(
|
|
223
|
+
join(tmpDir, '.planning', 'config.json'),
|
|
224
|
+
JSON.stringify({ model_profile: 'balanced' }),
|
|
225
|
+
);
|
|
226
|
+
|
|
227
|
+
const config = await loadConfig(tmpDir, 'frontend');
|
|
228
|
+
|
|
229
|
+
expect(config.model_profile).toBe('balanced');
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
it('loads from root .planning/ when workstream is undefined', async () => {
|
|
233
|
+
await mkdir(join(tmpDir, '.planning'), { recursive: true });
|
|
234
|
+
await writeFile(
|
|
235
|
+
join(tmpDir, '.planning', 'config.json'),
|
|
236
|
+
JSON.stringify({ model_profile: 'economy' }),
|
|
237
|
+
);
|
|
238
|
+
|
|
239
|
+
const config = await loadConfig(tmpDir);
|
|
240
|
+
|
|
241
|
+
expect(config.model_profile).toBe('economy');
|
|
242
|
+
});
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
// ─── ContextEngine workstream-aware planning dir ────────────────────────────
|
|
246
|
+
|
|
247
|
+
describe('ContextEngine with workstream', () => {
|
|
248
|
+
let tmpDir: string;
|
|
249
|
+
|
|
250
|
+
beforeEach(async () => {
|
|
251
|
+
tmpDir = join(tmpdir(), `gsd-ctx-ws-${Date.now()}-${Math.random().toString(36).slice(2)}`);
|
|
252
|
+
await mkdir(tmpDir, { recursive: true });
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
afterEach(async () => {
|
|
256
|
+
await rm(tmpDir, { recursive: true, force: true });
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
it('resolves files from workstream planning dir', async () => {
|
|
260
|
+
const { ContextEngine } = await import('./context-engine.js');
|
|
261
|
+
const { PhaseType } = await import('./types.js');
|
|
262
|
+
|
|
263
|
+
const wsDir = join(tmpDir, '.planning', 'workstreams', 'backend');
|
|
264
|
+
await mkdir(wsDir, { recursive: true });
|
|
265
|
+
await writeFile(join(wsDir, 'STATE.md'), '# State\nPhase: 01');
|
|
266
|
+
|
|
267
|
+
const engine = new ContextEngine(tmpDir, undefined, undefined, 'backend');
|
|
268
|
+
const files = await engine.resolveContextFiles(PhaseType.Execute);
|
|
269
|
+
|
|
270
|
+
expect(files.state).toContain('Phase: 01');
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
it('resolves files from root .planning/ without workstream', async () => {
|
|
274
|
+
const { ContextEngine } = await import('./context-engine.js');
|
|
275
|
+
const { PhaseType } = await import('./types.js');
|
|
276
|
+
|
|
277
|
+
await mkdir(join(tmpDir, '.planning'), { recursive: true });
|
|
278
|
+
await writeFile(join(tmpDir, '.planning', 'STATE.md'), '# State\nPhase: 02');
|
|
279
|
+
|
|
280
|
+
const engine = new ContextEngine(tmpDir);
|
|
281
|
+
const files = await engine.resolveContextFiles(PhaseType.Execute);
|
|
282
|
+
|
|
283
|
+
expect(files.state).toContain('Phase: 02');
|
|
284
|
+
});
|
|
285
|
+
});
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { describe, it, expect, afterEach } from 'vitest';
|
|
2
|
+
import { WebSocket } from 'ws';
|
|
3
|
+
import { WSTransport } from './ws-transport.js';
|
|
4
|
+
import { GSDEventType, type GSDEvent, type GSDEventBase } from './types.js';
|
|
5
|
+
|
|
6
|
+
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
7
|
+
|
|
8
|
+
function makeBase(overrides: Partial<GSDEventBase> = {}): Omit<GSDEventBase, 'type'> {
|
|
9
|
+
return {
|
|
10
|
+
timestamp: '2025-06-15T14:30:45.123Z',
|
|
11
|
+
sessionId: 'test-session',
|
|
12
|
+
...overrides,
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** Connect a WS client and resolve once open. */
|
|
17
|
+
function connectClient(port: number): Promise<WebSocket> {
|
|
18
|
+
return new Promise((resolve, reject) => {
|
|
19
|
+
const ws = new WebSocket(`ws://127.0.0.1:${port}`);
|
|
20
|
+
ws.on('open', () => resolve(ws));
|
|
21
|
+
ws.on('error', reject);
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Wait for the next message on a WS client. */
|
|
26
|
+
function waitForMessage(ws: WebSocket): Promise<string> {
|
|
27
|
+
return new Promise((resolve, reject) => {
|
|
28
|
+
const timeout = setTimeout(() => reject(new Error('message timeout')), 5000);
|
|
29
|
+
ws.once('message', (data) => {
|
|
30
|
+
clearTimeout(timeout);
|
|
31
|
+
resolve(data.toString());
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Track transports for cleanup
|
|
37
|
+
const activeTransports: WSTransport[] = [];
|
|
38
|
+
|
|
39
|
+
afterEach(() => {
|
|
40
|
+
for (const t of activeTransports) {
|
|
41
|
+
try { t.close(); } catch { /* ignore */ }
|
|
42
|
+
}
|
|
43
|
+
activeTransports.length = 0;
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// ─── Tests ───────────────────────────────────────────────────────────────────
|
|
47
|
+
|
|
48
|
+
describe('WSTransport', () => {
|
|
49
|
+
it('start() creates a server on the specified port', async () => {
|
|
50
|
+
const transport = new WSTransport({ port: 0 }); // dynamic port
|
|
51
|
+
activeTransports.push(transport);
|
|
52
|
+
|
|
53
|
+
await transport.start();
|
|
54
|
+
|
|
55
|
+
// Server is listening — we can connect a client
|
|
56
|
+
const address = (transport as any).server?.address();
|
|
57
|
+
expect(address).toBeTruthy();
|
|
58
|
+
expect(typeof address.port).toBe('number');
|
|
59
|
+
expect(address.port).toBeGreaterThan(0);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('onEvent broadcasts JSON to connected client', async () => {
|
|
63
|
+
const transport = new WSTransport({ port: 0 });
|
|
64
|
+
activeTransports.push(transport);
|
|
65
|
+
await transport.start();
|
|
66
|
+
|
|
67
|
+
const address = (transport as any).server?.address();
|
|
68
|
+
const client = await connectClient(address.port);
|
|
69
|
+
|
|
70
|
+
const event: GSDEvent = {
|
|
71
|
+
...makeBase(),
|
|
72
|
+
type: GSDEventType.SessionInit,
|
|
73
|
+
model: 'claude-sonnet-4-20250514',
|
|
74
|
+
tools: ['Read', 'Write'],
|
|
75
|
+
cwd: '/tmp/test',
|
|
76
|
+
} as GSDEvent;
|
|
77
|
+
|
|
78
|
+
const msgPromise = waitForMessage(client);
|
|
79
|
+
transport.onEvent(event);
|
|
80
|
+
|
|
81
|
+
const received = await msgPromise;
|
|
82
|
+
const parsed = JSON.parse(received);
|
|
83
|
+
|
|
84
|
+
expect(parsed.type).toBe('session_init');
|
|
85
|
+
expect(parsed.model).toBe('claude-sonnet-4-20250514');
|
|
86
|
+
expect(parsed.tools).toEqual(['Read', 'Write']);
|
|
87
|
+
|
|
88
|
+
client.close();
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('onEvent handles no connected clients without error', async () => {
|
|
92
|
+
const transport = new WSTransport({ port: 0 });
|
|
93
|
+
activeTransports.push(transport);
|
|
94
|
+
await transport.start();
|
|
95
|
+
|
|
96
|
+
// No clients connected — should not throw
|
|
97
|
+
expect(() => {
|
|
98
|
+
transport.onEvent({
|
|
99
|
+
...makeBase(),
|
|
100
|
+
type: GSDEventType.MilestoneStart,
|
|
101
|
+
phaseCount: 2,
|
|
102
|
+
prompt: 'test',
|
|
103
|
+
} as GSDEvent);
|
|
104
|
+
}).not.toThrow();
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('close() shuts down the server', async () => {
|
|
108
|
+
const transport = new WSTransport({ port: 0 });
|
|
109
|
+
// Don't push to activeTransports — we close manually
|
|
110
|
+
|
|
111
|
+
await transport.start();
|
|
112
|
+
const address = (transport as any).server?.address();
|
|
113
|
+
expect(address).toBeTruthy();
|
|
114
|
+
|
|
115
|
+
transport.close();
|
|
116
|
+
|
|
117
|
+
// Server should be null after close
|
|
118
|
+
expect((transport as any).server).toBeNull();
|
|
119
|
+
|
|
120
|
+
// Connecting should fail
|
|
121
|
+
await expect(connectClient(address.port)).rejects.toThrow();
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it('close() before start() does not throw', () => {
|
|
125
|
+
const transport = new WSTransport({ port: 0 });
|
|
126
|
+
expect(() => transport.close()).not.toThrow();
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it('broadcasts to multiple connected clients', async () => {
|
|
130
|
+
const transport = new WSTransport({ port: 0 });
|
|
131
|
+
activeTransports.push(transport);
|
|
132
|
+
await transport.start();
|
|
133
|
+
|
|
134
|
+
const address = (transport as any).server?.address();
|
|
135
|
+
const client1 = await connectClient(address.port);
|
|
136
|
+
const client2 = await connectClient(address.port);
|
|
137
|
+
|
|
138
|
+
const event: GSDEvent = {
|
|
139
|
+
...makeBase(),
|
|
140
|
+
type: GSDEventType.MilestoneComplete,
|
|
141
|
+
success: true,
|
|
142
|
+
totalCostUsd: 5.0,
|
|
143
|
+
totalDurationMs: 120000,
|
|
144
|
+
phasesCompleted: 3,
|
|
145
|
+
} as GSDEvent;
|
|
146
|
+
|
|
147
|
+
const msg1Promise = waitForMessage(client1);
|
|
148
|
+
const msg2Promise = waitForMessage(client2);
|
|
149
|
+
|
|
150
|
+
transport.onEvent(event);
|
|
151
|
+
|
|
152
|
+
const [msg1, msg2] = await Promise.all([msg1Promise, msg2Promise]);
|
|
153
|
+
|
|
154
|
+
expect(JSON.parse(msg1).type).toBe('milestone_complete');
|
|
155
|
+
expect(JSON.parse(msg2).type).toBe('milestone_complete');
|
|
156
|
+
expect(JSON.parse(msg1).success).toBe(true);
|
|
157
|
+
|
|
158
|
+
client1.close();
|
|
159
|
+
client2.close();
|
|
160
|
+
});
|
|
161
|
+
});
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WebSocket Transport — broadcasts GSD events as JSON over WebSocket.
|
|
3
|
+
*
|
|
4
|
+
* Implements TransportHandler. Starts a WebSocketServer on a given port
|
|
5
|
+
* and JSON-serializes each event to all connected clients.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { WebSocketServer, WebSocket } from 'ws';
|
|
9
|
+
import type { GSDEvent, TransportHandler } from './types.js';
|
|
10
|
+
|
|
11
|
+
export interface WSTransportOptions {
|
|
12
|
+
port: number;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export class WSTransport implements TransportHandler {
|
|
16
|
+
private readonly port: number;
|
|
17
|
+
private server: WebSocketServer | null = null;
|
|
18
|
+
private closing = false;
|
|
19
|
+
|
|
20
|
+
constructor(options: WSTransportOptions) {
|
|
21
|
+
this.port = options.port;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Start the WebSocket server on the configured port.
|
|
26
|
+
* Resolves once the server is listening.
|
|
27
|
+
*/
|
|
28
|
+
async start(): Promise<void> {
|
|
29
|
+
if (this.closing) return;
|
|
30
|
+
|
|
31
|
+
return new Promise<void>((resolve, reject) => {
|
|
32
|
+
try {
|
|
33
|
+
this.server = new WebSocketServer({ port: this.port });
|
|
34
|
+
this.server.on('listening', () => resolve());
|
|
35
|
+
this.server.on('error', (err) => reject(err));
|
|
36
|
+
} catch (err) {
|
|
37
|
+
reject(err);
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Broadcast a GSD event as JSON to all connected clients.
|
|
44
|
+
* Never throws — wraps each client.send in try/catch.
|
|
45
|
+
*/
|
|
46
|
+
onEvent(event: GSDEvent): void {
|
|
47
|
+
try {
|
|
48
|
+
if (!this.server) return;
|
|
49
|
+
|
|
50
|
+
const payload = JSON.stringify(event);
|
|
51
|
+
|
|
52
|
+
for (const client of this.server.clients) {
|
|
53
|
+
if (client.readyState === WebSocket.OPEN) {
|
|
54
|
+
try {
|
|
55
|
+
client.send(payload);
|
|
56
|
+
} catch {
|
|
57
|
+
// Ignore individual client send errors
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
} catch {
|
|
62
|
+
// TransportHandler contract: onEvent must never throw
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Close all client connections and shut down the server.
|
|
68
|
+
* Safe to call before start() — sets a closing flag.
|
|
69
|
+
*/
|
|
70
|
+
close(): void {
|
|
71
|
+
this.closing = true;
|
|
72
|
+
|
|
73
|
+
if (!this.server) return;
|
|
74
|
+
|
|
75
|
+
// Terminate all clients
|
|
76
|
+
for (const client of this.server.clients) {
|
|
77
|
+
try {
|
|
78
|
+
client.terminate();
|
|
79
|
+
} catch {
|
|
80
|
+
// Ignore client close errors
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Close the server
|
|
85
|
+
try {
|
|
86
|
+
this.server.close();
|
|
87
|
+
} catch {
|
|
88
|
+
// Ignore server close errors
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
this.server = null;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "NodeNext",
|
|
5
|
+
"moduleResolution": "NodeNext",
|
|
6
|
+
"strict": true,
|
|
7
|
+
"outDir": "dist",
|
|
8
|
+
"rootDir": "src",
|
|
9
|
+
"declaration": true,
|
|
10
|
+
"declarationMap": true,
|
|
11
|
+
"sourceMap": true,
|
|
12
|
+
"esModuleInterop": true,
|
|
13
|
+
"skipLibCheck": true,
|
|
14
|
+
"forceConsistentCasingInFileNames": true,
|
|
15
|
+
"resolveJsonModule": true,
|
|
16
|
+
"isolatedModules": true
|
|
17
|
+
},
|
|
18
|
+
"include": ["src/**/*.ts"],
|
|
19
|
+
"exclude": ["src/**/*.test.ts", "src/**/*.integration.test.ts", "dist", "node_modules"]
|
|
20
|
+
}
|