gsd-pi 2.71.0-dev.e17e0ce → 2.72.0-dev.3118184
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/README.md +46 -3
- package/dist/cli.js +76 -3
- package/dist/mcp-server.js +37 -14
- package/dist/onboarding.js +10 -0
- package/dist/resources/agents/debugger.md +58 -0
- package/dist/resources/agents/doc-writer.md +43 -0
- package/dist/resources/agents/git-ops.md +56 -0
- package/dist/resources/agents/javascript-pro.md +46 -271
- package/dist/resources/agents/planner.md +55 -0
- package/dist/resources/agents/refactorer.md +47 -0
- package/dist/resources/agents/reviewer.md +48 -0
- package/dist/resources/agents/security.md +59 -0
- package/dist/resources/agents/tester.md +50 -0
- package/dist/resources/agents/typescript-pro.md +41 -235
- package/dist/resources/extensions/async-jobs/await-tool.js +7 -4
- package/dist/resources/extensions/async-jobs/job-manager.js +28 -3
- package/dist/resources/extensions/claude-code-cli/partial-builder.js +40 -12
- package/dist/resources/extensions/claude-code-cli/stream-adapter.js +132 -10
- package/dist/resources/extensions/gsd/auto/loop.js +84 -1
- package/dist/resources/extensions/gsd/auto/phases.js +4 -0
- package/dist/resources/extensions/gsd/auto-post-unit.js +6 -0
- package/dist/resources/extensions/gsd/auto-prompts.js +88 -33
- package/dist/resources/extensions/gsd/auto-recovery.js +11 -0
- package/dist/resources/extensions/gsd/auto-start.js +24 -4
- package/dist/resources/extensions/gsd/auto.js +29 -19
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +3 -3
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +9 -11
- package/dist/resources/extensions/gsd/bootstrap/register-shortcuts.js +2 -5
- package/dist/resources/extensions/gsd/commands-handlers.js +4 -1
- package/dist/resources/extensions/gsd/context-injector.js +1 -1
- package/dist/resources/extensions/gsd/custom-workflow-engine.js +3 -7
- package/dist/resources/extensions/gsd/definition-io.js +15 -0
- package/dist/resources/extensions/gsd/dispatch-guard.js +4 -0
- package/dist/resources/extensions/gsd/doctor-providers.js +23 -0
- package/dist/resources/extensions/gsd/doctor-runtime-checks.js +6 -3
- package/dist/resources/extensions/gsd/error-classifier.js +4 -1
- package/dist/resources/extensions/gsd/gate-registry.js +208 -0
- package/dist/resources/extensions/gsd/git-service.js +11 -8
- package/dist/resources/extensions/gsd/gitignore.js +12 -6
- package/dist/resources/extensions/gsd/gsd-db.js +90 -6
- package/dist/resources/extensions/gsd/key-manager.js +2 -0
- package/dist/resources/extensions/gsd/milestone-validation-gates.js +11 -12
- package/dist/resources/extensions/gsd/notification-overlay.js +26 -12
- package/dist/resources/extensions/gsd/notification-store.js +5 -4
- package/dist/resources/extensions/gsd/preferences-skills.js +2 -34
- package/dist/resources/extensions/gsd/preferences-types.js +15 -0
- package/dist/resources/extensions/gsd/preferences.js +16 -3
- package/dist/resources/extensions/gsd/prompt-loader.js +4 -1
- package/dist/resources/extensions/gsd/prompt-validation.js +126 -0
- package/dist/resources/extensions/gsd/prompts/complete-slice.md +3 -1
- package/dist/resources/extensions/gsd/prompts/discuss.md +122 -13
- package/dist/resources/extensions/gsd/prompts/execute-task.md +2 -0
- package/dist/resources/extensions/gsd/prompts/system.md +1 -1
- package/dist/resources/extensions/gsd/prompts/validate-milestone.md +2 -0
- package/dist/resources/extensions/gsd/shortcut-defs.js +7 -1
- package/dist/resources/extensions/gsd/state.js +29 -2
- package/dist/resources/extensions/gsd/tools/complete-slice.js +52 -1
- package/dist/resources/extensions/gsd/tools/complete-task.js +51 -1
- package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +4 -1
- package/dist/resources/extensions/gsd/workflow-projections.js +7 -0
- package/dist/resources/extensions/gsd/worktree-manager.js +30 -3
- package/dist/resources/extensions/gsd/write-intercept.js +10 -1
- package/dist/resources/extensions/ollama/index.js +17 -10
- package/dist/resources/extensions/ollama/ollama-client.js +35 -6
- package/dist/resources/extensions/ollama/ollama-discovery.js +32 -6
- package/dist/resources/extensions/shared/gsd-phase-state.js +35 -0
- package/dist/resources/extensions/subagent/agents.js +8 -0
- package/dist/resources/extensions/subagent/index.js +17 -0
- package/dist/startup-model-validation.d.ts +0 -1
- package/dist/startup-model-validation.js +6 -2
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +14 -14
- package/dist/web/standalone/.next/build-manifest.json +2 -2
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
- package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
- package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/experimental/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/notifications/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +3 -3
- package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/page.js +2 -2
- package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +14 -14
- package/dist/web/standalone/.next/server/chunks/2331.js +16 -16
- package/dist/web/standalone/.next/server/chunks/4741.js +12 -12
- package/dist/web/standalone/.next/server/chunks/5822.js +2 -2
- package/dist/web/standalone/.next/server/chunks/63.js +8 -8
- package/dist/web/standalone/.next/server/chunks/6897.js +3 -3
- package/dist/web/standalone/.next/server/edge-runtime-webpack.js +2 -0
- package/dist/web/standalone/.next/server/functions-config-manifest.json +0 -9
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware-manifest.json +29 -2
- package/dist/web/standalone/.next/server/middleware.js +4 -12
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +1 -1
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/dist/web/standalone/.next/server/webpack-runtime.js +1 -1
- package/package.json +1 -1
- package/packages/mcp-server/dist/server.d.ts +12 -1
- package/packages/mcp-server/dist/server.d.ts.map +1 -1
- package/packages/mcp-server/dist/server.js +90 -42
- package/packages/mcp-server/dist/server.js.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.js +1 -1
- package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
- package/packages/mcp-server/src/server.ts +110 -38
- package/packages/mcp-server/src/workflow-tools.ts +1 -1
- package/packages/pi-ai/dist/env-api-keys.js +1 -0
- package/packages/pi-ai/dist/env-api-keys.js.map +1 -1
- package/packages/pi-ai/dist/models.custom.d.ts +105 -0
- package/packages/pi-ai/dist/models.custom.d.ts.map +1 -1
- package/packages/pi-ai/dist/models.custom.js +97 -0
- package/packages/pi-ai/dist/models.custom.js.map +1 -1
- package/packages/pi-ai/dist/models.generated.d.ts +648 -140
- package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
- package/packages/pi-ai/dist/models.generated.js +867 -370
- package/packages/pi-ai/dist/models.generated.js.map +1 -1
- package/packages/pi-ai/dist/models.generated.test.d.ts +2 -0
- package/packages/pi-ai/dist/models.generated.test.d.ts.map +1 -0
- package/packages/pi-ai/dist/models.generated.test.js +334 -0
- package/packages/pi-ai/dist/models.generated.test.js.map +1 -0
- package/packages/pi-ai/dist/models.test.js +105 -0
- package/packages/pi-ai/dist/models.test.js.map +1 -1
- package/packages/pi-ai/dist/types.d.ts +1 -1
- package/packages/pi-ai/dist/types.d.ts.map +1 -1
- package/packages/pi-ai/dist/types.js.map +1 -1
- package/packages/pi-ai/dist/utils/oauth/github-copilot.d.ts.map +1 -1
- package/packages/pi-ai/dist/utils/oauth/github-copilot.js +5 -1
- package/packages/pi-ai/dist/utils/oauth/github-copilot.js.map +1 -1
- package/packages/pi-ai/dist/utils/oauth/github-copilot.test.d.ts +2 -0
- package/packages/pi-ai/dist/utils/oauth/github-copilot.test.d.ts.map +1 -0
- package/packages/pi-ai/dist/utils/oauth/github-copilot.test.js +57 -0
- package/packages/pi-ai/dist/utils/oauth/github-copilot.test.js.map +1 -0
- package/packages/pi-ai/src/env-api-keys.ts +1 -0
- package/packages/pi-ai/src/models.custom.ts +98 -0
- package/packages/pi-ai/src/models.generated.test.ts +373 -0
- package/packages/pi-ai/src/models.generated.ts +867 -370
- package/packages/pi-ai/src/models.test.ts +135 -0
- package/packages/pi-ai/src/types.ts +1 -0
- package/packages/pi-ai/src/utils/oauth/github-copilot.test.ts +71 -0
- package/packages/pi-ai/src/utils/oauth/github-copilot.ts +4 -1
- package/packages/pi-coding-agent/dist/core/model-resolver.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-resolver.js +1 -0
- package/packages/pi-coding-agent/dist/core/model-resolver.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-resolver.test.d.ts +8 -0
- package/packages/pi-coding-agent/dist/core/model-resolver.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-resolver.test.js +75 -0
- package/packages/pi-coding-agent/dist/core/model-resolver.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/retry-handler.d.ts +5 -0
- package/packages/pi-coding-agent/dist/core/retry-handler.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/retry-handler.js +55 -1
- package/packages/pi-coding-agent/dist/core/retry-handler.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/retry-handler.test.js +57 -0
- package/packages/pi-coding-agent/dist/core/retry-handler.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/sdk.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/sdk.js +9 -0
- package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js +36 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.js +9 -2
- package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +87 -12
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/model-controller.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/model-controller.js +6 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/model-controller.js.map +1 -1
- package/packages/pi-coding-agent/package.json +1 -1
- package/packages/pi-coding-agent/src/core/model-resolver.test.ts +85 -0
- package/packages/pi-coding-agent/src/core/model-resolver.ts +1 -0
- package/packages/pi-coding-agent/src/core/retry-handler.test.ts +83 -0
- package/packages/pi-coding-agent/src/core/retry-handler.ts +60 -1
- package/packages/pi-coding-agent/src/core/sdk.ts +10 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/tool-execution.test.ts +72 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/model-selector.ts +15 -6
- package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +84 -12
- package/packages/pi-coding-agent/src/modes/interactive/controllers/model-controller.ts +6 -1
- package/packages/pi-tui/dist/components/__tests__/editor.test.js +12 -0
- package/packages/pi-tui/dist/components/__tests__/editor.test.js.map +1 -1
- package/packages/pi-tui/dist/components/__tests__/input.test.js +12 -0
- package/packages/pi-tui/dist/components/__tests__/input.test.js.map +1 -1
- package/packages/pi-tui/dist/keys.d.ts.map +1 -1
- package/packages/pi-tui/dist/keys.js +27 -0
- package/packages/pi-tui/dist/keys.js.map +1 -1
- package/packages/pi-tui/src/components/__tests__/editor.test.ts +18 -0
- package/packages/pi-tui/src/components/__tests__/input.test.ts +18 -0
- package/packages/pi-tui/src/keys.ts +32 -0
- package/pkg/package.json +1 -1
- package/src/resources/agents/debugger.md +58 -0
- package/src/resources/agents/doc-writer.md +43 -0
- package/src/resources/agents/git-ops.md +56 -0
- package/src/resources/agents/javascript-pro.md +46 -271
- package/src/resources/agents/planner.md +55 -0
- package/src/resources/agents/refactorer.md +47 -0
- package/src/resources/agents/reviewer.md +48 -0
- package/src/resources/agents/security.md +59 -0
- package/src/resources/agents/tester.md +50 -0
- package/src/resources/agents/typescript-pro.md +41 -235
- package/src/resources/extensions/async-jobs/await-tool.test.ts +40 -7
- package/src/resources/extensions/async-jobs/await-tool.ts +7 -4
- package/src/resources/extensions/async-jobs/job-manager.ts +33 -3
- package/src/resources/extensions/claude-code-cli/partial-builder.ts +45 -12
- package/src/resources/extensions/claude-code-cli/stream-adapter.ts +139 -8
- package/src/resources/extensions/claude-code-cli/tests/partial-builder.test.ts +91 -2
- package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +245 -2
- package/src/resources/extensions/gsd/auto/loop.ts +89 -1
- package/src/resources/extensions/gsd/auto/phases.ts +4 -0
- package/src/resources/extensions/gsd/auto-post-unit.ts +7 -0
- package/src/resources/extensions/gsd/auto-prompts.ts +111 -33
- package/src/resources/extensions/gsd/auto-recovery.ts +10 -0
- package/src/resources/extensions/gsd/auto-start.ts +31 -4
- package/src/resources/extensions/gsd/auto.ts +29 -20
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +3 -3
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +8 -10
- package/src/resources/extensions/gsd/bootstrap/register-shortcuts.ts +2 -5
- package/src/resources/extensions/gsd/commands-handlers.ts +5 -1
- package/src/resources/extensions/gsd/context-injector.ts +1 -1
- package/src/resources/extensions/gsd/custom-workflow-engine.ts +4 -8
- package/src/resources/extensions/gsd/definition-io.ts +18 -0
- package/src/resources/extensions/gsd/dispatch-guard.ts +5 -0
- package/src/resources/extensions/gsd/doctor-providers.ts +24 -0
- package/src/resources/extensions/gsd/doctor-runtime-checks.ts +6 -3
- package/src/resources/extensions/gsd/error-classifier.ts +4 -1
- package/src/resources/extensions/gsd/gate-registry.ts +251 -0
- package/src/resources/extensions/gsd/git-service.ts +11 -8
- package/src/resources/extensions/gsd/gitignore.ts +12 -6
- package/src/resources/extensions/gsd/gsd-db.ts +105 -6
- package/src/resources/extensions/gsd/key-manager.ts +2 -0
- package/src/resources/extensions/gsd/milestone-validation-gates.ts +11 -13
- package/src/resources/extensions/gsd/notification-overlay.ts +27 -11
- package/src/resources/extensions/gsd/notification-store.ts +5 -4
- package/src/resources/extensions/gsd/preferences-skills.ts +2 -36
- package/src/resources/extensions/gsd/preferences-types.ts +16 -0
- package/src/resources/extensions/gsd/preferences.ts +19 -6
- package/src/resources/extensions/gsd/prompt-loader.ts +6 -1
- package/src/resources/extensions/gsd/prompt-validation.ts +157 -0
- package/src/resources/extensions/gsd/prompts/complete-slice.md +3 -1
- package/src/resources/extensions/gsd/prompts/discuss.md +122 -13
- package/src/resources/extensions/gsd/prompts/execute-task.md +2 -0
- package/src/resources/extensions/gsd/prompts/system.md +1 -1
- package/src/resources/extensions/gsd/prompts/validate-milestone.md +2 -0
- package/src/resources/extensions/gsd/shortcut-defs.ts +8 -1
- package/src/resources/extensions/gsd/state.ts +33 -2
- package/src/resources/extensions/gsd/tests/auto-paused-ui-cleanup.test.ts +27 -0
- package/src/resources/extensions/gsd/tests/auto-start-model-capture.test.ts +14 -0
- package/src/resources/extensions/gsd/tests/block-db-writes.test.ts +63 -0
- package/src/resources/extensions/gsd/tests/complete-slice-gate-closure.test.ts +167 -0
- package/src/resources/extensions/gsd/tests/definition-io.test.ts +57 -0
- package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +26 -0
- package/src/resources/extensions/gsd/tests/doctor-heal-fixable-warnings.test.ts +14 -0
- package/src/resources/extensions/gsd/tests/doctor-providers.test.ts +36 -0
- package/src/resources/extensions/gsd/tests/false-degraded-mode-warning.test.ts +104 -0
- package/src/resources/extensions/gsd/tests/format-shortcut.test.ts +16 -0
- package/src/resources/extensions/gsd/tests/gate-dispatch.test.ts +27 -0
- package/src/resources/extensions/gsd/tests/gate-registry.test.ts +140 -0
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +107 -5
- package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +8 -6
- package/src/resources/extensions/gsd/tests/key-manager.test.ts +63 -0
- package/src/resources/extensions/gsd/tests/memory-pressure-stuck-state.test.ts +54 -0
- package/src/resources/extensions/gsd/tests/plan-milestone-artifact-verification.test.ts +62 -0
- package/src/resources/extensions/gsd/tests/post-unit-state-rebuild.test.ts +34 -0
- package/src/resources/extensions/gsd/tests/preferences-formatting.test.ts +87 -0
- package/src/resources/extensions/gsd/tests/preferences.test.ts +53 -0
- package/src/resources/extensions/gsd/tests/projection-regression.test.ts +96 -1
- package/src/resources/extensions/gsd/tests/prompt-loader-working-directory.test.ts +19 -0
- package/src/resources/extensions/gsd/tests/prompt-system-gate-coverage.test.ts +208 -0
- package/src/resources/extensions/gsd/tests/provider-errors.test.ts +9 -0
- package/src/resources/extensions/gsd/tests/register-hooks-depth-verification.test.ts +97 -0
- package/src/resources/extensions/gsd/tests/register-shortcuts.test.ts +3 -2
- package/src/resources/extensions/gsd/tests/stale-slice-rows.test.ts +41 -0
- package/src/resources/extensions/gsd/tools/complete-slice.ts +63 -0
- package/src/resources/extensions/gsd/tools/complete-task.ts +63 -0
- package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +4 -1
- package/src/resources/extensions/gsd/types.ts +26 -0
- package/src/resources/extensions/gsd/workflow-projections.ts +8 -0
- package/src/resources/extensions/gsd/worktree-manager.ts +29 -3
- package/src/resources/extensions/gsd/write-intercept.ts +10 -1
- package/src/resources/extensions/ollama/index.ts +17 -8
- package/src/resources/extensions/ollama/ollama-client.ts +35 -6
- package/src/resources/extensions/ollama/ollama-discovery.ts +37 -6
- package/src/resources/extensions/ollama/ollama-status-indicator.test.ts +28 -0
- package/src/resources/extensions/ollama/tests/ollama-discovery.test.ts +54 -0
- package/src/resources/extensions/shared/gsd-phase-state.ts +42 -0
- package/src/resources/extensions/shared/tests/gsd-phase-state.test.ts +48 -0
- package/src/resources/extensions/subagent/agents.ts +10 -0
- package/src/resources/extensions/subagent/index.ts +18 -0
- package/src/resources/extensions/subagent/tests/agents-conflicts.test.ts +33 -0
- /package/dist/web/standalone/.next/static/{cYPZv_bAhZk2ms-Pz6vsY → NzO79SOz9jHX-VY5-0t2O}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{cYPZv_bAhZk2ms-Pz6vsY → NzO79SOz9jHX-VY5-0t2O}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for formatSkillRef — pure formatting logic for skill references
|
|
3
|
+
* in the system prompt. Moved from preferences-skills.ts to preferences-types.ts
|
|
4
|
+
* to break the preferences ↔ preferences-skills circular dependency.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { describe, test } from "node:test";
|
|
8
|
+
import assert from "node:assert/strict";
|
|
9
|
+
|
|
10
|
+
import { formatSkillRef } from "../preferences-types.ts";
|
|
11
|
+
import type { SkillResolution } from "../preferences-types.ts";
|
|
12
|
+
|
|
13
|
+
function makeResolutions(entries: [string, Partial<SkillResolution>][]): Map<string, SkillResolution> {
|
|
14
|
+
const map = new Map<string, SkillResolution>();
|
|
15
|
+
for (const [key, partial] of entries) {
|
|
16
|
+
map.set(key, {
|
|
17
|
+
original: partial.original ?? key,
|
|
18
|
+
resolvedPath: partial.resolvedPath ?? null,
|
|
19
|
+
method: partial.method ?? "unresolved",
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
return map;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
describe("formatSkillRef", () => {
|
|
26
|
+
test("marks unresolved references with a warning", () => {
|
|
27
|
+
const resolutions = makeResolutions([
|
|
28
|
+
["my-skill", { method: "unresolved" }],
|
|
29
|
+
]);
|
|
30
|
+
const result = formatSkillRef("my-skill", resolutions);
|
|
31
|
+
assert.match(result, /my-skill/);
|
|
32
|
+
assert.match(result, /not found/);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test("marks unknown references (not in map) with a warning", () => {
|
|
36
|
+
const resolutions = new Map<string, SkillResolution>();
|
|
37
|
+
const result = formatSkillRef("unknown-skill", resolutions);
|
|
38
|
+
assert.match(result, /unknown-skill/);
|
|
39
|
+
assert.match(result, /not found/);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test("returns bare ref for absolute-path resolution", () => {
|
|
43
|
+
const resolutions = makeResolutions([
|
|
44
|
+
["/home/user/skills/SKILL.md", {
|
|
45
|
+
method: "absolute-path",
|
|
46
|
+
resolvedPath: "/home/user/skills/SKILL.md",
|
|
47
|
+
}],
|
|
48
|
+
]);
|
|
49
|
+
const result = formatSkillRef("/home/user/skills/SKILL.md", resolutions);
|
|
50
|
+
assert.equal(result, "/home/user/skills/SKILL.md");
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test("returns bare ref for absolute-dir resolution", () => {
|
|
54
|
+
const resolutions = makeResolutions([
|
|
55
|
+
["/home/user/skills/my-skill", {
|
|
56
|
+
method: "absolute-dir",
|
|
57
|
+
resolvedPath: "/home/user/skills/my-skill/SKILL.md",
|
|
58
|
+
}],
|
|
59
|
+
]);
|
|
60
|
+
const result = formatSkillRef("/home/user/skills/my-skill", resolutions);
|
|
61
|
+
assert.equal(result, "/home/user/skills/my-skill");
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test("shows resolved path for user-skill resolution", () => {
|
|
65
|
+
const resolutions = makeResolutions([
|
|
66
|
+
["code-review", {
|
|
67
|
+
method: "user-skill",
|
|
68
|
+
resolvedPath: "/home/user/.claude/skills/code-review/SKILL.md",
|
|
69
|
+
}],
|
|
70
|
+
]);
|
|
71
|
+
const result = formatSkillRef("code-review", resolutions);
|
|
72
|
+
assert.match(result, /code-review/);
|
|
73
|
+
assert.match(result, /\.claude\/skills\/code-review\/SKILL\.md/);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test("shows resolved path for project-skill resolution", () => {
|
|
77
|
+
const resolutions = makeResolutions([
|
|
78
|
+
["lint-fix", {
|
|
79
|
+
method: "project-skill",
|
|
80
|
+
resolvedPath: "/repo/.gsd/skills/lint-fix/SKILL.md",
|
|
81
|
+
}],
|
|
82
|
+
]);
|
|
83
|
+
const result = formatSkillRef("lint-fix", resolutions);
|
|
84
|
+
assert.match(result, /lint-fix/);
|
|
85
|
+
assert.match(result, /\.gsd\/skills\/lint-fix\/SKILL\.md/);
|
|
86
|
+
});
|
|
87
|
+
});
|
|
@@ -10,10 +10,14 @@
|
|
|
10
10
|
|
|
11
11
|
import test from "node:test";
|
|
12
12
|
import assert from "node:assert/strict";
|
|
13
|
+
import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
14
|
+
import { tmpdir } from "node:os";
|
|
15
|
+
import { join } from "node:path";
|
|
13
16
|
import {
|
|
14
17
|
validatePreferences,
|
|
15
18
|
applyModeDefaults,
|
|
16
19
|
getIsolationMode,
|
|
20
|
+
loadEffectiveGSDPreferences,
|
|
17
21
|
parsePreferencesMarkdown,
|
|
18
22
|
_resetParseWarningFlag,
|
|
19
23
|
} from "../preferences.ts";
|
|
@@ -501,6 +505,55 @@ test("experimental.rtk parses correctly from preferences markdown", () => {
|
|
|
501
505
|
assert.equal(prefs!.experimental?.rtk, true);
|
|
502
506
|
});
|
|
503
507
|
|
|
508
|
+
test("loadEffectiveGSDPreferences preserves experimental prefs across global+project merge", () => {
|
|
509
|
+
const originalCwd = process.cwd();
|
|
510
|
+
const originalGsdHome = process.env.GSD_HOME;
|
|
511
|
+
const tempProject = mkdtempSync(join(tmpdir(), "gsd-prefs-project-"));
|
|
512
|
+
const tempGsdHome = mkdtempSync(join(tmpdir(), "gsd-prefs-home-"));
|
|
513
|
+
|
|
514
|
+
try {
|
|
515
|
+
mkdirSync(join(tempProject, ".gsd"), { recursive: true });
|
|
516
|
+
|
|
517
|
+
writeFileSync(
|
|
518
|
+
join(tempGsdHome, "preferences.md"),
|
|
519
|
+
[
|
|
520
|
+
"---",
|
|
521
|
+
"version: 1",
|
|
522
|
+
"experimental:",
|
|
523
|
+
" rtk: true",
|
|
524
|
+
"---",
|
|
525
|
+
].join("\n"),
|
|
526
|
+
"utf-8",
|
|
527
|
+
);
|
|
528
|
+
|
|
529
|
+
writeFileSync(
|
|
530
|
+
join(tempProject, ".gsd", "PREFERENCES.md"),
|
|
531
|
+
[
|
|
532
|
+
"---",
|
|
533
|
+
"version: 1",
|
|
534
|
+
"git:",
|
|
535
|
+
" isolation: none",
|
|
536
|
+
"---",
|
|
537
|
+
].join("\n"),
|
|
538
|
+
"utf-8",
|
|
539
|
+
);
|
|
540
|
+
|
|
541
|
+
process.env.GSD_HOME = tempGsdHome;
|
|
542
|
+
process.chdir(tempProject);
|
|
543
|
+
|
|
544
|
+
const loaded = loadEffectiveGSDPreferences();
|
|
545
|
+
assert.notEqual(loaded, null);
|
|
546
|
+
assert.equal(loaded!.preferences.experimental?.rtk, true);
|
|
547
|
+
assert.equal(loaded!.preferences.git?.isolation, "none");
|
|
548
|
+
} finally {
|
|
549
|
+
process.chdir(originalCwd);
|
|
550
|
+
if (originalGsdHome === undefined) delete process.env.GSD_HOME;
|
|
551
|
+
else process.env.GSD_HOME = originalGsdHome;
|
|
552
|
+
rmSync(tempProject, { recursive: true, force: true });
|
|
553
|
+
rmSync(tempGsdHome, { recursive: true, force: true });
|
|
554
|
+
}
|
|
555
|
+
});
|
|
556
|
+
|
|
504
557
|
test("experimental.rtk defaults to off in new project preferences", () => {
|
|
505
558
|
// No experimental key → feature is disabled
|
|
506
559
|
const content = "---\nversion: 1\n---\n";
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import test from 'node:test';
|
|
6
6
|
import assert from 'node:assert/strict';
|
|
7
7
|
|
|
8
|
-
import { renderPlanContent, renderRoadmapContent } from '../workflow-projections.ts';
|
|
8
|
+
import { renderPlanContent, renderRoadmapContent, renderSummaryContent } from '../workflow-projections.ts';
|
|
9
9
|
import type { SliceRow, TaskRow } from '../gsd-db.ts';
|
|
10
10
|
|
|
11
11
|
// ─── Helpers ─────────────────────────────────────────────────────────────
|
|
@@ -172,3 +172,98 @@ test('renderRoadmapContent: slice with status "pending" shows ⬜', () => {
|
|
|
172
172
|
|
|
173
173
|
assert.ok(content.includes('⬜'), 'pending slice should show ⬜');
|
|
174
174
|
});
|
|
175
|
+
|
|
176
|
+
// ─── renderSummaryContent: double-frontmatter regression ─────────────────
|
|
177
|
+
|
|
178
|
+
test('renderSummaryContent: uses full_summary_md as-is when it contains frontmatter', () => {
|
|
179
|
+
const existingSummary = [
|
|
180
|
+
'---',
|
|
181
|
+
'id: T01',
|
|
182
|
+
'parent: S01',
|
|
183
|
+
'milestone: M001',
|
|
184
|
+
'key_files:',
|
|
185
|
+
' - src/thing.ts',
|
|
186
|
+
'verification_result: passed',
|
|
187
|
+
'completed_at: 2026-01-01T00:00:00Z',
|
|
188
|
+
'blocker_discovered: false',
|
|
189
|
+
'---',
|
|
190
|
+
'',
|
|
191
|
+
'# T01: Did the thing',
|
|
192
|
+
'',
|
|
193
|
+
'**One-liner summary**',
|
|
194
|
+
'',
|
|
195
|
+
'## What Happened',
|
|
196
|
+
'',
|
|
197
|
+
'Narrative content here.',
|
|
198
|
+
'',
|
|
199
|
+
'## Deviations',
|
|
200
|
+
'',
|
|
201
|
+
'None.',
|
|
202
|
+
'',
|
|
203
|
+
].join('\n');
|
|
204
|
+
|
|
205
|
+
const task = makeTaskRow({
|
|
206
|
+
id: 'T01',
|
|
207
|
+
status: 'complete',
|
|
208
|
+
title: 'Did the thing',
|
|
209
|
+
one_liner: 'One-liner summary',
|
|
210
|
+
narrative: 'Narrative content here.',
|
|
211
|
+
full_summary_md: existingSummary,
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
const result = renderSummaryContent(task, 'S01', 'M001');
|
|
215
|
+
|
|
216
|
+
// Must NOT produce double frontmatter
|
|
217
|
+
const frontmatterCount = (result.match(/^---$/gm) || []).length;
|
|
218
|
+
assert.equal(frontmatterCount, 2, `Expected exactly 2 frontmatter delimiters (one block), got ${frontmatterCount}`);
|
|
219
|
+
|
|
220
|
+
// Must NOT produce double H1 heading
|
|
221
|
+
const h1Count = (result.match(/^# T01:/gm) || []).length;
|
|
222
|
+
assert.equal(h1Count, 1, `Expected exactly 1 H1 heading, got ${h1Count}`);
|
|
223
|
+
|
|
224
|
+
// Content should match the full_summary_md exactly
|
|
225
|
+
assert.equal(result, existingSummary);
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
test('renderSummaryContent: synthesizes from DB columns when full_summary_md is empty', () => {
|
|
229
|
+
const task = makeTaskRow({
|
|
230
|
+
id: 'T01',
|
|
231
|
+
status: 'complete',
|
|
232
|
+
title: 'Did the thing',
|
|
233
|
+
one_liner: 'One-liner summary',
|
|
234
|
+
narrative: 'Built the feature.',
|
|
235
|
+
full_summary_md: '',
|
|
236
|
+
deviations: 'Deviated slightly.',
|
|
237
|
+
known_issues: 'None.',
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
const result = renderSummaryContent(task, 'S01', 'M001');
|
|
241
|
+
|
|
242
|
+
// Should have exactly one frontmatter block
|
|
243
|
+
const frontmatterCount = (result.match(/^---$/gm) || []).length;
|
|
244
|
+
assert.equal(frontmatterCount, 2, 'Should have one frontmatter block (2 delimiters)');
|
|
245
|
+
|
|
246
|
+
// Should contain synthesized sections
|
|
247
|
+
assert.ok(result.includes('## What Happened'), 'Should have What Happened section');
|
|
248
|
+
assert.ok(result.includes('Built the feature.'), 'Should use narrative for content');
|
|
249
|
+
assert.ok(result.includes('## Deviations'), 'Should have Deviations section');
|
|
250
|
+
assert.ok(result.includes('Deviated slightly.'), 'Should include deviation text');
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
test('renderSummaryContent: synthesizes when full_summary_md has no frontmatter', () => {
|
|
254
|
+
const task = makeTaskRow({
|
|
255
|
+
id: 'T02',
|
|
256
|
+
status: 'complete',
|
|
257
|
+
title: 'Partial summary',
|
|
258
|
+
narrative: 'Did some work.',
|
|
259
|
+
full_summary_md: 'Just a plain text summary with no frontmatter.',
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
const result = renderSummaryContent(task, 'S01', 'M001');
|
|
263
|
+
|
|
264
|
+
// Should synthesize with proper frontmatter since the stored md lacks it
|
|
265
|
+
assert.ok(result.startsWith('---'), 'Should start with frontmatter');
|
|
266
|
+
assert.ok(result.includes('id: T02'), 'Should have task ID in frontmatter');
|
|
267
|
+
assert.ok(result.includes('## What Happened'), 'Should have What Happened section');
|
|
268
|
+
assert.ok(result.includes('Did some work.'), 'Should use narrative');
|
|
269
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
|
|
4
|
+
import { loadPrompt } from "../prompt-loader.ts";
|
|
5
|
+
|
|
6
|
+
test("loadPrompt normalizes workingDirectory backslashes for bash-friendly prompts (#4048)", () => {
|
|
7
|
+
const prompt = loadPrompt("research-milestone", {
|
|
8
|
+
milestoneId: "M001",
|
|
9
|
+
milestoneTitle: "Windows path fix",
|
|
10
|
+
workingDirectory: "C:\\Dev\\NB\\TR",
|
|
11
|
+
inlinedContext: "context",
|
|
12
|
+
skillActivation: "skill activation",
|
|
13
|
+
skillDiscoveryMode: "off",
|
|
14
|
+
skillDiscoveryInstructions: " disabled",
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
assert.match(prompt, /Your working directory is `C:\/Dev\/NB\/TR`/);
|
|
18
|
+
assert.doesNotMatch(prompt, /C:\\Dev\\NB\\TR/);
|
|
19
|
+
});
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prompt-system gate coverage tests.
|
|
3
|
+
*
|
|
4
|
+
* These tests pin the invariants the plan file documents:
|
|
5
|
+
* 1. Every pending slice-scoped gate is routed to exactly one owner turn.
|
|
6
|
+
* Q8 (owned by complete-slice) MUST NOT leak into gate-evaluate and
|
|
7
|
+
* get silently dropped the way it used to before the registry landed.
|
|
8
|
+
* 2. getPendingGatesForTurn filters by the registry's owner turn, not
|
|
9
|
+
* just the DB scope column.
|
|
10
|
+
* 3. Output validators recognize artifacts that contain the required
|
|
11
|
+
* gate section headings, and flag ones that don't.
|
|
12
|
+
* 4. Prompt output produced by the validators reflects MV01-MV04.
|
|
13
|
+
*
|
|
14
|
+
* They also assert the VALIDATION.md renderer still produces headings
|
|
15
|
+
* matching the registry's promptSection strings, so future renderer
|
|
16
|
+
* edits that drift from the registry fail the suite loudly.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { describe, test, beforeEach, afterEach } from "node:test";
|
|
20
|
+
import assert from "node:assert/strict";
|
|
21
|
+
import { mkdtempSync, rmSync } from "node:fs";
|
|
22
|
+
import { join } from "node:path";
|
|
23
|
+
import { tmpdir } from "node:os";
|
|
24
|
+
|
|
25
|
+
import {
|
|
26
|
+
openDatabase,
|
|
27
|
+
closeDatabase,
|
|
28
|
+
insertMilestone,
|
|
29
|
+
insertSlice,
|
|
30
|
+
insertTask,
|
|
31
|
+
insertGateRow,
|
|
32
|
+
getPendingGates,
|
|
33
|
+
getPendingGatesForTurn,
|
|
34
|
+
} from "../gsd-db.ts";
|
|
35
|
+
import {
|
|
36
|
+
GATE_REGISTRY,
|
|
37
|
+
getGatesForTurn,
|
|
38
|
+
type OwnerTurn,
|
|
39
|
+
} from "../gate-registry.ts";
|
|
40
|
+
import {
|
|
41
|
+
validateSliceSummaryOutput,
|
|
42
|
+
validateTaskSummaryOutput,
|
|
43
|
+
validateMilestoneValidationOutput,
|
|
44
|
+
validateGateSections,
|
|
45
|
+
} from "../prompt-validation.ts";
|
|
46
|
+
|
|
47
|
+
function setupTestDb(): string {
|
|
48
|
+
const tmpDir = mkdtempSync(join(tmpdir(), "prompt-gate-coverage-"));
|
|
49
|
+
const dbPath = join(tmpDir, "gsd.db");
|
|
50
|
+
openDatabase(dbPath);
|
|
51
|
+
insertMilestone({ id: "M001", title: "Test", status: "active" });
|
|
52
|
+
insertSlice({
|
|
53
|
+
milestoneId: "M001",
|
|
54
|
+
id: "S01",
|
|
55
|
+
title: "Test Slice",
|
|
56
|
+
status: "pending",
|
|
57
|
+
risk: "medium",
|
|
58
|
+
depends: [],
|
|
59
|
+
});
|
|
60
|
+
insertTask({
|
|
61
|
+
id: "T01",
|
|
62
|
+
sliceId: "S01",
|
|
63
|
+
milestoneId: "M001",
|
|
64
|
+
title: "Test Task",
|
|
65
|
+
status: "pending",
|
|
66
|
+
});
|
|
67
|
+
return tmpDir;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
describe("getPendingGatesForTurn routes by owner turn, not scope column", () => {
|
|
71
|
+
let tmpDir: string;
|
|
72
|
+
beforeEach(() => {
|
|
73
|
+
tmpDir = setupTestDb();
|
|
74
|
+
});
|
|
75
|
+
afterEach(() => {
|
|
76
|
+
closeDatabase();
|
|
77
|
+
rmSync(tmpDir, { recursive: true, force: true });
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test("Q8 stored as scope:'slice' is owned by complete-slice, not gate-evaluate", () => {
|
|
81
|
+
// Seed the three slice-scoped gates plan-slice writes today.
|
|
82
|
+
insertGateRow({ milestoneId: "M001", sliceId: "S01", gateId: "Q3", scope: "slice" });
|
|
83
|
+
insertGateRow({ milestoneId: "M001", sliceId: "S01", gateId: "Q4", scope: "slice" });
|
|
84
|
+
insertGateRow({ milestoneId: "M001", sliceId: "S01", gateId: "Q8", scope: "slice" });
|
|
85
|
+
|
|
86
|
+
// getPendingGates(..., "slice") returns all three (unchanged).
|
|
87
|
+
const allSlicePending = getPendingGates("M001", "S01", "slice");
|
|
88
|
+
assert.equal(allSlicePending.length, 3);
|
|
89
|
+
|
|
90
|
+
// But the turn-aware helper routes them correctly.
|
|
91
|
+
const gateEval = getPendingGatesForTurn("M001", "S01", "gate-evaluate");
|
|
92
|
+
assert.deepEqual(gateEval.map((g) => g.gate_id).sort(), ["Q3", "Q4"]);
|
|
93
|
+
|
|
94
|
+
const completeSlice = getPendingGatesForTurn("M001", "S01", "complete-slice");
|
|
95
|
+
assert.deepEqual(completeSlice.map((g) => g.gate_id), ["Q8"]);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test("task-scoped gates are scoped to the requested task id", () => {
|
|
99
|
+
insertGateRow({ milestoneId: "M001", sliceId: "S01", gateId: "Q5", scope: "task", taskId: "T01" });
|
|
100
|
+
insertGateRow({ milestoneId: "M001", sliceId: "S01", gateId: "Q6", scope: "task", taskId: "T01" });
|
|
101
|
+
insertGateRow({ milestoneId: "M001", sliceId: "S01", gateId: "Q5", scope: "task", taskId: "T02" });
|
|
102
|
+
|
|
103
|
+
const t1 = getPendingGatesForTurn("M001", "S01", "execute-task", "T01");
|
|
104
|
+
assert.equal(t1.length, 2);
|
|
105
|
+
assert.ok(t1.every((g) => g.gate_id === "Q5" || g.gate_id === "Q6"));
|
|
106
|
+
|
|
107
|
+
const t2 = getPendingGatesForTurn("M001", "S01", "execute-task", "T02");
|
|
108
|
+
assert.equal(t2.length, 1);
|
|
109
|
+
assert.equal(t2[0].gate_id, "Q5");
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
describe("per-turn output validators", () => {
|
|
114
|
+
test("validateSliceSummaryOutput flags missing Operational Readiness", () => {
|
|
115
|
+
const md = `# S01: Test Slice\n\n## What Happened\nstuff\n\n## Verification\nstuff\n`;
|
|
116
|
+
const result = validateSliceSummaryOutput(md);
|
|
117
|
+
assert.equal(result.valid, false);
|
|
118
|
+
assert.ok(result.missing.some((m) => m.includes("Q8")));
|
|
119
|
+
assert.ok(result.missing.some((m) => m.includes("Operational Readiness")));
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
test("validateSliceSummaryOutput passes when Operational Readiness heading is present", () => {
|
|
123
|
+
const md = `# S01\n\n## Operational Readiness\n- Health: /health\n- Failure: alert\n`;
|
|
124
|
+
const result = validateSliceSummaryOutput(md);
|
|
125
|
+
assert.equal(result.valid, true);
|
|
126
|
+
assert.equal(result.missing.length, 0);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
test("validateMilestoneValidationOutput requires all four MV headings", () => {
|
|
130
|
+
// Missing Requirement Coverage.
|
|
131
|
+
const md = [
|
|
132
|
+
"# Milestone Validation: M001",
|
|
133
|
+
"## Success Criteria Checklist",
|
|
134
|
+
"ok",
|
|
135
|
+
"## Slice Delivery Audit",
|
|
136
|
+
"ok",
|
|
137
|
+
"## Cross-Slice Integration",
|
|
138
|
+
"ok",
|
|
139
|
+
].join("\n\n");
|
|
140
|
+
const result = validateMilestoneValidationOutput(md);
|
|
141
|
+
assert.equal(result.valid, false);
|
|
142
|
+
assert.ok(result.missing.some((m) => m.includes("MV04")));
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
test("validateMilestoneValidationOutput passes for a complete VALIDATION.md", () => {
|
|
146
|
+
const md = [
|
|
147
|
+
"# Milestone Validation: M001",
|
|
148
|
+
"## Success Criteria Checklist",
|
|
149
|
+
"ok",
|
|
150
|
+
"## Slice Delivery Audit",
|
|
151
|
+
"ok",
|
|
152
|
+
"## Cross-Slice Integration",
|
|
153
|
+
"ok",
|
|
154
|
+
"## Requirement Coverage",
|
|
155
|
+
"ok",
|
|
156
|
+
].join("\n\n");
|
|
157
|
+
const result = validateMilestoneValidationOutput(md);
|
|
158
|
+
assert.equal(result.valid, true, `unexpected missing: ${result.missing.join(", ")}`);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
test("validateTaskSummaryOutput flags missing task-gate sections", () => {
|
|
162
|
+
const md = `# T01\n\n## What Happened\nstuff\n\n## Verification\nstuff\n`;
|
|
163
|
+
const result = validateTaskSummaryOutput(md);
|
|
164
|
+
assert.equal(result.valid, false);
|
|
165
|
+
const idsInMissing = result.missing.join(" ");
|
|
166
|
+
assert.ok(idsInMissing.includes("Q5"));
|
|
167
|
+
assert.ok(idsInMissing.includes("Q6"));
|
|
168
|
+
assert.ok(idsInMissing.includes("Q7"));
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
test("validateGateSections returns empty missing when gate bucket is empty", () => {
|
|
172
|
+
// Build a phoney owner turn that owns nothing (simulate by validating
|
|
173
|
+
// against a real turn against an artifact containing every section).
|
|
174
|
+
const fullMd = getGatesForTurn("validate-milestone")
|
|
175
|
+
.map((g) => `## ${g.promptSection}\n\nstuff`)
|
|
176
|
+
.join("\n\n");
|
|
177
|
+
const result = validateGateSections(fullMd, "validate-milestone");
|
|
178
|
+
assert.equal(result.valid, true);
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
describe("registry / renderer parity", () => {
|
|
183
|
+
test("MV promptSections match the validate-milestone renderer H2 headings", () => {
|
|
184
|
+
// Mirror the string literals from tools/validate-milestone.ts
|
|
185
|
+
// renderValidationMarkdown() so a rename there flips this test red.
|
|
186
|
+
const expectedHeadings = [
|
|
187
|
+
"Success Criteria Checklist",
|
|
188
|
+
"Slice Delivery Audit",
|
|
189
|
+
"Cross-Slice Integration",
|
|
190
|
+
"Requirement Coverage",
|
|
191
|
+
];
|
|
192
|
+
const registryHeadings = getGatesForTurn("validate-milestone").map((g) => g.promptSection);
|
|
193
|
+
assert.deepEqual(registryHeadings.sort(), [...expectedHeadings].sort());
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
test("Q8 promptSection matches the complete-slice renderer H2 heading", () => {
|
|
197
|
+
// Mirror the slice-summary H2 introduced in tools/complete-slice.ts.
|
|
198
|
+
assert.equal(GATE_REGISTRY.Q8.promptSection, "Operational Readiness");
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
test("registry owner turns cover every turn gate-registry.ts declares", () => {
|
|
202
|
+
const ownerTurns = new Set<OwnerTurn>(Object.values(GATE_REGISTRY).map((g) => g.ownerTurn));
|
|
203
|
+
assert.ok(ownerTurns.has("gate-evaluate"));
|
|
204
|
+
assert.ok(ownerTurns.has("execute-task"));
|
|
205
|
+
assert.ok(ownerTurns.has("complete-slice"));
|
|
206
|
+
assert.ok(ownerTurns.has("validate-milestone"));
|
|
207
|
+
});
|
|
208
|
+
});
|
|
@@ -32,6 +32,15 @@ test("classifyError detects rate limit from message", () => {
|
|
|
32
32
|
assert.equal(result.kind, "rate-limit");
|
|
33
33
|
});
|
|
34
34
|
|
|
35
|
+
test("classifyError treats OpenRouter affordability errors as transient rate-limit class", () => {
|
|
36
|
+
const result = classifyError(
|
|
37
|
+
"402 This request requires more credits, or fewer max_tokens. You requested up to 32000 tokens, but can only afford 329.",
|
|
38
|
+
);
|
|
39
|
+
assert.ok(isTransient(result));
|
|
40
|
+
assert.equal(result.kind, "rate-limit");
|
|
41
|
+
assert.ok("retryAfterMs" in result && result.retryAfterMs > 0);
|
|
42
|
+
});
|
|
43
|
+
|
|
35
44
|
test("classifyError extracts reset delay from message", () => {
|
|
36
45
|
const result = classifyError("rate limit exceeded, reset in 45s");
|
|
37
46
|
assert.equal(result.kind, "rate-limit");
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { mkdirSync, rmSync } from "node:fs";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { tmpdir } from "node:os";
|
|
6
|
+
|
|
7
|
+
import { registerHooks } from "../bootstrap/register-hooks.ts";
|
|
8
|
+
import {
|
|
9
|
+
getPendingGate,
|
|
10
|
+
resetWriteGateState,
|
|
11
|
+
shouldBlockContextArtifactSave,
|
|
12
|
+
} from "../bootstrap/write-gate.ts";
|
|
13
|
+
|
|
14
|
+
function makeTempDir(prefix: string): string {
|
|
15
|
+
const dir = join(
|
|
16
|
+
tmpdir(),
|
|
17
|
+
`gsd-depth-gate-${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
|
|
18
|
+
);
|
|
19
|
+
mkdirSync(dir, { recursive: true });
|
|
20
|
+
return dir;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
test("register-hooks unlocks milestone depth verification from question id without guided-flow state (#4047)", async (t) => {
|
|
24
|
+
const dir = makeTempDir("manual");
|
|
25
|
+
const originalCwd = process.cwd();
|
|
26
|
+
process.chdir(dir);
|
|
27
|
+
resetWriteGateState();
|
|
28
|
+
|
|
29
|
+
t.after(() => {
|
|
30
|
+
resetWriteGateState();
|
|
31
|
+
process.chdir(originalCwd);
|
|
32
|
+
rmSync(dir, { recursive: true, force: true });
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const handlers = new Map<string, Array<(event: any, ctx?: any) => Promise<void> | void>>();
|
|
36
|
+
const pi = {
|
|
37
|
+
on(event: string, handler: (event: any, ctx?: any) => Promise<void> | void) {
|
|
38
|
+
const existing = handlers.get(event) ?? [];
|
|
39
|
+
existing.push(handler);
|
|
40
|
+
handlers.set(event, existing);
|
|
41
|
+
},
|
|
42
|
+
} as any;
|
|
43
|
+
|
|
44
|
+
registerHooks(pi);
|
|
45
|
+
|
|
46
|
+
const questionId = "depth_verification_M001_confirm";
|
|
47
|
+
const questions = [
|
|
48
|
+
{
|
|
49
|
+
id: questionId,
|
|
50
|
+
question: "Do you agree?",
|
|
51
|
+
options: [
|
|
52
|
+
{ label: "Yes, you got it (Recommended)" },
|
|
53
|
+
{ label: "Needs adjustment" },
|
|
54
|
+
],
|
|
55
|
+
},
|
|
56
|
+
];
|
|
57
|
+
|
|
58
|
+
const toolCallHandlers = handlers.get("tool_call");
|
|
59
|
+
const toolResultHandlers = handlers.get("tool_result");
|
|
60
|
+
assert.ok(toolCallHandlers?.length, "tool_call handler should be registered");
|
|
61
|
+
assert.ok(toolResultHandlers?.length, "tool_result handler should be registered");
|
|
62
|
+
|
|
63
|
+
for (const handler of toolCallHandlers ?? []) {
|
|
64
|
+
await handler({
|
|
65
|
+
toolName: "ask_user_questions",
|
|
66
|
+
input: { questions },
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
assert.equal(getPendingGate(), questionId, "gate should be set even without guided-flow state");
|
|
71
|
+
assert.equal(
|
|
72
|
+
shouldBlockContextArtifactSave("CONTEXT", "M001").block,
|
|
73
|
+
true,
|
|
74
|
+
"milestone context should still be blocked before confirmation",
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
for (const handler of toolResultHandlers ?? []) {
|
|
78
|
+
await handler({
|
|
79
|
+
toolName: "ask_user_questions",
|
|
80
|
+
input: { questions },
|
|
81
|
+
details: {
|
|
82
|
+
response: {
|
|
83
|
+
answers: {
|
|
84
|
+
[questionId]: { selected: "Yes, you got it (Recommended)" },
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
assert.equal(getPendingGate(), null, "confirming the depth question should clear the pending gate");
|
|
92
|
+
assert.equal(
|
|
93
|
+
shouldBlockContextArtifactSave("CONTEXT", "M001").block,
|
|
94
|
+
false,
|
|
95
|
+
"question-id milestone inference should unlock the matching milestone context write",
|
|
96
|
+
);
|
|
97
|
+
});
|
|
@@ -69,14 +69,15 @@ test("dashboard shortcut resolves the project root instead of the current worktr
|
|
|
69
69
|
|
|
70
70
|
assert.ok(customCalls > 0, "shortcut opens the dashboard overlay when project root is resolved");
|
|
71
71
|
assert.equal(notices.length, 0, "shortcut does not fall back to the missing-.gsd warning");
|
|
72
|
-
assert.equal(shortcuts.length,
|
|
72
|
+
assert.equal(shortcuts.length, 5, "all GSD shortcuts are still registered");
|
|
73
73
|
const keys = shortcuts.map((shortcut) => shortcut.key);
|
|
74
74
|
assert.ok(keys.includes("ctrl+alt+g"), "primary dashboard shortcut is registered");
|
|
75
75
|
assert.ok(keys.includes("ctrl+shift+g"), "fallback dashboard shortcut is registered");
|
|
76
76
|
assert.ok(keys.includes("ctrl+alt+n"), "primary notifications shortcut is registered");
|
|
77
77
|
assert.ok(keys.includes("ctrl+shift+n"), "fallback notifications shortcut is registered");
|
|
78
78
|
assert.ok(keys.includes("ctrl+alt+p"), "primary parallel shortcut is registered");
|
|
79
|
-
|
|
79
|
+
// No Ctrl+Shift+P fallback — conflicts with cycleModelBackward (shift+ctrl+p)
|
|
80
|
+
assert.ok(!keys.includes("ctrl+shift+p"), "parallel fallback must not be registered (conflicts with cycleModelBackward)");
|
|
80
81
|
});
|
|
81
82
|
|
|
82
83
|
test("parallel shortcut passes resolved project root into overlay", async (t) => {
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* stale-slice-rows.test.ts — #3658
|
|
3
|
+
*
|
|
4
|
+
* Verify that state.ts contains slice-level status reconciliation that
|
|
5
|
+
* updates stale DB rows (status "pending") when disk artifacts (SUMMARY)
|
|
6
|
+
* prove the slice is complete. Without this, the dependency resolver builds
|
|
7
|
+
* doneSliceIds from stale DB rows and downstream slices stay blocked.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { describe, test } from "node:test";
|
|
11
|
+
import assert from "node:assert/strict";
|
|
12
|
+
import { readFileSync } from "node:fs";
|
|
13
|
+
import { join, dirname } from "node:path";
|
|
14
|
+
import { fileURLToPath } from "node:url";
|
|
15
|
+
|
|
16
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
17
|
+
const sourceFile = join(__dirname, "..", "state.ts");
|
|
18
|
+
|
|
19
|
+
describe("stale slice row reconciliation (#3658)", () => {
|
|
20
|
+
const source = readFileSync(sourceFile, "utf-8");
|
|
21
|
+
|
|
22
|
+
test("imports updateSliceStatus from gsd-db", () => {
|
|
23
|
+
assert.match(source, /import\s*\{[^}]*updateSliceStatus[^}]*\}\s*from/);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test("checks isStatusDone before reconciling slice rows", () => {
|
|
27
|
+
assert.match(source, /isStatusDone\(dbSlice\.status\)/);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test("resolves SUMMARY file to detect completed slices on disk", () => {
|
|
31
|
+
assert.match(source, /resolveSliceFile\(basePath,\s*mid,\s*dbSlice\.id,\s*["']SUMMARY["']\)/);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test("calls updateSliceStatus to reconcile stale rows", () => {
|
|
35
|
+
assert.match(source, /updateSliceStatus\(mid,\s*dbSlice\.id,\s*["']complete["']\)/);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test("references issue #3599 in reconciliation comment", () => {
|
|
39
|
+
assert.match(source, /#3599/);
|
|
40
|
+
});
|
|
41
|
+
});
|