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
|
@@ -13,7 +13,7 @@ import { existsSync, readdirSync, readFileSync } from 'node:fs';
|
|
|
13
13
|
import { debugCount, debugTime } from './debug-logger.js';
|
|
14
14
|
import { logWarning, logError } from './workflow-logger.js';
|
|
15
15
|
import { extractVerdict } from './verdict-parser.js';
|
|
16
|
-
import { isDbAvailable, wasDbOpenAttempted, getAllMilestones, getMilestone, getMilestoneSlices, getSliceTasks, getReplanHistory, getSlice, insertMilestone, insertSlice, insertTask, updateTaskStatus,
|
|
16
|
+
import { isDbAvailable, wasDbOpenAttempted, getAllMilestones, getMilestone, getMilestoneSlices, getSliceTasks, getReplanHistory, getSlice, insertMilestone, insertSlice, insertTask, updateSliceStatus, updateTaskStatus, getPendingGateCountForTurn, } from './gsd-db.js';
|
|
17
17
|
/**
|
|
18
18
|
* A "ghost" milestone directory contains only META.json (and no substantive
|
|
19
19
|
* files like CONTEXT, CONTEXT-DRAFT, ROADMAP, or SUMMARY). These appear when
|
|
@@ -286,6 +286,26 @@ function reconcileDiskToDb(basePath) {
|
|
|
286
286
|
depends: s.depends, demo: s.demo,
|
|
287
287
|
});
|
|
288
288
|
}
|
|
289
|
+
// Reconcile stale *existing* slice rows (#3599): a slice row may exist in
|
|
290
|
+
// the DB with status "pending" even though disk artifacts (SUMMARY) prove
|
|
291
|
+
// completion — the same class of desync that task-level reconciliation
|
|
292
|
+
// (further below) already handles. Without this, the dependency resolver
|
|
293
|
+
// builds doneSliceIds from stale DB rows and downstream slices stay blocked
|
|
294
|
+
// forever with "No slice eligible".
|
|
295
|
+
for (const dbSlice of dbSlices) {
|
|
296
|
+
if (isStatusDone(dbSlice.status))
|
|
297
|
+
continue;
|
|
298
|
+
const summaryPath = resolveSliceFile(basePath, mid, dbSlice.id, "SUMMARY");
|
|
299
|
+
if (summaryPath) {
|
|
300
|
+
try {
|
|
301
|
+
updateSliceStatus(mid, dbSlice.id, "complete");
|
|
302
|
+
logWarning("reconcile", `slice ${mid}/${dbSlice.id} status reconciled from "${dbSlice.status}" to "complete" (#3599)`, { mid, sid: dbSlice.id });
|
|
303
|
+
}
|
|
304
|
+
catch (e) {
|
|
305
|
+
logError("reconcile", `failed to update slice ${dbSlice.id}`, { sid: dbSlice.id, error: e.message });
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
289
309
|
}
|
|
290
310
|
return allMilestones;
|
|
291
311
|
}
|
|
@@ -724,7 +744,14 @@ export async function deriveStateFromDb(basePath) {
|
|
|
724
744
|
};
|
|
725
745
|
}
|
|
726
746
|
}
|
|
727
|
-
|
|
747
|
+
// ── Quality gate evaluation check ──────────────────────────────────
|
|
748
|
+
// Pause before execution only when gates owned by the `gate-evaluate`
|
|
749
|
+
// turn (Q3/Q4) are still pending. Q8 is also `scope:"slice"` but is
|
|
750
|
+
// owned by `complete-slice`, so it must NOT block the evaluating-gates
|
|
751
|
+
// phase — otherwise auto-loop stalls forever waiting for a gate that
|
|
752
|
+
// this turn never evaluates. See gate-registry.ts for the ownership map.
|
|
753
|
+
// Slices with zero gate rows (pre-feature or simple) skip straight through.
|
|
754
|
+
const pendingGateCount = getPendingGateCountForTurn(activeMilestone.id, activeSlice.id, "gate-evaluate");
|
|
728
755
|
if (pendingGateCount > 0) {
|
|
729
756
|
return {
|
|
730
757
|
activeMilestone, activeSlice, activeTask: null,
|
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
import { join } from "node:path";
|
|
10
10
|
import { mkdirSync } from "node:fs";
|
|
11
11
|
import { isClosedStatus } from "../status-guards.js";
|
|
12
|
-
import { transaction, insertMilestone, insertSlice, getSlice, getSliceTasks, getMilestone, updateSliceStatus, setSliceSummaryMd, } from "../gsd-db.js";
|
|
12
|
+
import { transaction, insertMilestone, insertSlice, getSlice, getSliceTasks, getMilestone, updateSliceStatus, setSliceSummaryMd, saveGateResult, getPendingGatesForTurn, } from "../gsd-db.js";
|
|
13
|
+
import { getGatesForTurn } from "../gate-registry.js";
|
|
13
14
|
import { resolveSlicePath, clearPathCache } from "../paths.js";
|
|
14
15
|
import { checkOwnership, sliceUnitKey } from "../unit-ownership.js";
|
|
15
16
|
import { saveFile, clearParseCache } from "../files.js";
|
|
@@ -19,6 +20,19 @@ import { renderAllProjections } from "../workflow-projections.js";
|
|
|
19
20
|
import { writeManifest } from "../workflow-manifest.js";
|
|
20
21
|
import { appendEvent } from "../workflow-events.js";
|
|
21
22
|
import { logWarning, logError } from "../workflow-logger.js";
|
|
23
|
+
/**
|
|
24
|
+
* Map a complete-slice-owned gate id to the CompleteSliceParams field
|
|
25
|
+
* whose presence drives `pass` vs. `omitted`. Keep this in lockstep with
|
|
26
|
+
* the gates declared in gate-registry.ts under ownerTurn "complete-slice".
|
|
27
|
+
*/
|
|
28
|
+
function sliceGateFieldForId(id, params) {
|
|
29
|
+
switch (id) {
|
|
30
|
+
case "Q8":
|
|
31
|
+
return params.operationalReadiness;
|
|
32
|
+
default:
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
22
36
|
/**
|
|
23
37
|
* Render slice summary markdown matching the template format.
|
|
24
38
|
* YAML frontmatter uses snake_case keys for parseSummary() compatibility.
|
|
@@ -134,6 +148,10 @@ ${reqSurfaced}
|
|
|
134
148
|
|
|
135
149
|
${reqInvalidated}
|
|
136
150
|
|
|
151
|
+
## Operational Readiness
|
|
152
|
+
|
|
153
|
+
${params.operationalReadiness?.trim() || "None."}
|
|
154
|
+
|
|
137
155
|
## Deviations
|
|
138
156
|
|
|
139
157
|
${params.deviations || "None."}
|
|
@@ -271,6 +289,39 @@ export async function handleCompleteSlice(params, basePath) {
|
|
|
271
289
|
}
|
|
272
290
|
// Store rendered markdown in DB for D004 recovery
|
|
273
291
|
setSliceSummaryMd(params.milestoneId, params.sliceId, summaryMd, uatMd);
|
|
292
|
+
// ── Close gates owned by complete-slice (Q8) ───────────────────────────
|
|
293
|
+
// Each owned gate maps to a specific summary section via the registry.
|
|
294
|
+
// If the caller populated the corresponding field, record `pass`; if the
|
|
295
|
+
// field is empty, record `omitted`. Without this loop, Q8 would stay
|
|
296
|
+
// pending forever and block future state derivation (see gate-registry).
|
|
297
|
+
try {
|
|
298
|
+
const pendingGates = getPendingGatesForTurn(params.milestoneId, params.sliceId, "complete-slice");
|
|
299
|
+
if (pendingGates.length > 0) {
|
|
300
|
+
const ownedDefs = new Map(getGatesForTurn("complete-slice").map((g) => [g.id, g]));
|
|
301
|
+
for (const row of pendingGates) {
|
|
302
|
+
const def = ownedDefs.get(row.gate_id);
|
|
303
|
+
if (!def)
|
|
304
|
+
continue;
|
|
305
|
+
// Map gate id → param field it maps to. Keep the map local so
|
|
306
|
+
// adding a new complete-slice gate is a single place change.
|
|
307
|
+
const field = sliceGateFieldForId(def.id, params);
|
|
308
|
+
const hasContent = typeof field === "string" && field.trim().length > 0;
|
|
309
|
+
saveGateResult({
|
|
310
|
+
milestoneId: params.milestoneId,
|
|
311
|
+
sliceId: params.sliceId,
|
|
312
|
+
gateId: def.id,
|
|
313
|
+
verdict: hasContent ? "pass" : "omitted",
|
|
314
|
+
rationale: hasContent
|
|
315
|
+
? `${def.promptSection} section populated in slice summary`
|
|
316
|
+
: `${def.promptSection} section left empty — recorded as omitted`,
|
|
317
|
+
findings: hasContent ? field.trim() : "",
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
catch (gateErr) {
|
|
323
|
+
logWarning("tool", `complete-slice gate close warning for ${params.milestoneId}/${params.sliceId}: ${gateErr.message}`);
|
|
324
|
+
}
|
|
274
325
|
// Invalidate all caches
|
|
275
326
|
invalidateStateCache();
|
|
276
327
|
clearPathCache();
|
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
import { join } from "node:path";
|
|
10
10
|
import { mkdirSync } from "node:fs";
|
|
11
11
|
import { isClosedStatus } from "../status-guards.js";
|
|
12
|
-
import { transaction, insertMilestone, insertSlice, insertTask, insertVerificationEvidence, getMilestone, getSlice, getTask, updateTaskStatus, setTaskSummaryMd, deleteVerificationEvidence, } from "../gsd-db.js";
|
|
12
|
+
import { transaction, insertMilestone, insertSlice, insertTask, insertVerificationEvidence, getMilestone, getSlice, getTask, updateTaskStatus, setTaskSummaryMd, deleteVerificationEvidence, saveGateResult, getPendingGatesForTurn, } from "../gsd-db.js";
|
|
13
|
+
import { getGatesForTurn } from "../gate-registry.js";
|
|
13
14
|
import { resolveSliceFile, resolveTasksDir, clearPathCache } from "../paths.js";
|
|
14
15
|
import { checkOwnership, taskUnitKey } from "../unit-ownership.js";
|
|
15
16
|
import { saveFile, clearParseCache } from "../files.js";
|
|
@@ -19,6 +20,23 @@ import { renderAllProjections, renderSummaryContent } from "../workflow-projecti
|
|
|
19
20
|
import { writeManifest } from "../workflow-manifest.js";
|
|
20
21
|
import { appendEvent } from "../workflow-events.js";
|
|
21
22
|
import { logWarning, logError } from "../workflow-logger.js";
|
|
23
|
+
/**
|
|
24
|
+
* Map an execute-task-owned gate id to the CompleteTaskParams field whose
|
|
25
|
+
* presence drives `pass` vs. `omitted`. Keep in lockstep with the gates
|
|
26
|
+
* declared in gate-registry.ts under ownerTurn "execute-task".
|
|
27
|
+
*/
|
|
28
|
+
function taskGateFieldForId(id, params) {
|
|
29
|
+
switch (id) {
|
|
30
|
+
case "Q5":
|
|
31
|
+
return params.failureModes;
|
|
32
|
+
case "Q6":
|
|
33
|
+
return params.loadProfile;
|
|
34
|
+
case "Q7":
|
|
35
|
+
return params.negativeTests;
|
|
36
|
+
default:
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
22
40
|
/**
|
|
23
41
|
* Normalize a list parameter that may arrive as a string (newline-delimited
|
|
24
42
|
* bullet list from the LLM) into a string array (#3361).
|
|
@@ -189,6 +207,38 @@ export async function handleCompleteTask(params, basePath) {
|
|
|
189
207
|
}
|
|
190
208
|
// Store rendered markdown in DB for D004 recovery
|
|
191
209
|
setTaskSummaryMd(params.milestoneId, params.sliceId, params.taskId, summaryMd);
|
|
210
|
+
// ── Close gates owned by execute-task (Q5/Q6/Q7) for this task ────────
|
|
211
|
+
// Each gate id maps to a specific params field via taskGateFieldForId.
|
|
212
|
+
// When the model populates the field, record `pass`; when it's empty,
|
|
213
|
+
// record `omitted`. Task-scoped rows are filtered by taskId so a single
|
|
214
|
+
// task's completion doesn't touch sibling tasks' gate rows.
|
|
215
|
+
try {
|
|
216
|
+
const pendingGates = getPendingGatesForTurn(params.milestoneId, params.sliceId, "execute-task", params.taskId);
|
|
217
|
+
if (pendingGates.length > 0) {
|
|
218
|
+
const ownedDefs = new Map(getGatesForTurn("execute-task").map((g) => [g.id, g]));
|
|
219
|
+
for (const row of pendingGates) {
|
|
220
|
+
const def = ownedDefs.get(row.gate_id);
|
|
221
|
+
if (!def)
|
|
222
|
+
continue;
|
|
223
|
+
const field = taskGateFieldForId(def.id, params);
|
|
224
|
+
const hasContent = typeof field === "string" && field.trim().length > 0;
|
|
225
|
+
saveGateResult({
|
|
226
|
+
milestoneId: params.milestoneId,
|
|
227
|
+
sliceId: params.sliceId,
|
|
228
|
+
taskId: params.taskId,
|
|
229
|
+
gateId: def.id,
|
|
230
|
+
verdict: hasContent ? "pass" : "omitted",
|
|
231
|
+
rationale: hasContent
|
|
232
|
+
? `${def.promptSection} section populated in task summary`
|
|
233
|
+
: `${def.promptSection} section left empty — recorded as omitted`,
|
|
234
|
+
findings: hasContent ? field.trim() : "",
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
catch (gateErr) {
|
|
240
|
+
logWarning("tool", `complete-task gate close warning for ${params.milestoneId}/${params.sliceId}/${params.taskId}: ${gateErr.message}`);
|
|
241
|
+
}
|
|
192
242
|
// Invalidate all caches
|
|
193
243
|
invalidateStateCache();
|
|
194
244
|
clearPathCache();
|
|
@@ -2,6 +2,7 @@ import { ensureDbOpen } from "../bootstrap/dynamic-tools.js";
|
|
|
2
2
|
import { sanitizeCompleteMilestoneParams } from "../bootstrap/sanitize-complete-milestone.js";
|
|
3
3
|
import { loadWriteGateSnapshot, shouldBlockContextArtifactSaveInSnapshot } from "../bootstrap/write-gate.js";
|
|
4
4
|
import { getMilestone, getSliceStatusSummary, getSliceTaskCounts, _getAdapter, saveGateResult, } from "../gsd-db.js";
|
|
5
|
+
import { GATE_REGISTRY } from "../gate-registry.js";
|
|
5
6
|
import { saveArtifactToDb } from "../db-writer.js";
|
|
6
7
|
import { handleCompleteMilestone } from "./complete-milestone.js";
|
|
7
8
|
import { handleCompleteTask } from "./complete-task.js";
|
|
@@ -323,7 +324,9 @@ export async function executeSaveGateResult(params, basePath = process.cwd()) {
|
|
|
323
324
|
isError: true,
|
|
324
325
|
};
|
|
325
326
|
}
|
|
326
|
-
|
|
327
|
+
// Source of truth: gate-registry.ts. Every declared GateId is accepted,
|
|
328
|
+
// so adding a new gate in one place automatically flows through here.
|
|
329
|
+
const validGates = Object.keys(GATE_REGISTRY);
|
|
327
330
|
if (!validGates.includes(params.gateId)) {
|
|
328
331
|
return {
|
|
329
332
|
content: [{ type: "text", text: `Error: Invalid gateId "${params.gateId}". Must be one of: ${validGates.join(", ")}` }],
|
|
@@ -138,6 +138,13 @@ export function renderRoadmapProjection(basePath, milestoneId) {
|
|
|
138
138
|
* regeneration, they are queried from the DB by renderSummaryProjection.
|
|
139
139
|
*/
|
|
140
140
|
export function renderSummaryContent(taskRow, sliceId, milestoneId, evidence) {
|
|
141
|
+
// If the task already has a fully rendered summary (written by handleCompleteTask's
|
|
142
|
+
// renderSummaryMarkdown), use it as-is. That content already includes frontmatter,
|
|
143
|
+
// heading, and all sections. Re-wrapping it inside a second frontmatter/heading
|
|
144
|
+
// envelope produces double frontmatter and duplicate sections.
|
|
145
|
+
if (taskRow.full_summary_md && taskRow.full_summary_md.trimStart().startsWith("---")) {
|
|
146
|
+
return taskRow.full_summary_md;
|
|
147
|
+
}
|
|
141
148
|
// ── Frontmatter (YAML list format, matches parseSummary() expectations) ──
|
|
142
149
|
const keyFilesYaml = taskRow.key_files && taskRow.key_files.length > 0
|
|
143
150
|
? taskRow.key_files.map(f => ` - ${f}`).join("\n")
|
|
@@ -468,14 +468,41 @@ export function removeWorktree(basePath, name, opts = {}) {
|
|
|
468
468
|
}
|
|
469
469
|
}
|
|
470
470
|
}
|
|
471
|
-
/**
|
|
472
|
-
|
|
473
|
-
|
|
471
|
+
/**
|
|
472
|
+
* Paths to skip in all worktree diffs (internal/runtime artifacts).
|
|
473
|
+
*
|
|
474
|
+
* NOTE: These arrays must stay synchronized with GSD_RUNTIME_PATTERNS in gitignore.ts.
|
|
475
|
+
* That file is the canonical source of truth for runtime ignore patterns.
|
|
476
|
+
* This module uses a split representation (paths/exact/prefixes) for efficient matching.
|
|
477
|
+
*/
|
|
478
|
+
const SKIP_PATHS = [
|
|
479
|
+
".gsd/worktrees/",
|
|
480
|
+
".gsd/runtime/",
|
|
481
|
+
".gsd/activity/",
|
|
482
|
+
".gsd/forensics/",
|
|
483
|
+
".gsd/parallel/",
|
|
484
|
+
".gsd/journal/",
|
|
485
|
+
];
|
|
486
|
+
const SKIP_EXACT = [
|
|
487
|
+
".gsd/STATE.md",
|
|
488
|
+
".gsd/auto.lock",
|
|
489
|
+
".gsd/metrics.json",
|
|
490
|
+
".gsd/state-manifest.json",
|
|
491
|
+
".gsd/doctor-history.jsonl",
|
|
492
|
+
".gsd/event-log.jsonl",
|
|
493
|
+
];
|
|
494
|
+
/** File prefixes to skip (for wildcard patterns like completed-units*.json, gsd.db*). */
|
|
495
|
+
const SKIP_PREFIXES = [
|
|
496
|
+
".gsd/completed-units",
|
|
497
|
+
".gsd/gsd.db",
|
|
498
|
+
];
|
|
474
499
|
function shouldSkipPath(filePath) {
|
|
475
500
|
if (SKIP_PATHS.some(p => filePath.startsWith(p)))
|
|
476
501
|
return true;
|
|
477
502
|
if (SKIP_EXACT.includes(filePath))
|
|
478
503
|
return true;
|
|
504
|
+
if (SKIP_PREFIXES.some(p => filePath.startsWith(p)))
|
|
505
|
+
return true;
|
|
479
506
|
return false;
|
|
480
507
|
}
|
|
481
508
|
function parseDiffNameStatus(entries) {
|
|
@@ -22,6 +22,9 @@ const BLOCKED_PATTERNS = [
|
|
|
22
22
|
/(^|[/\\])\.gsd[/\\]STATE\.md$/i,
|
|
23
23
|
// Also match resolved symlink paths under ~/.gsd/projects/ (Pitfall #6)
|
|
24
24
|
/(^|[/\\])\.gsd[/\\]projects[/\\][^/\\]+[/\\]STATE\.md$/i,
|
|
25
|
+
// gsd.db and WAL/SHM files — single-writer WAL connection managed by engine (#3625)
|
|
26
|
+
/(^|[/\\])\.gsd[/\\]gsd\.db(-wal|-shm)?$/i,
|
|
27
|
+
/(^|[/\\])\.gsd[/\\]projects[/\\][^/\\]+[/\\]gsd\.db(-wal|-shm)?$/i,
|
|
25
28
|
];
|
|
26
29
|
/**
|
|
27
30
|
* Bash command patterns that target STATE.md.
|
|
@@ -38,6 +41,12 @@ const BASH_STATE_PATTERNS = [
|
|
|
38
41
|
/\bsed\b.*-i.*STATE\.md/i,
|
|
39
42
|
// dd output to STATE.md
|
|
40
43
|
/\bdd\b.*of=\S*STATE\.md/i,
|
|
44
|
+
// Direct DB access via sqlite3/sql.js/better-sqlite3 targeting gsd.db (#3625)
|
|
45
|
+
/\b(sqlite3|sql\.js|better-sqlite3|node:sqlite)\b.*gsd\.db/i,
|
|
46
|
+
/\bgsd\.db\b.*\b(sqlite3|sql\.js|better-sqlite3)\b/i,
|
|
47
|
+
// Shell writes targeting gsd.db files
|
|
48
|
+
/[>|]+\s*\S*gsd\.db/i,
|
|
49
|
+
/\b(cp|mv|dd)\b.*gsd\.db/i,
|
|
41
50
|
];
|
|
42
51
|
/**
|
|
43
52
|
* Tests whether the given file path matches a blocked authoritative .gsd/ state file.
|
|
@@ -75,7 +84,7 @@ function matchesBlockedPattern(path) {
|
|
|
75
84
|
* Error message returned when an agent attempts to directly write an authoritative .gsd/ state file.
|
|
76
85
|
* Directs the agent to use engine tool calls instead.
|
|
77
86
|
*/
|
|
78
|
-
export const BLOCKED_WRITE_ERROR = `Direct writes to .gsd/STATE.md are blocked. Use engine tool calls instead:
|
|
87
|
+
export const BLOCKED_WRITE_ERROR = `Direct writes to .gsd/STATE.md and .gsd/gsd.db are blocked. Use engine tool calls instead:
|
|
79
88
|
- To complete a task: call gsd_complete_task(milestone_id, slice_id, task_id, summary)
|
|
80
89
|
- To complete a slice: call gsd_complete_slice(milestone_id, slice_id, summary, uat_result)
|
|
81
90
|
- To save a decision: call gsd_save_decision(scope, decision, choice, rationale)
|
|
@@ -49,16 +49,22 @@ async function probeAndRegister(pi) {
|
|
|
49
49
|
return false;
|
|
50
50
|
}
|
|
51
51
|
const models = await discoverModels();
|
|
52
|
-
if (models.length === 0)
|
|
53
|
-
|
|
52
|
+
if (models.length === 0) {
|
|
53
|
+
// No local models means there's nothing usable to register in GSD.
|
|
54
|
+
// Keep the footer/status clean instead of advertising Ollama availability.
|
|
55
|
+
if (providerRegistered) {
|
|
56
|
+
pi.unregisterProvider("ollama");
|
|
57
|
+
providerRegistered = false;
|
|
58
|
+
}
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
54
61
|
const baseUrl = client.getOllamaHost();
|
|
55
|
-
// Use authMode "apiKey"
|
|
56
|
-
//
|
|
57
|
-
//
|
|
58
|
-
// Authorization header so the dummy key is harmless.
|
|
62
|
+
// Use authMode "apiKey" (#3440). Local Ollama ignores the Authorization header,
|
|
63
|
+
// so the "ollama" fallback is harmless. For cloud endpoints (OLLAMA_HOST pointing
|
|
64
|
+
// to ollama.com or a remote instance), OLLAMA_API_KEY is picked up here.
|
|
59
65
|
pi.registerProvider("ollama", {
|
|
60
66
|
authMode: "apiKey",
|
|
61
|
-
apiKey: "ollama",
|
|
67
|
+
apiKey: process.env.OLLAMA_API_KEY ?? "ollama",
|
|
62
68
|
baseUrl,
|
|
63
69
|
api: "ollama-chat",
|
|
64
70
|
streamSimple: streamOllamaChat,
|
|
@@ -102,10 +108,11 @@ export default function ollama(pi) {
|
|
|
102
108
|
else {
|
|
103
109
|
probeAndRegister(pi)
|
|
104
110
|
.then((found) => {
|
|
105
|
-
|
|
106
|
-
ctx.ui.setStatus("ollama", "Ollama");
|
|
111
|
+
ctx.ui.setStatus("ollama", found ? "Ollama" : undefined);
|
|
107
112
|
})
|
|
108
|
-
.catch(() => {
|
|
113
|
+
.catch(() => {
|
|
114
|
+
ctx.ui.setStatus("ollama", undefined);
|
|
115
|
+
});
|
|
109
116
|
}
|
|
110
117
|
});
|
|
111
118
|
pi.on("session_shutdown", async () => {
|
|
@@ -15,11 +15,34 @@ export function getOllamaHost() {
|
|
|
15
15
|
return host;
|
|
16
16
|
return `http://${host}`;
|
|
17
17
|
}
|
|
18
|
+
/**
|
|
19
|
+
* Get auth headers for Ollama API requests.
|
|
20
|
+
* For cloud endpoints (OLLAMA_HOST pointing to ollama.com or remote instances),
|
|
21
|
+
* OLLAMA_API_KEY is used as a Bearer token. Local Ollama ignores the header.
|
|
22
|
+
*/
|
|
23
|
+
function getAuthHeaders() {
|
|
24
|
+
const apiKey = process.env.OLLAMA_API_KEY;
|
|
25
|
+
if (!apiKey)
|
|
26
|
+
return {};
|
|
27
|
+
return { Authorization: `Bearer ${apiKey}` };
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Merge auth headers into request options.
|
|
31
|
+
*/
|
|
32
|
+
function withAuth(options = {}) {
|
|
33
|
+
const authHeaders = getAuthHeaders();
|
|
34
|
+
if (Object.keys(authHeaders).length === 0)
|
|
35
|
+
return options;
|
|
36
|
+
return {
|
|
37
|
+
...options,
|
|
38
|
+
headers: { ...authHeaders, ...(options.headers || {}) },
|
|
39
|
+
};
|
|
40
|
+
}
|
|
18
41
|
async function fetchWithTimeout(url, options = {}, timeoutMs = REQUEST_TIMEOUT_MS) {
|
|
19
42
|
const controller = new AbortController();
|
|
20
43
|
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
21
44
|
try {
|
|
22
|
-
return await fetch(url, { ...options, signal: controller.signal });
|
|
45
|
+
return await fetch(url, withAuth({ ...options, signal: controller.signal }));
|
|
23
46
|
}
|
|
24
47
|
finally {
|
|
25
48
|
clearTimeout(timeout);
|
|
@@ -27,10 +50,16 @@ async function fetchWithTimeout(url, options = {}, timeoutMs = REQUEST_TIMEOUT_M
|
|
|
27
50
|
}
|
|
28
51
|
/**
|
|
29
52
|
* Check if Ollama is running and reachable.
|
|
53
|
+
* For cloud endpoints (OLLAMA_HOST pointing to ollama.com), uses /api/tags
|
|
54
|
+
* as the probe since the root endpoint may not be available.
|
|
30
55
|
*/
|
|
31
56
|
export async function isRunning() {
|
|
32
57
|
try {
|
|
33
|
-
const
|
|
58
|
+
const host = getOllamaHost();
|
|
59
|
+
const isCloud = host.includes("ollama.com") || host.includes("cloud");
|
|
60
|
+
const probeUrl = isCloud ? `${host}/api/tags` : `${host}/`;
|
|
61
|
+
const timeout = isCloud ? REQUEST_TIMEOUT_MS : PROBE_TIMEOUT_MS;
|
|
62
|
+
const response = await fetchWithTimeout(probeUrl, isCloud ? { method: "GET" } : {}, timeout);
|
|
34
63
|
return response.ok;
|
|
35
64
|
}
|
|
36
65
|
catch {
|
|
@@ -92,12 +121,12 @@ export async function getRunningModels() {
|
|
|
92
121
|
* Returns when the pull is complete.
|
|
93
122
|
*/
|
|
94
123
|
export async function pullModel(name, onProgress, signal) {
|
|
95
|
-
const response = await fetch(`${getOllamaHost()}/api/pull`, {
|
|
124
|
+
const response = await fetch(`${getOllamaHost()}/api/pull`, withAuth({
|
|
96
125
|
method: "POST",
|
|
97
126
|
headers: { "Content-Type": "application/json" },
|
|
98
127
|
body: JSON.stringify({ name, stream: true }),
|
|
99
128
|
signal,
|
|
100
|
-
});
|
|
129
|
+
}));
|
|
101
130
|
if (!response.ok) {
|
|
102
131
|
const text = await response.text();
|
|
103
132
|
throw new Error(`Ollama /api/pull returned ${response.status}: ${text}`);
|
|
@@ -114,12 +143,12 @@ export async function pullModel(name, onProgress, signal) {
|
|
|
114
143
|
* Returns an async generator yielding each NDJSON response chunk.
|
|
115
144
|
*/
|
|
116
145
|
export async function* chat(request, signal) {
|
|
117
|
-
const response = await fetch(`${getOllamaHost()}/api/chat`, {
|
|
146
|
+
const response = await fetch(`${getOllamaHost()}/api/chat`, withAuth({
|
|
118
147
|
method: "POST",
|
|
119
148
|
headers: { "Content-Type": "application/json" },
|
|
120
149
|
body: JSON.stringify(request),
|
|
121
150
|
signal,
|
|
122
|
-
});
|
|
151
|
+
}));
|
|
123
152
|
if (!response.ok) {
|
|
124
153
|
const text = await response.text();
|
|
125
154
|
throw new Error(`Ollama /api/chat returned ${response.status}: ${text}`);
|
|
@@ -6,14 +6,40 @@
|
|
|
6
6
|
*
|
|
7
7
|
* Returns models in the format expected by pi.registerProvider().
|
|
8
8
|
*/
|
|
9
|
-
import { listModels } from "./ollama-client.js";
|
|
9
|
+
import { listModels, showModel } from "./ollama-client.js";
|
|
10
10
|
import { estimateContextFromParams, formatModelSize, getModelCapabilities, humanizeModelName, } from "./model-capabilities.js";
|
|
11
|
+
/**
|
|
12
|
+
* Extract context window from /api/show model_info.
|
|
13
|
+
* Keys follow the pattern "{architecture}.context_length" (e.g. "llama.context_length").
|
|
14
|
+
*/
|
|
15
|
+
function extractContextFromModelInfo(modelInfo) {
|
|
16
|
+
for (const [key, value] of Object.entries(modelInfo)) {
|
|
17
|
+
if (key.endsWith(".context_length") && typeof value === "number" && value > 0) {
|
|
18
|
+
return value;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
11
23
|
const ZERO_COST = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
|
|
12
|
-
function enrichModel(info) {
|
|
24
|
+
async function enrichModel(info, deps) {
|
|
13
25
|
const caps = getModelCapabilities(info.name);
|
|
14
26
|
const parameterSize = info.details?.parameter_size ?? "";
|
|
15
|
-
//
|
|
27
|
+
// /api/tags doesn't include context length; /api/show does via "{arch}.context_length" in model_info.
|
|
28
|
+
let showContextWindow;
|
|
29
|
+
if (caps.contextWindow === undefined) {
|
|
30
|
+
try {
|
|
31
|
+
const showData = await deps.showModel(info.name);
|
|
32
|
+
showContextWindow = extractContextFromModelInfo(showData.model_info);
|
|
33
|
+
}
|
|
34
|
+
catch (err) {
|
|
35
|
+
// non-fatal: fall through to estimate
|
|
36
|
+
if (process.env.GSD_DEBUG)
|
|
37
|
+
console.warn(`[ollama] /api/show failed for ${info.name}:`, err instanceof Error ? err.message : String(err));
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
// Determine context window: known table > /api/show > estimate from param size > default
|
|
16
41
|
const contextWindow = caps.contextWindow ??
|
|
42
|
+
showContextWindow ??
|
|
17
43
|
(parameterSize ? estimateContextFromParams(parameterSize) : 8192);
|
|
18
44
|
// Determine max tokens: known table > fraction of context > default
|
|
19
45
|
const maxTokens = caps.maxTokens ?? Math.min(Math.floor(contextWindow / 4), 16384);
|
|
@@ -38,11 +64,11 @@ function enrichModel(info) {
|
|
|
38
64
|
/**
|
|
39
65
|
* Discover all locally available Ollama models with enriched capabilities.
|
|
40
66
|
*/
|
|
41
|
-
export async function discoverModels() {
|
|
42
|
-
const tags = await listModels();
|
|
67
|
+
export async function discoverModels(deps = { listModels, showModel }) {
|
|
68
|
+
const tags = await deps.listModels();
|
|
43
69
|
if (!tags.models || tags.models.length === 0)
|
|
44
70
|
return [];
|
|
45
|
-
return tags.models.map(enrichModel);
|
|
71
|
+
return Promise.all(tags.models.map((m) => enrichModel(m, deps)));
|
|
46
72
|
}
|
|
47
73
|
/**
|
|
48
74
|
* Format a discovered model for display in model list.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GSD Phase State — cross-extension coordination
|
|
3
|
+
* Copyright (c) 2026 Jeremy McSpadden <jeremy@fluxlabs.net>
|
|
4
|
+
*
|
|
5
|
+
* Lightweight module-level state that GSD auto-mode writes to and the
|
|
6
|
+
* subagent tool reads from. Both extensions run in the same process so
|
|
7
|
+
* a module variable is sufficient — no file I/O needed.
|
|
8
|
+
*/
|
|
9
|
+
let _active = false;
|
|
10
|
+
let _currentPhase = null;
|
|
11
|
+
/** Mark GSD auto-mode as active. */
|
|
12
|
+
export function activateGSD() {
|
|
13
|
+
_active = true;
|
|
14
|
+
}
|
|
15
|
+
/** Mark GSD auto-mode as inactive and clear the current phase. */
|
|
16
|
+
export function deactivateGSD() {
|
|
17
|
+
_active = false;
|
|
18
|
+
_currentPhase = null;
|
|
19
|
+
}
|
|
20
|
+
/** Set the currently dispatched GSD phase (e.g. "plan-milestone"). */
|
|
21
|
+
export function setCurrentPhase(phase) {
|
|
22
|
+
_currentPhase = phase;
|
|
23
|
+
}
|
|
24
|
+
/** Clear the current phase (unit completed or aborted). */
|
|
25
|
+
export function clearCurrentPhase() {
|
|
26
|
+
_currentPhase = null;
|
|
27
|
+
}
|
|
28
|
+
/** Returns true if GSD auto-mode is currently active. */
|
|
29
|
+
export function isGSDActive() {
|
|
30
|
+
return _active;
|
|
31
|
+
}
|
|
32
|
+
/** Returns the current GSD phase, or null if none is active. */
|
|
33
|
+
export function getCurrentPhase() {
|
|
34
|
+
return _active ? _currentPhase : null;
|
|
35
|
+
}
|
|
@@ -5,6 +5,12 @@ import * as fs from "node:fs";
|
|
|
5
5
|
import * as path from "node:path";
|
|
6
6
|
import { getAgentDir, parseFrontmatter } from "@gsd/pi-coding-agent";
|
|
7
7
|
const PROJECT_AGENT_DIR_CANDIDATES = [".gsd", ".pi"];
|
|
8
|
+
export function parseConflictsWith(value) {
|
|
9
|
+
if (typeof value !== "string")
|
|
10
|
+
return undefined;
|
|
11
|
+
const conflicts = value.split(",").map((s) => s.trim()).filter(Boolean);
|
|
12
|
+
return conflicts.length > 0 ? conflicts : undefined;
|
|
13
|
+
}
|
|
8
14
|
function parseAgentTools(value) {
|
|
9
15
|
if (typeof value === "string") {
|
|
10
16
|
const tools = value
|
|
@@ -52,11 +58,13 @@ function loadAgentsFromDir(dir, source) {
|
|
|
52
58
|
continue;
|
|
53
59
|
}
|
|
54
60
|
const tools = parseAgentTools(frontmatter.tools);
|
|
61
|
+
const conflictsWith = parseConflictsWith(frontmatter.conflicts_with);
|
|
55
62
|
agents.push({
|
|
56
63
|
name: frontmatter.name,
|
|
57
64
|
description: frontmatter.description,
|
|
58
65
|
tools: tools && tools.length > 0 ? tools : undefined,
|
|
59
66
|
model: frontmatter.model,
|
|
67
|
+
conflictsWith,
|
|
60
68
|
systemPrompt: body,
|
|
61
69
|
source,
|
|
62
70
|
filePath,
|
|
@@ -21,6 +21,7 @@ import { getMarkdownTheme } from "@gsd/pi-coding-agent";
|
|
|
21
21
|
import { Container, Markdown, Spacer, Text } from "@gsd/pi-tui";
|
|
22
22
|
import { Type } from "@sinclair/typebox";
|
|
23
23
|
import { formatTokenCount } from "../shared/mod.js";
|
|
24
|
+
import { getCurrentPhase } from "../shared/gsd-phase-state.js";
|
|
24
25
|
import { discoverAgents } from "./agents.js";
|
|
25
26
|
import { createIsolation, mergeDeltaPatches, readIsolationMode, } from "./isolation.js";
|
|
26
27
|
import { registerWorker, updateWorker } from "./worker-registry.js";
|
|
@@ -268,6 +269,22 @@ async function runSingleAgent(defaultCwd, agents, agentName, task, cwd, step, si
|
|
|
268
269
|
step,
|
|
269
270
|
};
|
|
270
271
|
}
|
|
272
|
+
// GSD phase guard: block agents that conflict with the active GSD phase
|
|
273
|
+
if (agent.conflictsWith && agent.conflictsWith.length > 0) {
|
|
274
|
+
const activePhase = getCurrentPhase();
|
|
275
|
+
if (activePhase && agent.conflictsWith.includes(activePhase)) {
|
|
276
|
+
return {
|
|
277
|
+
agent: agentName,
|
|
278
|
+
agentSource: agent.source,
|
|
279
|
+
task,
|
|
280
|
+
exitCode: 1,
|
|
281
|
+
messages: [],
|
|
282
|
+
stderr: `Agent "${agentName}" is blocked: it conflicts with the active GSD phase "${activePhase}". Use the built-in GSD workflow instead.`,
|
|
283
|
+
usage: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0, contextTokens: 0, turns: 0 },
|
|
284
|
+
step,
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
}
|
|
271
288
|
let tmpPromptDir = null;
|
|
272
289
|
let tmpPromptPath = null;
|
|
273
290
|
const currentResult = {
|
|
@@ -23,10 +23,14 @@ import { getPiDefaultModelAndProvider } from './pi-migration.js';
|
|
|
23
23
|
export function validateConfiguredModel(modelRegistry, settingsManager) {
|
|
24
24
|
const configuredProvider = settingsManager.getDefaultProvider();
|
|
25
25
|
const configuredModel = settingsManager.getDefaultModel();
|
|
26
|
-
const allModels = modelRegistry.getAll();
|
|
27
26
|
const availableModels = modelRegistry.getAvailable();
|
|
27
|
+
// Check against availableModels (configured + auth'd) rather than getAll()
|
|
28
|
+
// so a stale default pointing at an unconfigured provider triggers the
|
|
29
|
+
// fallback. Previously a model present in the registry but missing API
|
|
30
|
+
// key / OAuth would satisfy configuredExists and survive startup, ending
|
|
31
|
+
// up as ctx.model even though it couldn't actually be used.
|
|
28
32
|
const configuredExists = configuredProvider && configuredModel &&
|
|
29
|
-
|
|
33
|
+
availableModels.some((m) => m.provider === configuredProvider && m.id === configuredModel);
|
|
30
34
|
if (!configuredModel || !configuredExists) {
|
|
31
35
|
// Model not configured at all, or removed from registry — pick a fallback.
|
|
32
36
|
// Only fires when the model is genuinely unknown (not just temporarily unavailable).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
NzO79SOz9jHX-VY5-0t2O
|