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
|
@@ -61,6 +61,9 @@ export function parseDoctorArgs(args) {
|
|
|
61
61
|
const requestedScope = mode === "doctor" ? parts[0] : parts[1];
|
|
62
62
|
return { jsonMode, dryRun, fixFlag, includeBuild, includeTests, mode, requestedScope };
|
|
63
63
|
}
|
|
64
|
+
export function isDoctorHealActionable(issue) {
|
|
65
|
+
return issue.fixable && issue.severity !== "info";
|
|
66
|
+
}
|
|
64
67
|
export async function handleDoctor(args, ctx, pi) {
|
|
65
68
|
const { jsonMode, dryRun, fixFlag, includeBuild, includeTests, mode, requestedScope } = parseDoctorArgs(args);
|
|
66
69
|
const scope = await selectDoctorScope(projectRoot(), requestedScope);
|
|
@@ -88,7 +91,7 @@ export async function handleDoctor(args, ctx, pi) {
|
|
|
88
91
|
scope: effectiveScope,
|
|
89
92
|
includeWarnings: true,
|
|
90
93
|
});
|
|
91
|
-
const actionable = unresolved.filter(
|
|
94
|
+
const actionable = unresolved.filter(isDoctorHealActionable);
|
|
92
95
|
if (actionable.length === 0) {
|
|
93
96
|
ctx.ui.notify("Doctor heal found nothing actionable to hand off to the LLM.", "info");
|
|
94
97
|
return;
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
*/
|
|
15
15
|
import { readFileSync, existsSync } from "node:fs";
|
|
16
16
|
import { resolve, sep } from "node:path";
|
|
17
|
-
import { readFrozenDefinition } from "./
|
|
17
|
+
import { readFrozenDefinition } from "./definition-io.js";
|
|
18
18
|
/** Maximum characters per artifact to prevent context window blowout. */
|
|
19
19
|
const MAX_CONTEXT_CHARS = 10_000;
|
|
20
20
|
/**
|
|
@@ -13,17 +13,13 @@
|
|
|
13
13
|
*/
|
|
14
14
|
import { readFileSync } from "node:fs";
|
|
15
15
|
import { join } from "node:path";
|
|
16
|
-
import { parse } from "yaml";
|
|
17
16
|
import { readGraph, writeGraph, getNextPendingStep, markStepComplete, expandIteration, } from "./graph.js";
|
|
18
17
|
import { injectContext } from "./context-injector.js";
|
|
18
|
+
import { readFrozenDefinition } from "./definition-io.js";
|
|
19
19
|
import { parseUnitId } from "./unit-id.js";
|
|
20
20
|
import { withFileLock } from "./file-lock.js";
|
|
21
|
-
|
|
22
|
-
export
|
|
23
|
-
const defPath = join(runDir, "DEFINITION.yaml");
|
|
24
|
-
const raw = readFileSync(defPath, "utf-8");
|
|
25
|
-
return parse(raw, { schema: "core" });
|
|
26
|
-
}
|
|
21
|
+
// Re-export for downstream consumers
|
|
22
|
+
export { readFrozenDefinition } from "./definition-io.js";
|
|
27
23
|
export class CustomWorkflowEngine {
|
|
28
24
|
engineId = "custom";
|
|
29
25
|
runDir;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* definition-io.ts — Read frozen DEFINITION.yaml from a run directory.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from custom-workflow-engine.ts to break the circular dependency
|
|
5
|
+
* between context-injector.ts and custom-workflow-engine.ts.
|
|
6
|
+
*/
|
|
7
|
+
import { readFileSync } from "node:fs";
|
|
8
|
+
import { join } from "node:path";
|
|
9
|
+
import { parse } from "yaml";
|
|
10
|
+
/** Read and parse the frozen DEFINITION.yaml from a run directory. */
|
|
11
|
+
export function readFrozenDefinition(runDir) {
|
|
12
|
+
const defPath = join(runDir, "DEFINITION.yaml");
|
|
13
|
+
const raw = readFileSync(defPath, "utf-8");
|
|
14
|
+
return parse(raw, { schema: "core" });
|
|
15
|
+
}
|
|
@@ -102,6 +102,10 @@ export function getPriorSliceCompletionBlocker(base, _mainBranch, unitType, unit
|
|
|
102
102
|
}
|
|
103
103
|
}
|
|
104
104
|
else {
|
|
105
|
+
const milestoneUsesExplicitDeps = slices.some((slice) => slice.depends.length > 0);
|
|
106
|
+
if (milestoneUsesExplicitDeps) {
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
105
109
|
// Positional fallback is only a heuristic for legacy slices with no
|
|
106
110
|
// declared dependencies. Skip any earlier slice that depends on the
|
|
107
111
|
// target, directly or transitively, or we can deadlock a valid zero-dep
|
|
@@ -147,10 +147,33 @@ const PROVIDER_ROUTES = {
|
|
|
147
147
|
openai: ["github-copilot", "openai-codex"],
|
|
148
148
|
google: ["google-gemini-cli"],
|
|
149
149
|
};
|
|
150
|
+
/**
|
|
151
|
+
* Providers that use external CLI authentication (not API keys).
|
|
152
|
+
* These are always considered "ok" — the host CLI handles auth.
|
|
153
|
+
*/
|
|
154
|
+
const CLI_AUTH_PROVIDERS = new Set([
|
|
155
|
+
"claude-code",
|
|
156
|
+
"openai-codex",
|
|
157
|
+
"google-gemini-cli",
|
|
158
|
+
"google-antigravity",
|
|
159
|
+
]);
|
|
150
160
|
function checkLlmProviders() {
|
|
151
161
|
const required = collectConfiguredModelProviders();
|
|
152
162
|
const results = [];
|
|
153
163
|
for (const providerId of required) {
|
|
164
|
+
// CLI-authenticated providers don't need API keys — skip key check
|
|
165
|
+
if (CLI_AUTH_PROVIDERS.has(providerId)) {
|
|
166
|
+
const info = PROVIDER_REGISTRY.find(p => p.id === providerId);
|
|
167
|
+
results.push({
|
|
168
|
+
name: providerId,
|
|
169
|
+
label: info?.label ?? providerId,
|
|
170
|
+
category: "llm",
|
|
171
|
+
status: "ok",
|
|
172
|
+
message: `${info?.label ?? providerId} — CLI auth (no key needed)`,
|
|
173
|
+
required: true,
|
|
174
|
+
});
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
154
177
|
const info = PROVIDER_REGISTRY.find(p => p.id === providerId);
|
|
155
178
|
const label = providerId === "anthropic-vertex"
|
|
156
179
|
? "Anthropic Vertex"
|
|
@@ -277,13 +277,16 @@ export async function checkRuntimeHealth(basePath, issues, fixesApplied, shouldF
|
|
|
277
277
|
if (existsSync(gitignorePath) && nativeIsRepo(basePath)) {
|
|
278
278
|
const content = readFileSync(gitignorePath, "utf-8");
|
|
279
279
|
const existingLines = new Set(content.split("\n").map(l => l.trim()).filter(l => l && !l.startsWith("#")));
|
|
280
|
-
// Check for critical runtime patterns that must be present
|
|
280
|
+
// Check for critical runtime patterns that must be present.
|
|
281
|
+
// NOTE: GSD_RUNTIME_PATTERNS in gitignore.ts is the canonical source of truth.
|
|
282
|
+
// This is a minimal subset for the doctor check.
|
|
281
283
|
const criticalPatterns = [
|
|
282
284
|
".gsd/activity/",
|
|
283
285
|
".gsd/runtime/",
|
|
284
286
|
".gsd/auto.lock",
|
|
285
|
-
".gsd/gsd.db",
|
|
286
|
-
".gsd/completed-units
|
|
287
|
+
".gsd/gsd.db*",
|
|
288
|
+
".gsd/completed-units*.json",
|
|
289
|
+
".gsd/event-log.jsonl",
|
|
287
290
|
];
|
|
288
291
|
// If blanket .gsd/ or .gsd is present, all patterns are covered
|
|
289
292
|
const hasBlanketIgnore = existingLines.has(".gsd/") || existingLines.has(".gsd");
|
|
@@ -20,6 +20,9 @@ export function resetRetryState(state) {
|
|
|
20
20
|
// ── Classification ──────────────────────────────────────────────────────────
|
|
21
21
|
const PERMANENT_RE = /auth|unauthorized|forbidden|invalid.*key|invalid.*api|billing|quota exceeded|account/i;
|
|
22
22
|
const RATE_LIMIT_RE = /rate.?limit|too many requests|429/i;
|
|
23
|
+
// OpenRouter affordability-style quota errors should be treated as transient
|
|
24
|
+
// so core retry logic can lower maxTokens and continue in-session.
|
|
25
|
+
const AFFORDABILITY_RE = /requires more credits|can only afford|insufficient credits|not enough credits|fewer max_tokens/i;
|
|
23
26
|
const NETWORK_RE = /network|ECONNRESET|ETIMEDOUT|ECONNREFUSED|socket hang up|fetch failed|connection.*reset|dns/i;
|
|
24
27
|
const SERVER_RE = /internal server error|500|502|503|overloaded|server_error|api_error|service.?unavailable/i;
|
|
25
28
|
// ECONNRESET/ECONNREFUSED are in NETWORK_RE (same-model retry first).
|
|
@@ -42,7 +45,7 @@ const RESET_DELAY_RE = /reset in (\d+)s/i;
|
|
|
42
45
|
*/
|
|
43
46
|
export function classifyError(errorMsg, retryAfterMs) {
|
|
44
47
|
const isPermanent = PERMANENT_RE.test(errorMsg);
|
|
45
|
-
const isRateLimit = RATE_LIMIT_RE.test(errorMsg);
|
|
48
|
+
const isRateLimit = RATE_LIMIT_RE.test(errorMsg) || AFFORDABILITY_RE.test(errorMsg);
|
|
46
49
|
// 1. Permanent — but rate limit takes precedence
|
|
47
50
|
if (isPermanent && !isRateLimit) {
|
|
48
51
|
return { kind: "permanent" };
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GSD Gate Registry — single source of truth for quality-gate ownership.
|
|
3
|
+
*
|
|
4
|
+
* Each gate declares which workflow turn owns it, the scope at which it is
|
|
5
|
+
* persisted in the `quality_gates` table, and the question/guidance text used
|
|
6
|
+
* in the prompt that turn sends. The registry replaces the ad-hoc
|
|
7
|
+
* `GATE_QUESTIONS` table that used to live in `auto-prompts.ts`, and every
|
|
8
|
+
* layer of the prompt system (prompt builders, dispatch rules, state
|
|
9
|
+
* derivation, tool handlers) consults it so a pending gate can never be
|
|
10
|
+
* silently dropped.
|
|
11
|
+
*
|
|
12
|
+
* Design notes:
|
|
13
|
+
* - `GATE_REGISTRY` is exhaustiveness-checked against `GateId` via
|
|
14
|
+
* `satisfies Record<GateId, GateDefinition>`, so adding a new GateId
|
|
15
|
+
* without a registry entry is a compile error.
|
|
16
|
+
* - `getGatesForTurn(turn)` returns the definitions a turn owns.
|
|
17
|
+
* - `assertGateCoverage(pending, turn)` throws a GSDError if the pending
|
|
18
|
+
* list for a turn contains unknown gates, or if any gate owned by the
|
|
19
|
+
* turn is missing from the pending list.
|
|
20
|
+
*/
|
|
21
|
+
import { GSDError, GSD_PARSE_ERROR } from "./errors.js";
|
|
22
|
+
export const GATE_REGISTRY = {
|
|
23
|
+
Q3: {
|
|
24
|
+
id: "Q3",
|
|
25
|
+
scope: "slice",
|
|
26
|
+
ownerTurn: "gate-evaluate",
|
|
27
|
+
question: "How can this be exploited?",
|
|
28
|
+
guidance: [
|
|
29
|
+
"Identify abuse scenarios: parameter tampering, replay attacks, privilege escalation.",
|
|
30
|
+
"Map data exposure risks: PII, tokens, secrets accessible through this slice.",
|
|
31
|
+
"Define input trust boundaries: untrusted user input reaching DB, API, or filesystem.",
|
|
32
|
+
"If none apply, return verdict 'omitted' with rationale explaining why.",
|
|
33
|
+
].join("\n"),
|
|
34
|
+
promptSection: "Abuse Surface",
|
|
35
|
+
},
|
|
36
|
+
Q4: {
|
|
37
|
+
id: "Q4",
|
|
38
|
+
scope: "slice",
|
|
39
|
+
ownerTurn: "gate-evaluate",
|
|
40
|
+
question: "What existing promises does this break?",
|
|
41
|
+
guidance: [
|
|
42
|
+
"List which existing requirements (R001, R003, etc.) are touched by this slice.",
|
|
43
|
+
"Identify what must be re-tested after shipping.",
|
|
44
|
+
"Flag decisions that should be revisited given the new scope.",
|
|
45
|
+
"If no existing requirements are affected, return verdict 'omitted'.",
|
|
46
|
+
].join("\n"),
|
|
47
|
+
promptSection: "Broken Promises",
|
|
48
|
+
},
|
|
49
|
+
Q5: {
|
|
50
|
+
id: "Q5",
|
|
51
|
+
scope: "task",
|
|
52
|
+
ownerTurn: "execute-task",
|
|
53
|
+
question: "What breaks when dependencies fail?",
|
|
54
|
+
guidance: [
|
|
55
|
+
"Enumerate the task's external dependencies (APIs, filesystem, network, subprocesses).",
|
|
56
|
+
"Describe the failure path for each: timeout, malformed response, connection loss.",
|
|
57
|
+
"Verify the implementation handles each failure or explicitly bubbles the error.",
|
|
58
|
+
"Return verdict 'omitted' only if the task has no external dependencies.",
|
|
59
|
+
].join("\n"),
|
|
60
|
+
promptSection: "Failure Modes",
|
|
61
|
+
},
|
|
62
|
+
Q6: {
|
|
63
|
+
id: "Q6",
|
|
64
|
+
scope: "task",
|
|
65
|
+
ownerTurn: "execute-task",
|
|
66
|
+
question: "What is the 10x load breakpoint?",
|
|
67
|
+
guidance: [
|
|
68
|
+
"Identify the resource that saturates first at 10x the expected load.",
|
|
69
|
+
"Describe the protection applied (pool sizing, rate limiting, pagination, caching).",
|
|
70
|
+
"Return verdict 'omitted' if the task has no runtime load dimension.",
|
|
71
|
+
].join("\n"),
|
|
72
|
+
promptSection: "Load Profile",
|
|
73
|
+
},
|
|
74
|
+
Q7: {
|
|
75
|
+
id: "Q7",
|
|
76
|
+
scope: "task",
|
|
77
|
+
ownerTurn: "execute-task",
|
|
78
|
+
question: "What negative tests protect this task?",
|
|
79
|
+
guidance: [
|
|
80
|
+
"List malformed inputs, error paths, and boundary conditions the tests cover.",
|
|
81
|
+
"Point to the specific test files or cases that assert each negative scenario.",
|
|
82
|
+
"Return verdict 'omitted' only if the task has no meaningful negative surface.",
|
|
83
|
+
].join("\n"),
|
|
84
|
+
promptSection: "Negative Tests",
|
|
85
|
+
},
|
|
86
|
+
Q8: {
|
|
87
|
+
id: "Q8",
|
|
88
|
+
scope: "slice",
|
|
89
|
+
ownerTurn: "complete-slice",
|
|
90
|
+
question: "How will ops know this slice is healthy or broken?",
|
|
91
|
+
guidance: [
|
|
92
|
+
"Describe the health signal (metric, log line, dashboard) that proves the slice works.",
|
|
93
|
+
"Describe the failure signal that triggers an alert or paging.",
|
|
94
|
+
"Document the recovery procedure and any monitoring gaps.",
|
|
95
|
+
"Return verdict 'omitted' only for slices with no runtime behavior at all.",
|
|
96
|
+
].join("\n"),
|
|
97
|
+
promptSection: "Operational Readiness",
|
|
98
|
+
},
|
|
99
|
+
MV01: {
|
|
100
|
+
id: "MV01",
|
|
101
|
+
scope: "milestone",
|
|
102
|
+
ownerTurn: "validate-milestone",
|
|
103
|
+
question: "Is every success criterion in the milestone roadmap satisfied?",
|
|
104
|
+
guidance: [
|
|
105
|
+
"Walk the success-criteria checklist from the milestone roadmap.",
|
|
106
|
+
"For each criterion, point to the slice / assessment / verification evidence that proves it.",
|
|
107
|
+
"Return verdict 'flag' if any criterion is unmet or unverifiable.",
|
|
108
|
+
].join("\n"),
|
|
109
|
+
promptSection: "Success Criteria Checklist",
|
|
110
|
+
},
|
|
111
|
+
MV02: {
|
|
112
|
+
id: "MV02",
|
|
113
|
+
scope: "milestone",
|
|
114
|
+
ownerTurn: "validate-milestone",
|
|
115
|
+
question: "Does every slice have a SUMMARY.md and a passing assessment?",
|
|
116
|
+
guidance: [
|
|
117
|
+
"Confirm every slice listed in the roadmap has a SUMMARY.md.",
|
|
118
|
+
"Confirm each slice has an ASSESSMENT verdict of 'pass' (or justified 'omitted').",
|
|
119
|
+
"Flag missing artifacts and slices with outstanding follow-ups or known limitations.",
|
|
120
|
+
].join("\n"),
|
|
121
|
+
promptSection: "Slice Delivery Audit",
|
|
122
|
+
},
|
|
123
|
+
MV03: {
|
|
124
|
+
id: "MV03",
|
|
125
|
+
scope: "milestone",
|
|
126
|
+
ownerTurn: "validate-milestone",
|
|
127
|
+
question: "Do the slices integrate end-to-end?",
|
|
128
|
+
guidance: [
|
|
129
|
+
"Trace at least one cross-slice flow proving the pieces compose.",
|
|
130
|
+
"Flag gaps where two slices were built in isolation with no integration evidence.",
|
|
131
|
+
].join("\n"),
|
|
132
|
+
promptSection: "Cross-Slice Integration",
|
|
133
|
+
},
|
|
134
|
+
MV04: {
|
|
135
|
+
id: "MV04",
|
|
136
|
+
scope: "milestone",
|
|
137
|
+
ownerTurn: "validate-milestone",
|
|
138
|
+
question: "Are all touched requirements covered and still coherent?",
|
|
139
|
+
guidance: [
|
|
140
|
+
"For each requirement advanced, validated, surfaced, or invalidated across the milestone's slices, confirm the milestone-level evidence matches.",
|
|
141
|
+
"Flag requirements that slices claim to advance but no artifact proves.",
|
|
142
|
+
].join("\n"),
|
|
143
|
+
promptSection: "Requirement Coverage",
|
|
144
|
+
},
|
|
145
|
+
};
|
|
146
|
+
/** Stable ordered lists per owner turn — iteration order matches declaration. */
|
|
147
|
+
const ORDERED_GATES = Object.values(GATE_REGISTRY);
|
|
148
|
+
/** Return every gate owned by a turn, in stable declaration order. */
|
|
149
|
+
export function getGatesForTurn(turn) {
|
|
150
|
+
return ORDERED_GATES.filter((g) => g.ownerTurn === turn);
|
|
151
|
+
}
|
|
152
|
+
/** Return the set of gate ids a turn owns. */
|
|
153
|
+
export function getGateIdsForTurn(turn) {
|
|
154
|
+
return new Set(getGatesForTurn(turn).map((g) => g.id));
|
|
155
|
+
}
|
|
156
|
+
/** Look up a definition by gate id, or undefined if unknown. */
|
|
157
|
+
export function getGateDefinition(id) {
|
|
158
|
+
return GATE_REGISTRY[id];
|
|
159
|
+
}
|
|
160
|
+
/** Look up the owner turn for a gate id. Throws if the gate is unknown. */
|
|
161
|
+
export function getOwnerTurn(id) {
|
|
162
|
+
const def = GATE_REGISTRY[id];
|
|
163
|
+
if (!def) {
|
|
164
|
+
throw new GSDError(GSD_PARSE_ERROR, `gate-registry: unknown gate id "${id}"`);
|
|
165
|
+
}
|
|
166
|
+
return def.ownerTurn;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Assert that the pending gate rows for a turn match what the registry says
|
|
170
|
+
* the turn owns. Fails loudly rather than silently skipping.
|
|
171
|
+
*
|
|
172
|
+
* - Every row in `pending` must have a definition whose `ownerTurn` matches `turn`.
|
|
173
|
+
* (The caller is responsible for scoping the pending list — e.g. filtering
|
|
174
|
+
* by slice scope before passing it in.)
|
|
175
|
+
* - `options.requireAll` (default true): every gate the turn owns must appear
|
|
176
|
+
* in `pending`. Set to false for turns like `execute-task` that only need
|
|
177
|
+
* coverage for the subset of gates that were seeded (e.g. tasks with no
|
|
178
|
+
* external dependencies have no Q5 row).
|
|
179
|
+
*/
|
|
180
|
+
export function assertGateCoverage(pending, turn, options = {}) {
|
|
181
|
+
const requireAll = options.requireAll ?? true;
|
|
182
|
+
const expected = getGateIdsForTurn(turn);
|
|
183
|
+
const pendingIds = new Set(pending.map((g) => g.gate_id));
|
|
184
|
+
const unknown = [];
|
|
185
|
+
for (const id of pendingIds) {
|
|
186
|
+
const def = getGateDefinition(id);
|
|
187
|
+
if (!def) {
|
|
188
|
+
unknown.push(id);
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
if (def.ownerTurn !== turn) {
|
|
192
|
+
unknown.push(`${id} (owned by ${def.ownerTurn}, not ${turn})`);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
if (unknown.length > 0) {
|
|
196
|
+
throw new GSDError(GSD_PARSE_ERROR, `assertGateCoverage: turn "${turn}" received pending gates it does not own: ${unknown.join(", ")}`);
|
|
197
|
+
}
|
|
198
|
+
if (requireAll) {
|
|
199
|
+
const missing = [];
|
|
200
|
+
for (const id of expected) {
|
|
201
|
+
if (!pendingIds.has(id))
|
|
202
|
+
missing.push(id);
|
|
203
|
+
}
|
|
204
|
+
if (missing.length > 0) {
|
|
205
|
+
throw new GSDError(GSD_PARSE_ERROR, `assertGateCoverage: turn "${turn}" is missing required gates: ${missing.join(", ")}`);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
@@ -78,22 +78,25 @@ export class MergeConflictError extends GSDError {
|
|
|
78
78
|
/**
|
|
79
79
|
* GSD runtime paths that should be excluded from smart staging.
|
|
80
80
|
* These are transient/generated artifacts that should never be committed.
|
|
81
|
-
*
|
|
82
|
-
*
|
|
81
|
+
*
|
|
82
|
+
* NOTE: GSD_RUNTIME_PATTERNS in gitignore.ts is the canonical source of truth.
|
|
83
|
+
* This array must stay synchronized with it.
|
|
83
84
|
*/
|
|
84
85
|
export const RUNTIME_EXCLUSION_PATHS = [
|
|
85
86
|
".gsd/activity/",
|
|
87
|
+
".gsd/forensics/",
|
|
86
88
|
".gsd/runtime/",
|
|
87
89
|
".gsd/worktrees/",
|
|
90
|
+
".gsd/parallel/",
|
|
88
91
|
".gsd/auto.lock",
|
|
89
92
|
".gsd/metrics.json",
|
|
90
|
-
".gsd/completed-units
|
|
93
|
+
".gsd/completed-units*.json", // covers completed-units.json and archived completed-units-{MID}.json
|
|
94
|
+
".gsd/state-manifest.json",
|
|
91
95
|
".gsd/STATE.md",
|
|
92
|
-
".gsd/gsd.db",
|
|
93
|
-
".gsd/
|
|
94
|
-
".gsd/
|
|
95
|
-
".gsd/
|
|
96
|
-
".gsd/doctor-history.jsonl", // doctor run history (#2296)
|
|
96
|
+
".gsd/gsd.db*",
|
|
97
|
+
".gsd/journal/",
|
|
98
|
+
".gsd/doctor-history.jsonl",
|
|
99
|
+
".gsd/event-log.jsonl",
|
|
97
100
|
".gsd/DISCUSSION-MANIFEST.json",
|
|
98
101
|
];
|
|
99
102
|
// ─── Integration Branch Metadata ───────────────────────────────────────────
|
|
@@ -13,6 +13,12 @@ import { gsdRoot } from "./paths.js";
|
|
|
13
13
|
import { GIT_NO_PROMPT_ENV } from "./git-constants.js";
|
|
14
14
|
/**
|
|
15
15
|
* GSD runtime patterns for git index cleanup.
|
|
16
|
+
*
|
|
17
|
+
* CANONICAL SOURCE OF TRUTH: This array is the authoritative list of runtime
|
|
18
|
+
* ignore patterns. Other modules (RUNTIME_EXCLUSION_PATHS in git-service.ts,
|
|
19
|
+
* SKIP_* arrays in worktree-manager.ts, criticalPatterns in doctor-runtime-checks.ts)
|
|
20
|
+
* must stay synchronized with this list.
|
|
21
|
+
*
|
|
16
22
|
* With external state (symlink), these are a no-op in most cases,
|
|
17
23
|
* but retained for backwards compatibility during migration.
|
|
18
24
|
*/
|
|
@@ -24,13 +30,13 @@ const GSD_RUNTIME_PATTERNS = [
|
|
|
24
30
|
".gsd/parallel/",
|
|
25
31
|
".gsd/auto.lock",
|
|
26
32
|
".gsd/metrics.json",
|
|
27
|
-
".gsd/completed-units
|
|
33
|
+
".gsd/completed-units*.json", // covers completed-units.json and archived completed-units-{MID}.json
|
|
34
|
+
".gsd/state-manifest.json",
|
|
28
35
|
".gsd/STATE.md",
|
|
29
|
-
".gsd/gsd.db",
|
|
30
|
-
".gsd/
|
|
31
|
-
".gsd/
|
|
32
|
-
".gsd/
|
|
33
|
-
".gsd/doctor-history.jsonl", // doctor run history (#2296)
|
|
36
|
+
".gsd/gsd.db*",
|
|
37
|
+
".gsd/journal/",
|
|
38
|
+
".gsd/doctor-history.jsonl",
|
|
39
|
+
".gsd/event-log.jsonl",
|
|
34
40
|
".gsd/DISCUSSION-MANIFEST.json",
|
|
35
41
|
".gsd/milestones/**/*-CONTINUE.md",
|
|
36
42
|
".gsd/milestones/**/continue.md",
|
|
@@ -8,6 +8,7 @@ import { createRequire } from "node:module";
|
|
|
8
8
|
import { existsSync, copyFileSync, mkdirSync, realpathSync } from "node:fs";
|
|
9
9
|
import { dirname } from "node:path";
|
|
10
10
|
import { GSDError, GSD_STALE_STATE } from "./errors.js";
|
|
11
|
+
import { getGateIdsForTurn } from "./gate-registry.js";
|
|
11
12
|
import { logError, logWarning } from "./workflow-logger.js";
|
|
12
13
|
const _require = createRequire(import.meta.url);
|
|
13
14
|
let providerName = null;
|
|
@@ -120,6 +121,25 @@ function openRawDb(path) {
|
|
|
120
121
|
return new Database(path);
|
|
121
122
|
}
|
|
122
123
|
const SCHEMA_VERSION = 14;
|
|
124
|
+
function indexExists(db, name) {
|
|
125
|
+
return !!db.prepare("SELECT 1 as present FROM sqlite_master WHERE type = 'index' AND name = ?").get(name);
|
|
126
|
+
}
|
|
127
|
+
function dedupeVerificationEvidenceRows(db) {
|
|
128
|
+
db.exec(`
|
|
129
|
+
DELETE FROM verification_evidence
|
|
130
|
+
WHERE rowid NOT IN (
|
|
131
|
+
SELECT MIN(rowid)
|
|
132
|
+
FROM verification_evidence
|
|
133
|
+
GROUP BY task_id, slice_id, milestone_id, command, verdict
|
|
134
|
+
)
|
|
135
|
+
`);
|
|
136
|
+
}
|
|
137
|
+
function ensureVerificationEvidenceDedupIndex(db) {
|
|
138
|
+
if (indexExists(db, "idx_verification_evidence_dedup"))
|
|
139
|
+
return;
|
|
140
|
+
dedupeVerificationEvidenceRows(db);
|
|
141
|
+
db.exec("CREATE UNIQUE INDEX IF NOT EXISTS idx_verification_evidence_dedup ON verification_evidence(task_id, slice_id, milestone_id, command, verdict)");
|
|
142
|
+
}
|
|
123
143
|
function initSchema(db, fileBacked) {
|
|
124
144
|
if (fileBacked)
|
|
125
145
|
db.exec("PRAGMA journal_mode=WAL");
|
|
@@ -131,7 +151,7 @@ function initSchema(db, fileBacked) {
|
|
|
131
151
|
db.exec("PRAGMA auto_vacuum = INCREMENTAL");
|
|
132
152
|
if (fileBacked)
|
|
133
153
|
db.exec("PRAGMA cache_size = -8000"); // 8 MB page cache
|
|
134
|
-
if (fileBacked)
|
|
154
|
+
if (fileBacked && process.platform !== "darwin")
|
|
135
155
|
db.exec("PRAGMA mmap_size = 67108864"); // 64 MB mmap
|
|
136
156
|
db.exec("PRAGMA temp_store = MEMORY");
|
|
137
157
|
db.exec("PRAGMA foreign_keys = ON");
|
|
@@ -357,7 +377,7 @@ function initSchema(db, fileBacked) {
|
|
|
357
377
|
db.exec("CREATE INDEX IF NOT EXISTS idx_milestones_status ON milestones(status)");
|
|
358
378
|
db.exec("CREATE INDEX IF NOT EXISTS idx_quality_gates_pending ON quality_gates(milestone_id, slice_id, status)");
|
|
359
379
|
db.exec("CREATE INDEX IF NOT EXISTS idx_verification_evidence_task ON verification_evidence(milestone_id, slice_id, task_id)");
|
|
360
|
-
db
|
|
380
|
+
ensureVerificationEvidenceDedupIndex(db);
|
|
361
381
|
// v14 index — slice dependency lookups
|
|
362
382
|
db.exec("CREATE INDEX IF NOT EXISTS idx_slice_deps_target ON slice_dependencies(milestone_id, depends_on_slice_id)");
|
|
363
383
|
db.exec(`CREATE VIEW IF NOT EXISTS active_decisions AS SELECT * FROM decisions WHERE superseded_by IS NULL`);
|
|
@@ -667,7 +687,7 @@ function migrateSchema(db) {
|
|
|
667
687
|
db.exec("CREATE INDEX IF NOT EXISTS idx_milestones_status ON milestones(status)");
|
|
668
688
|
db.exec("CREATE INDEX IF NOT EXISTS idx_quality_gates_pending ON quality_gates(milestone_id, slice_id, status)");
|
|
669
689
|
db.exec("CREATE INDEX IF NOT EXISTS idx_verification_evidence_task ON verification_evidence(milestone_id, slice_id, task_id)");
|
|
670
|
-
db
|
|
690
|
+
ensureVerificationEvidenceDedupIndex(db);
|
|
671
691
|
db.prepare("INSERT INTO schema_version (version, applied_at) VALUES (:version, :applied_at)").run({
|
|
672
692
|
":version": 13,
|
|
673
693
|
":applied_at": new Date().toISOString(),
|
|
@@ -799,6 +819,7 @@ export function closeDatabase() {
|
|
|
799
819
|
currentDb = null;
|
|
800
820
|
currentPath = null;
|
|
801
821
|
currentPid = 0;
|
|
822
|
+
_dbOpenAttempted = false;
|
|
802
823
|
}
|
|
803
824
|
}
|
|
804
825
|
/** Run a full VACUUM — call sparingly (e.g. after milestone completion). */
|
|
@@ -1332,6 +1353,29 @@ export function setSliceSummaryMd(milestoneId, sliceId, summaryMd, uatMd) {
|
|
|
1332
1353
|
currentDb.prepare(`UPDATE slices SET full_summary_md = :summary_md, full_uat_md = :uat_md WHERE milestone_id = :mid AND id = :sid`).run({ ":mid": milestoneId, ":sid": sliceId, ":summary_md": summaryMd, ":uat_md": uatMd });
|
|
1333
1354
|
}
|
|
1334
1355
|
function rowToTask(row) {
|
|
1356
|
+
const parseTaskArray = (value) => {
|
|
1357
|
+
if (Array.isArray(value)) {
|
|
1358
|
+
return value.filter((entry) => typeof entry === "string");
|
|
1359
|
+
}
|
|
1360
|
+
if (typeof value !== "string")
|
|
1361
|
+
return [];
|
|
1362
|
+
const trimmed = value.trim();
|
|
1363
|
+
if (!trimmed)
|
|
1364
|
+
return [];
|
|
1365
|
+
try {
|
|
1366
|
+
const parsed = JSON.parse(trimmed);
|
|
1367
|
+
if (Array.isArray(parsed)) {
|
|
1368
|
+
return parsed.filter((entry) => typeof entry === "string");
|
|
1369
|
+
}
|
|
1370
|
+
if (typeof parsed === "string" && parsed.trim()) {
|
|
1371
|
+
return [parsed.trim()];
|
|
1372
|
+
}
|
|
1373
|
+
}
|
|
1374
|
+
catch {
|
|
1375
|
+
// Older/corrupt DB rows may contain raw comma-separated paths instead of JSON arrays.
|
|
1376
|
+
}
|
|
1377
|
+
return trimmed.split(",").map((entry) => entry.trim()).filter(Boolean);
|
|
1378
|
+
};
|
|
1335
1379
|
return {
|
|
1336
1380
|
milestone_id: row["milestone_id"],
|
|
1337
1381
|
slice_id: row["slice_id"],
|
|
@@ -1351,10 +1395,10 @@ function rowToTask(row) {
|
|
|
1351
1395
|
full_summary_md: row["full_summary_md"],
|
|
1352
1396
|
description: row["description"] ?? "",
|
|
1353
1397
|
estimate: row["estimate"] ?? "",
|
|
1354
|
-
files:
|
|
1398
|
+
files: parseTaskArray(row["files"]),
|
|
1355
1399
|
verify: row["verify"] ?? "",
|
|
1356
|
-
inputs:
|
|
1357
|
-
expected_output:
|
|
1400
|
+
inputs: parseTaskArray(row["inputs"]),
|
|
1401
|
+
expected_output: parseTaskArray(row["expected_output"]),
|
|
1358
1402
|
observability_impact: row["observability_impact"] ?? "",
|
|
1359
1403
|
full_plan_md: row["full_plan_md"] ?? "",
|
|
1360
1404
|
sequence: row["sequence"] ?? 0,
|
|
@@ -1927,3 +1971,43 @@ export function getPendingSliceGateCount(milestoneId, sliceId) {
|
|
|
1927
1971
|
WHERE milestone_id = :mid AND slice_id = :sid AND scope = 'slice' AND status = 'pending'`).get({ ":mid": milestoneId, ":sid": sliceId });
|
|
1928
1972
|
return row ? row["cnt"] : 0;
|
|
1929
1973
|
}
|
|
1974
|
+
/**
|
|
1975
|
+
* Return pending gate rows owned by a specific workflow turn.
|
|
1976
|
+
*
|
|
1977
|
+
* Unlike `getPendingGates(..., scope)`, this filters by the registry's
|
|
1978
|
+
* `ownerTurn` metadata so callers can distinguish Q3/Q4 (owned by
|
|
1979
|
+
* gate-evaluate) from Q8 (owned by complete-slice) even though both are
|
|
1980
|
+
* scope:"slice". Pass `taskId` to narrow task-scoped results to one task.
|
|
1981
|
+
*/
|
|
1982
|
+
export function getPendingGatesForTurn(milestoneId, sliceId, turn, taskId) {
|
|
1983
|
+
if (!currentDb)
|
|
1984
|
+
return [];
|
|
1985
|
+
const ids = getGateIdsForTurn(turn);
|
|
1986
|
+
if (ids.size === 0)
|
|
1987
|
+
return [];
|
|
1988
|
+
const idList = [...ids];
|
|
1989
|
+
const placeholders = idList.map((_, i) => `:gid${i}`).join(",");
|
|
1990
|
+
const params = {
|
|
1991
|
+
":mid": milestoneId,
|
|
1992
|
+
":sid": sliceId,
|
|
1993
|
+
};
|
|
1994
|
+
idList.forEach((id, i) => {
|
|
1995
|
+
params[`:gid${i}`] = id;
|
|
1996
|
+
});
|
|
1997
|
+
let sql = `SELECT * FROM quality_gates
|
|
1998
|
+
WHERE milestone_id = :mid AND slice_id = :sid
|
|
1999
|
+
AND status = 'pending'
|
|
2000
|
+
AND gate_id IN (${placeholders})`;
|
|
2001
|
+
if (taskId !== undefined) {
|
|
2002
|
+
sql += ` AND task_id = :tid`;
|
|
2003
|
+
params[":tid"] = taskId;
|
|
2004
|
+
}
|
|
2005
|
+
return currentDb.prepare(sql).all(params).map(rowToGate);
|
|
2006
|
+
}
|
|
2007
|
+
/**
|
|
2008
|
+
* Count pending gates for a turn. Convenience wrapper used by state
|
|
2009
|
+
* derivation to decide whether a phase transition should pause.
|
|
2010
|
+
*/
|
|
2011
|
+
export function getPendingGateCountForTurn(milestoneId, sliceId, turn) {
|
|
2012
|
+
return getPendingGatesForTurn(milestoneId, sliceId, turn).length;
|
|
2013
|
+
}
|
|
@@ -26,6 +26,8 @@ export const PROVIDER_REGISTRY = [
|
|
|
26
26
|
{ id: "custom-openai", label: "Custom (OpenAI-compat)", category: "llm", envVar: "CUSTOM_OPENAI_API_KEY" },
|
|
27
27
|
{ id: "cerebras", label: "Cerebras", category: "llm", envVar: "CEREBRAS_API_KEY" },
|
|
28
28
|
{ id: "azure-openai-responses", label: "Azure OpenAI", category: "llm", envVar: "AZURE_OPENAI_API_KEY" },
|
|
29
|
+
{ id: "alibaba-coding-plan", label: "Alibaba Coding Plan", category: "llm", envVar: "ALIBABA_API_KEY", dashboardUrl: "bailian.console.aliyun.com" },
|
|
30
|
+
{ id: "alibaba-dashscope", label: "Alibaba DashScope", category: "llm", envVar: "DASHSCOPE_API_KEY", dashboardUrl: "dashscope.console.aliyun.com" },
|
|
29
31
|
// Tool Keys
|
|
30
32
|
{ id: "context7", label: "Context7 Docs", category: "tool", envVar: "CONTEXT7_API_KEY", dashboardUrl: "context7.com/dashboard" },
|
|
31
33
|
{ id: "jina", label: "Jina Page Extract", category: "tool", envVar: "JINA_API_KEY", dashboardUrl: "jina.ai/api" },
|
|
@@ -6,17 +6,12 @@
|
|
|
6
6
|
* records in the DB. This module inserts milestone-level validation gates
|
|
7
7
|
* that correspond to the validation checks performed.
|
|
8
8
|
*
|
|
9
|
-
* Gate IDs for milestone validation
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* MV03 — Cross-slice integration
|
|
13
|
-
* MV04 — Requirement coverage
|
|
14
|
-
*
|
|
15
|
-
* These use the existing quality_gates table with scope "milestone".
|
|
9
|
+
* Gate IDs for milestone validation (MV01–MV04) are sourced from the
|
|
10
|
+
* gate registry so the definitions stay in lockstep with prompt builders,
|
|
11
|
+
* dispatch rules, and state derivation. See gate-registry.ts.
|
|
16
12
|
*/
|
|
17
13
|
import { _getAdapter } from "./gsd-db.js";
|
|
18
|
-
|
|
19
|
-
const MILESTONE_GATE_IDS = ["MV01", "MV02", "MV03", "MV04"];
|
|
14
|
+
import { getGatesForTurn } from "./gate-registry.js";
|
|
20
15
|
/**
|
|
21
16
|
* Insert milestone-level quality_gates records for a validation run.
|
|
22
17
|
*
|
|
@@ -24,21 +19,25 @@ const MILESTONE_GATE_IDS = ["MV01", "MV02", "MV03", "MV04"];
|
|
|
24
19
|
* from the overall milestone validation verdict. Individual gate-level
|
|
25
20
|
* verdicts are not available (the handler receives a single verdict),
|
|
26
21
|
* so all gates share the overall verdict.
|
|
22
|
+
*
|
|
23
|
+
* Gate IDs come from the registry — adding/removing an MV-scoped gate
|
|
24
|
+
* in gate-registry.ts automatically flows through here.
|
|
27
25
|
*/
|
|
28
26
|
export function insertMilestoneValidationGates(milestoneId, sliceId, verdict, evaluatedAt) {
|
|
29
27
|
const db = _getAdapter();
|
|
30
28
|
if (!db)
|
|
31
29
|
return;
|
|
32
30
|
const gateVerdict = verdict === "pass" ? "pass" : "flag";
|
|
33
|
-
|
|
31
|
+
const milestoneGates = getGatesForTurn("validate-milestone");
|
|
32
|
+
for (const def of milestoneGates) {
|
|
34
33
|
db.prepare(`INSERT OR REPLACE INTO quality_gates
|
|
35
34
|
(milestone_id, slice_id, gate_id, scope, task_id, status, verdict, rationale, findings, evaluated_at)
|
|
36
35
|
VALUES (:mid, :sid, :gid, 'milestone', '', 'complete', :verdict, :rationale, '', :evaluated_at)`).run({
|
|
37
36
|
":mid": milestoneId,
|
|
38
37
|
":sid": sliceId,
|
|
39
|
-
":gid":
|
|
38
|
+
":gid": def.id,
|
|
40
39
|
":verdict": gateVerdict,
|
|
41
|
-
":rationale":
|
|
40
|
+
":rationale": `${def.promptSection} — milestone validation verdict: ${verdict}`,
|
|
42
41
|
":evaluated_at": evaluatedAt,
|
|
43
42
|
});
|
|
44
43
|
}
|