gsd-pi 2.70.0 → 2.70.1-dev.366a872
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/dist/loader.js +4 -0
- package/dist/resources/extensions/claude-code-cli/stream-adapter.js +270 -23
- package/dist/resources/extensions/get-secrets-from-user.js +17 -1
- package/dist/resources/extensions/gsd/auto-model-selection.js +33 -19
- package/dist/resources/extensions/gsd/auto-prompts.js +7 -3
- package/dist/resources/extensions/gsd/auto-start.js +28 -12
- package/dist/resources/extensions/gsd/auto.js +12 -8
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +4 -0
- package/dist/resources/extensions/gsd/commands-handlers.js +22 -8
- package/dist/resources/extensions/gsd/custom-workflow-engine.js +16 -12
- package/dist/resources/extensions/gsd/doctor-engine-checks.js +12 -0
- package/dist/resources/extensions/gsd/doctor-format.js +2 -0
- package/dist/resources/extensions/gsd/file-lock.js +60 -0
- package/dist/resources/extensions/gsd/guided-flow.js +33 -20
- package/dist/resources/extensions/gsd/init-wizard.js +3 -11
- package/dist/resources/extensions/gsd/pre-execution-checks.js +5 -3
- package/dist/resources/extensions/gsd/prompts/discuss.md +31 -13
- package/dist/resources/extensions/gsd/state.js +234 -332
- package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +34 -0
- package/dist/resources/extensions/gsd/validate-directory.js +30 -12
- package/dist/resources/extensions/gsd/workflow-events.js +25 -13
- package/dist/resources/extensions/gsd/workflow-mcp-auto-prep.js +56 -0
- package/dist/resources/extensions/gsd/workflow-mcp.js +12 -1
- package/dist/resources/extensions/slash-commands/audit.js +2 -1
- package/dist/resources/extensions/subagent/isolation.js +4 -2
- package/dist/update-check.d.ts +1 -0
- package/dist/update-check.js +30 -27
- package/dist/update-cmd.js +3 -11
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +11 -11
- package/dist/web/standalone/.next/build-manifest.json +4 -4
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
- package/dist/web/standalone/.next/required-server-files.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 +3 -3
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
- 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 +3 -3
- 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/boot/route_client-reference-manifest.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/input/route_client-reference-manifest.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/resize/route_client-reference-manifest.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/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.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/dev-mode/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/experimental/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/experimental/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.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/live-state/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/notifications/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/notifications/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.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/remote-questions/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.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/command/route_client-reference-manifest.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/events/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.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/settings-data/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.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/skill-health/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.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/switch-root/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +4 -4
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -4
- 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 +3 -3
- 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 +11 -11
- package/dist/web/standalone/.next/server/chunks/63.js +3 -3
- package/dist/web/standalone/.next/server/chunks/6897.js +1 -1
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware.js +2 -2
- package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
- package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
- 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/static/chunks/2826.dd3dc8bbd3025fa5.js +9 -0
- package/dist/web/standalone/.next/static/chunks/app/_not-found/{page-2f24283c162b6ab3.js → page-f2a7482d42a5614b.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/app/{layout-9ecfd95f343793f0.js → layout-a16c7a7ecdf0c2cf.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/app/page-f1e30ab6bb269149.js +1 -0
- package/dist/web/standalone/.next/static/chunks/main-app-fdab67f7802d7832.js +1 -0
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +1 -0
- package/dist/web/standalone/.next/static/chunks/{webpack-6e4d7e9a4f57bed4.js → webpack-b868033a5834586d.js} +1 -1
- package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
- package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
- package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
- package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api.target.mk +14 -14
- package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_except.target.mk +14 -14
- package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_maybe.target.mk +14 -14
- package/dist/web/standalone/server.js +1 -1
- package/dist/web-mode.js +4 -0
- package/package.json +11 -11
- package/packages/mcp-server/dist/env-writer.d.ts +39 -0
- package/packages/mcp-server/dist/env-writer.d.ts.map +1 -0
- package/packages/mcp-server/dist/env-writer.js +158 -0
- package/packages/mcp-server/dist/env-writer.js.map +1 -0
- package/packages/mcp-server/dist/server.d.ts +11 -2
- package/packages/mcp-server/dist/server.d.ts.map +1 -1
- package/packages/mcp-server/dist/server.js +102 -2
- package/packages/mcp-server/dist/server.js.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.d.ts +2 -0
- package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.js +35 -3
- package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
- package/packages/mcp-server/src/env-writer.test.ts +280 -0
- package/packages/mcp-server/src/env-writer.ts +183 -0
- package/packages/mcp-server/src/import-candidates.test.ts +48 -0
- package/packages/mcp-server/src/secure-env-collect.test.ts +265 -0
- package/packages/mcp-server/src/server.ts +137 -3
- package/packages/mcp-server/src/workflow-tools.ts +34 -1
- package/packages/pi-agent-core/dist/agent.d.ts +8 -0
- package/packages/pi-agent-core/dist/agent.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/agent.js +3 -0
- package/packages/pi-agent-core/dist/agent.js.map +1 -1
- package/packages/pi-agent-core/src/agent.test.ts +82 -0
- package/packages/pi-agent-core/src/agent.ts +12 -0
- package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js +348 -0
- package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/config.d.ts +1 -0
- package/packages/pi-coding-agent/dist/core/lsp/config.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/config.js +38 -15
- package/packages/pi-coding-agent/dist/core/lsp/config.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 +10 -0
- package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.d.ts +19 -2
- package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.js +50 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/extension-input.d.ts +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/extension-input.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/extension-input.js +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/extension-input.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +158 -23
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +6 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +58 -2
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js +3 -1
- package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-types.d.ts +1 -0
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-types.js.map +1 -1
- package/packages/pi-coding-agent/package.json +1 -1
- package/packages/pi-coding-agent/src/core/chat-controller-ordering.test.ts +418 -0
- package/packages/pi-coding-agent/src/core/extensions/types.ts +2 -0
- package/packages/pi-coding-agent/src/core/lsp/config.ts +43 -17
- package/packages/pi-coding-agent/src/core/sdk.ts +8 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/dynamic-border.ts +58 -2
- package/packages/pi-coding-agent/src/modes/interactive/components/extension-input.ts +2 -0
- package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +189 -29
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode-state.ts +1 -0
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +66 -2
- package/packages/pi-coding-agent/src/modes/interactive/slash-command-handlers.ts +7 -5
- package/packages/pi-coding-agent/src/modes/rpc/rpc-mode.ts +1 -1
- package/packages/pi-coding-agent/src/modes/rpc/rpc-types.ts +1 -0
- package/packages/pi-tui/dist/components/__tests__/input.test.js +9 -0
- package/packages/pi-tui/dist/components/__tests__/input.test.js.map +1 -1
- package/packages/pi-tui/dist/components/__tests__/markdown-maxlines.test.d.ts +2 -0
- package/packages/pi-tui/dist/components/__tests__/markdown-maxlines.test.d.ts.map +1 -0
- package/packages/pi-tui/dist/components/__tests__/markdown-maxlines.test.js +66 -0
- package/packages/pi-tui/dist/components/__tests__/markdown-maxlines.test.js.map +1 -0
- package/packages/pi-tui/dist/components/input.d.ts +2 -0
- package/packages/pi-tui/dist/components/input.d.ts.map +1 -1
- package/packages/pi-tui/dist/components/input.js +7 -4
- package/packages/pi-tui/dist/components/input.js.map +1 -1
- package/packages/pi-tui/dist/components/markdown.d.ts +3 -0
- package/packages/pi-tui/dist/components/markdown.d.ts.map +1 -1
- package/packages/pi-tui/dist/components/markdown.js +17 -1
- package/packages/pi-tui/dist/components/markdown.js.map +1 -1
- package/packages/pi-tui/src/components/__tests__/input.test.ts +11 -0
- package/packages/pi-tui/src/components/__tests__/markdown-maxlines.test.ts +75 -0
- package/packages/pi-tui/src/components/input.ts +7 -4
- package/packages/pi-tui/src/components/markdown.ts +22 -1
- package/pkg/package.json +1 -1
- package/src/resources/extensions/claude-code-cli/stream-adapter.ts +384 -24
- package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +317 -0
- package/src/resources/extensions/get-secrets-from-user.ts +24 -1
- package/src/resources/extensions/gsd/auto-model-selection.ts +39 -25
- package/src/resources/extensions/gsd/auto-prompts.ts +7 -3
- package/src/resources/extensions/gsd/auto-start.ts +37 -14
- package/src/resources/extensions/gsd/auto.ts +12 -8
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +4 -0
- package/src/resources/extensions/gsd/commands-handlers.ts +22 -7
- package/src/resources/extensions/gsd/custom-workflow-engine.ts +19 -14
- package/src/resources/extensions/gsd/doctor-engine-checks.ts +14 -0
- package/src/resources/extensions/gsd/doctor-format.ts +1 -0
- package/src/resources/extensions/gsd/doctor-types.ts +1 -0
- package/src/resources/extensions/gsd/file-lock.ts +59 -0
- package/src/resources/extensions/gsd/guided-flow.ts +36 -17
- package/src/resources/extensions/gsd/init-wizard.ts +3 -13
- package/src/resources/extensions/gsd/pre-execution-checks.ts +6 -3
- package/src/resources/extensions/gsd/prompts/discuss.md +31 -13
- package/src/resources/extensions/gsd/state.ts +274 -344
- package/src/resources/extensions/gsd/tests/derive-state-helpers.test.ts +436 -0
- package/src/resources/extensions/gsd/tests/discuss-incremental-persistence.test.ts +9 -0
- package/src/resources/extensions/gsd/tests/doctor-scope-db-unavailable.test.ts +43 -0
- package/src/resources/extensions/gsd/tests/file-lock.test.ts +103 -0
- package/src/resources/extensions/gsd/tests/interactive-routing-bypass.test.ts +207 -0
- package/src/resources/extensions/gsd/tests/pre-exec-backtick-strip.test.ts +48 -1
- package/src/resources/extensions/gsd/tests/resource-loader-import-path.test.ts +8 -7
- package/src/resources/extensions/gsd/tests/secure-env-collect.test.ts +45 -0
- package/src/resources/extensions/gsd/tests/validate-directory.test.ts +33 -1
- package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +87 -1
- package/src/resources/extensions/gsd/tests/workflow-mcp-auto-prep.test.ts +76 -0
- package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +180 -1
- package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +22 -0
- package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +60 -25
- package/src/resources/extensions/gsd/validate-directory.ts +33 -11
- package/src/resources/extensions/gsd/workflow-events.ts +34 -25
- package/src/resources/extensions/gsd/workflow-mcp-auto-prep.ts +76 -0
- package/src/resources/extensions/gsd/workflow-mcp.ts +16 -1
- package/src/resources/extensions/slash-commands/audit.ts +2 -1
- package/src/resources/extensions/subagent/isolation.ts +4 -3
- package/dist/web/standalone/.next/static/chunks/2826.821e01b07d92e948.js +0 -9
- package/dist/web/standalone/.next/static/chunks/app/page-7115e62689b5fd84.js +0 -1
- package/dist/web/standalone/.next/static/chunks/main-app-d3d4c336195465f9.js +0 -1
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ab5a8926e07ec673.js +0 -1
- /package/dist/web/standalone/.next/static/{Nl6lg7zP5dNgNBV1107v1 → eMly3KqfZihJJAHQGmxvO}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{Nl6lg7zP5dNgNBV1107v1 → eMly3KqfZihJJAHQGmxvO}/_ssgManifest.js +0 -0
|
@@ -7,9 +7,13 @@ import {
|
|
|
7
7
|
makeStreamExhaustedErrorMessage,
|
|
8
8
|
buildPromptFromContext,
|
|
9
9
|
buildSdkOptions,
|
|
10
|
+
createClaudeCodeElicitationHandler,
|
|
10
11
|
extractToolResultsFromSdkUserMessage,
|
|
11
12
|
getClaudeLookupCommand,
|
|
13
|
+
parseAskUserQuestionsElicitation,
|
|
14
|
+
parseTextInputElicitation,
|
|
12
15
|
parseClaudeLookupOutput,
|
|
16
|
+
roundResultToElicitationContent,
|
|
13
17
|
} from "../stream-adapter.ts";
|
|
14
18
|
import type { Context, Message } from "@gsd/pi-ai";
|
|
15
19
|
import type { SDKUserMessage } from "../sdk-types.ts";
|
|
@@ -217,6 +221,35 @@ describe("stream-adapter — session persistence (#2859)", () => {
|
|
|
217
221
|
assert.equal(srv.env.GSD_CLI_PATH, "/tmp/gsd");
|
|
218
222
|
assert.equal(srv.env.GSD_PERSIST_WRITE_GATE_STATE, "1");
|
|
219
223
|
assert.equal(srv.env.GSD_WORKFLOW_PROJECT_ROOT, "/tmp/project");
|
|
224
|
+
assert.deepEqual(options.disallowedTools, ["AskUserQuestion"]);
|
|
225
|
+
} finally {
|
|
226
|
+
process.env.GSD_WORKFLOW_MCP_COMMAND = prev.GSD_WORKFLOW_MCP_COMMAND;
|
|
227
|
+
process.env.GSD_WORKFLOW_MCP_NAME = prev.GSD_WORKFLOW_MCP_NAME;
|
|
228
|
+
process.env.GSD_WORKFLOW_MCP_ARGS = prev.GSD_WORKFLOW_MCP_ARGS;
|
|
229
|
+
process.env.GSD_WORKFLOW_MCP_ENV = prev.GSD_WORKFLOW_MCP_ENV;
|
|
230
|
+
process.env.GSD_WORKFLOW_MCP_CWD = prev.GSD_WORKFLOW_MCP_CWD;
|
|
231
|
+
}
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
test("buildSdkOptions disables AskUserQuestion for custom workflow MCP server names", () => {
|
|
235
|
+
const prev = {
|
|
236
|
+
GSD_WORKFLOW_MCP_COMMAND: process.env.GSD_WORKFLOW_MCP_COMMAND,
|
|
237
|
+
GSD_WORKFLOW_MCP_NAME: process.env.GSD_WORKFLOW_MCP_NAME,
|
|
238
|
+
GSD_WORKFLOW_MCP_ARGS: process.env.GSD_WORKFLOW_MCP_ARGS,
|
|
239
|
+
GSD_WORKFLOW_MCP_ENV: process.env.GSD_WORKFLOW_MCP_ENV,
|
|
240
|
+
GSD_WORKFLOW_MCP_CWD: process.env.GSD_WORKFLOW_MCP_CWD,
|
|
241
|
+
};
|
|
242
|
+
try {
|
|
243
|
+
process.env.GSD_WORKFLOW_MCP_COMMAND = "node";
|
|
244
|
+
process.env.GSD_WORKFLOW_MCP_NAME = "custom-workflow";
|
|
245
|
+
process.env.GSD_WORKFLOW_MCP_ARGS = JSON.stringify(["packages/mcp-server/dist/cli.js"]);
|
|
246
|
+
process.env.GSD_WORKFLOW_MCP_ENV = JSON.stringify({ GSD_CLI_PATH: "/tmp/gsd" });
|
|
247
|
+
process.env.GSD_WORKFLOW_MCP_CWD = "/tmp/project";
|
|
248
|
+
|
|
249
|
+
const options = buildSdkOptions("claude-sonnet-4-20250514", "test");
|
|
250
|
+
const mcpServers = options.mcpServers as Record<string, any>;
|
|
251
|
+
assert.ok(mcpServers?.["custom-workflow"], "expected custom workflow server config");
|
|
252
|
+
assert.deepEqual(options.disallowedTools, ["AskUserQuestion"]);
|
|
220
253
|
} finally {
|
|
221
254
|
process.env.GSD_WORKFLOW_MCP_COMMAND = prev.GSD_WORKFLOW_MCP_COMMAND;
|
|
222
255
|
process.env.GSD_WORKFLOW_MCP_NAME = prev.GSD_WORKFLOW_MCP_NAME;
|
|
@@ -252,6 +285,9 @@ describe("stream-adapter — session persistence (#2859)", () => {
|
|
|
252
285
|
const mcpServers = (options as any).mcpServers;
|
|
253
286
|
if (mcpServers) {
|
|
254
287
|
assert.ok(mcpServers["gsd-workflow"], "if present, must be gsd-workflow");
|
|
288
|
+
assert.deepEqual((options as any).disallowedTools, ["AskUserQuestion"]);
|
|
289
|
+
} else {
|
|
290
|
+
assert.deepEqual((options as any).disallowedTools, ["AskUserQuestion"]);
|
|
255
291
|
}
|
|
256
292
|
rmSync(emptyDir, { recursive: true, force: true });
|
|
257
293
|
} finally {
|
|
@@ -298,6 +334,7 @@ describe("stream-adapter — session persistence (#2859)", () => {
|
|
|
298
334
|
assert.equal(srv.env.GSD_CLI_PATH, "/tmp/gsd");
|
|
299
335
|
assert.equal(srv.env.GSD_PERSIST_WRITE_GATE_STATE, "1");
|
|
300
336
|
assert.equal(srv.env.GSD_WORKFLOW_PROJECT_ROOT, resolvedRepoDir);
|
|
337
|
+
assert.deepEqual(options.disallowedTools, ["AskUserQuestion"]);
|
|
301
338
|
} finally {
|
|
302
339
|
process.chdir(originalCwd);
|
|
303
340
|
rmSync(repoDir, { recursive: true, force: true });
|
|
@@ -309,6 +346,286 @@ describe("stream-adapter — session persistence (#2859)", () => {
|
|
|
309
346
|
process.env.GSD_CLI_PATH = prev.GSD_CLI_PATH;
|
|
310
347
|
}
|
|
311
348
|
});
|
|
349
|
+
|
|
350
|
+
test("buildSdkOptions preserves runtime callbacks such as onElicitation", () => {
|
|
351
|
+
const prev = {
|
|
352
|
+
GSD_WORKFLOW_MCP_COMMAND: process.env.GSD_WORKFLOW_MCP_COMMAND,
|
|
353
|
+
GSD_WORKFLOW_MCP_NAME: process.env.GSD_WORKFLOW_MCP_NAME,
|
|
354
|
+
GSD_WORKFLOW_MCP_ARGS: process.env.GSD_WORKFLOW_MCP_ARGS,
|
|
355
|
+
GSD_WORKFLOW_MCP_ENV: process.env.GSD_WORKFLOW_MCP_ENV,
|
|
356
|
+
GSD_WORKFLOW_MCP_CWD: process.env.GSD_WORKFLOW_MCP_CWD,
|
|
357
|
+
};
|
|
358
|
+
const onElicitation = async () => ({ action: "decline" as const });
|
|
359
|
+
try {
|
|
360
|
+
delete process.env.GSD_WORKFLOW_MCP_COMMAND;
|
|
361
|
+
delete process.env.GSD_WORKFLOW_MCP_NAME;
|
|
362
|
+
delete process.env.GSD_WORKFLOW_MCP_ARGS;
|
|
363
|
+
delete process.env.GSD_WORKFLOW_MCP_ENV;
|
|
364
|
+
delete process.env.GSD_WORKFLOW_MCP_CWD;
|
|
365
|
+
const options = buildSdkOptions("claude-sonnet-4-20250514", "test", { onElicitation });
|
|
366
|
+
assert.equal(options.onElicitation, onElicitation);
|
|
367
|
+
} finally {
|
|
368
|
+
process.env.GSD_WORKFLOW_MCP_COMMAND = prev.GSD_WORKFLOW_MCP_COMMAND;
|
|
369
|
+
process.env.GSD_WORKFLOW_MCP_NAME = prev.GSD_WORKFLOW_MCP_NAME;
|
|
370
|
+
process.env.GSD_WORKFLOW_MCP_ARGS = prev.GSD_WORKFLOW_MCP_ARGS;
|
|
371
|
+
process.env.GSD_WORKFLOW_MCP_ENV = prev.GSD_WORKFLOW_MCP_ENV;
|
|
372
|
+
process.env.GSD_WORKFLOW_MCP_CWD = prev.GSD_WORKFLOW_MCP_CWD;
|
|
373
|
+
}
|
|
374
|
+
});
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
describe("stream-adapter — MCP elicitation bridge", () => {
|
|
378
|
+
const askUserQuestionsRequest = {
|
|
379
|
+
serverName: "gsd-workflow",
|
|
380
|
+
message: "Please answer the following question(s).",
|
|
381
|
+
mode: "form" as const,
|
|
382
|
+
requestedSchema: {
|
|
383
|
+
type: "object" as const,
|
|
384
|
+
properties: {
|
|
385
|
+
storage_scope: {
|
|
386
|
+
type: "string",
|
|
387
|
+
title: "Storage",
|
|
388
|
+
description: "Does this app need to sync across devices?",
|
|
389
|
+
oneOf: [
|
|
390
|
+
{ const: "Local-only (Recommended)", title: "Local-only (Recommended)" },
|
|
391
|
+
{ const: "Cloud-synced", title: "Cloud-synced" },
|
|
392
|
+
{ const: "None of the above", title: "None of the above" },
|
|
393
|
+
],
|
|
394
|
+
},
|
|
395
|
+
storage_scope__note: {
|
|
396
|
+
type: "string",
|
|
397
|
+
title: "Storage Note",
|
|
398
|
+
description: "Optional note for None of the above.",
|
|
399
|
+
},
|
|
400
|
+
platform: {
|
|
401
|
+
type: "array",
|
|
402
|
+
title: "Platform",
|
|
403
|
+
description: "Where should it run?",
|
|
404
|
+
items: {
|
|
405
|
+
anyOf: [
|
|
406
|
+
{ const: "Web", title: "Web" },
|
|
407
|
+
{ const: "Desktop", title: "Desktop" },
|
|
408
|
+
{ const: "Mobile", title: "Mobile" },
|
|
409
|
+
],
|
|
410
|
+
},
|
|
411
|
+
},
|
|
412
|
+
},
|
|
413
|
+
},
|
|
414
|
+
};
|
|
415
|
+
|
|
416
|
+
test("parseAskUserQuestionsElicitation rebuilds interview questions from the MCP schema", () => {
|
|
417
|
+
const questions = parseAskUserQuestionsElicitation(askUserQuestionsRequest);
|
|
418
|
+
assert.deepEqual(questions, [
|
|
419
|
+
{
|
|
420
|
+
id: "storage_scope",
|
|
421
|
+
header: "Storage",
|
|
422
|
+
question: "Does this app need to sync across devices?",
|
|
423
|
+
options: [
|
|
424
|
+
{ label: "Local-only (Recommended)", description: "" },
|
|
425
|
+
{ label: "Cloud-synced", description: "" },
|
|
426
|
+
],
|
|
427
|
+
noteFieldId: "storage_scope__note",
|
|
428
|
+
},
|
|
429
|
+
{
|
|
430
|
+
id: "platform",
|
|
431
|
+
header: "Platform",
|
|
432
|
+
question: "Where should it run?",
|
|
433
|
+
options: [
|
|
434
|
+
{ label: "Web", description: "" },
|
|
435
|
+
{ label: "Desktop", description: "" },
|
|
436
|
+
{ label: "Mobile", description: "" },
|
|
437
|
+
],
|
|
438
|
+
allowMultiple: true,
|
|
439
|
+
},
|
|
440
|
+
]);
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
test("roundResultToElicitationContent preserves notes for None of the above", () => {
|
|
444
|
+
const questions = parseAskUserQuestionsElicitation(askUserQuestionsRequest);
|
|
445
|
+
assert.ok(questions);
|
|
446
|
+
|
|
447
|
+
const content = roundResultToElicitationContent(questions, {
|
|
448
|
+
endInterview: false,
|
|
449
|
+
answers: {
|
|
450
|
+
storage_scope: {
|
|
451
|
+
selected: "None of the above",
|
|
452
|
+
notes: "Needs selective sync later",
|
|
453
|
+
},
|
|
454
|
+
platform: {
|
|
455
|
+
selected: ["Web", "Desktop"],
|
|
456
|
+
notes: "",
|
|
457
|
+
},
|
|
458
|
+
},
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
assert.deepEqual(content, {
|
|
462
|
+
storage_scope: "None of the above",
|
|
463
|
+
storage_scope__note: "Needs selective sync later",
|
|
464
|
+
platform: ["Web", "Desktop"],
|
|
465
|
+
});
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
test("createClaudeCodeElicitationHandler accepts interview-style answers from custom UI", async () => {
|
|
469
|
+
const handler = createClaudeCodeElicitationHandler({
|
|
470
|
+
custom: async (_factory: any) => ({
|
|
471
|
+
endInterview: false,
|
|
472
|
+
answers: {
|
|
473
|
+
storage_scope: {
|
|
474
|
+
selected: "Cloud-synced",
|
|
475
|
+
notes: "",
|
|
476
|
+
},
|
|
477
|
+
platform: {
|
|
478
|
+
selected: ["Web", "Mobile"],
|
|
479
|
+
notes: "",
|
|
480
|
+
},
|
|
481
|
+
},
|
|
482
|
+
}),
|
|
483
|
+
} as any);
|
|
484
|
+
|
|
485
|
+
assert.ok(handler);
|
|
486
|
+
const result = await handler!(askUserQuestionsRequest, { signal: new AbortController().signal });
|
|
487
|
+
assert.deepEqual(result, {
|
|
488
|
+
action: "accept",
|
|
489
|
+
content: {
|
|
490
|
+
storage_scope: "Cloud-synced",
|
|
491
|
+
platform: ["Web", "Mobile"],
|
|
492
|
+
},
|
|
493
|
+
});
|
|
494
|
+
});
|
|
495
|
+
|
|
496
|
+
test("createClaudeCodeElicitationHandler falls back to dialog prompts when custom UI is unavailable", async () => {
|
|
497
|
+
const ui = {
|
|
498
|
+
custom: async () => undefined,
|
|
499
|
+
select: async (_title: string, options: string[], opts?: { allowMultiple?: boolean }) => {
|
|
500
|
+
if (opts?.allowMultiple) return ["Desktop", "Mobile"];
|
|
501
|
+
return options.includes("None of the above") ? "None of the above" : options[0];
|
|
502
|
+
},
|
|
503
|
+
input: async () => "CLI-only deployment target",
|
|
504
|
+
};
|
|
505
|
+
const handler = createClaudeCodeElicitationHandler(ui as any);
|
|
506
|
+
assert.ok(handler);
|
|
507
|
+
|
|
508
|
+
const result = await handler!(askUserQuestionsRequest, { signal: new AbortController().signal });
|
|
509
|
+
assert.deepEqual(result, {
|
|
510
|
+
action: "accept",
|
|
511
|
+
content: {
|
|
512
|
+
storage_scope: "None of the above",
|
|
513
|
+
storage_scope__note: "CLI-only deployment target",
|
|
514
|
+
platform: ["Desktop", "Mobile"],
|
|
515
|
+
},
|
|
516
|
+
});
|
|
517
|
+
});
|
|
518
|
+
|
|
519
|
+
test("parseTextInputElicitation recognizes secure free-text MCP forms", () => {
|
|
520
|
+
const request = {
|
|
521
|
+
serverName: "gsd-workflow",
|
|
522
|
+
message: "Enter values for environment variables.",
|
|
523
|
+
mode: "form" as const,
|
|
524
|
+
requestedSchema: {
|
|
525
|
+
type: "object" as const,
|
|
526
|
+
properties: {
|
|
527
|
+
TEST_PASSWORD: {
|
|
528
|
+
type: "string",
|
|
529
|
+
title: "TEST_PASSWORD",
|
|
530
|
+
description: "Format: min 8 characters\nLeave empty to skip.",
|
|
531
|
+
},
|
|
532
|
+
PROJECT_NAME: {
|
|
533
|
+
type: "string",
|
|
534
|
+
title: "PROJECT_NAME",
|
|
535
|
+
description: "Human-readable project name.",
|
|
536
|
+
},
|
|
537
|
+
},
|
|
538
|
+
},
|
|
539
|
+
};
|
|
540
|
+
|
|
541
|
+
const parsed = parseTextInputElicitation(request as any);
|
|
542
|
+
assert.deepEqual(parsed, [
|
|
543
|
+
{
|
|
544
|
+
id: "TEST_PASSWORD",
|
|
545
|
+
title: "TEST_PASSWORD",
|
|
546
|
+
description: "Format: min 8 characters\nLeave empty to skip.",
|
|
547
|
+
required: false,
|
|
548
|
+
secure: true,
|
|
549
|
+
},
|
|
550
|
+
{
|
|
551
|
+
id: "PROJECT_NAME",
|
|
552
|
+
title: "PROJECT_NAME",
|
|
553
|
+
description: "Human-readable project name.",
|
|
554
|
+
required: false,
|
|
555
|
+
secure: false,
|
|
556
|
+
},
|
|
557
|
+
]);
|
|
558
|
+
});
|
|
559
|
+
|
|
560
|
+
test("parseTextInputElicitation accepts legacy keys schema and skips unsupported fields", () => {
|
|
561
|
+
const request = {
|
|
562
|
+
serverName: "gsd-workflow",
|
|
563
|
+
message: "Enter secure values",
|
|
564
|
+
mode: "form" as const,
|
|
565
|
+
requestedSchema: {
|
|
566
|
+
type: "object" as const,
|
|
567
|
+
keys: {
|
|
568
|
+
API_TOKEN: {
|
|
569
|
+
type: "string",
|
|
570
|
+
title: "API_TOKEN",
|
|
571
|
+
description: "Leave empty to skip.",
|
|
572
|
+
},
|
|
573
|
+
META: {
|
|
574
|
+
type: "object",
|
|
575
|
+
title: "metadata",
|
|
576
|
+
},
|
|
577
|
+
},
|
|
578
|
+
},
|
|
579
|
+
};
|
|
580
|
+
|
|
581
|
+
const parsed = parseTextInputElicitation(request as any);
|
|
582
|
+
assert.deepEqual(parsed, [
|
|
583
|
+
{
|
|
584
|
+
id: "API_TOKEN",
|
|
585
|
+
title: "API_TOKEN",
|
|
586
|
+
description: "Leave empty to skip.",
|
|
587
|
+
required: false,
|
|
588
|
+
secure: true,
|
|
589
|
+
},
|
|
590
|
+
]);
|
|
591
|
+
});
|
|
592
|
+
|
|
593
|
+
test("createClaudeCodeElicitationHandler collects secure_env_collect fields through input dialogs", async () => {
|
|
594
|
+
const secureRequest = {
|
|
595
|
+
serverName: "gsd-workflow",
|
|
596
|
+
message: "Enter values for environment variables.",
|
|
597
|
+
mode: "form" as const,
|
|
598
|
+
requestedSchema: {
|
|
599
|
+
type: "object" as const,
|
|
600
|
+
properties: {
|
|
601
|
+
TEST_PASSWORD: {
|
|
602
|
+
type: "string",
|
|
603
|
+
title: "TEST_PASSWORD",
|
|
604
|
+
description: "Format: Your secure testing password\nLeave empty to skip.",
|
|
605
|
+
},
|
|
606
|
+
},
|
|
607
|
+
},
|
|
608
|
+
};
|
|
609
|
+
|
|
610
|
+
const inputCalls: Array<{ opts?: { secure?: boolean } }> = [];
|
|
611
|
+
const handler = createClaudeCodeElicitationHandler({
|
|
612
|
+
input: async (_title: string, _placeholder?: string, opts?: { secure?: boolean }) => {
|
|
613
|
+
inputCalls.push({ opts });
|
|
614
|
+
return "super-secret";
|
|
615
|
+
},
|
|
616
|
+
} as any);
|
|
617
|
+
assert.ok(handler);
|
|
618
|
+
|
|
619
|
+
const result = await handler!(secureRequest as any, { signal: new AbortController().signal });
|
|
620
|
+
assert.deepEqual(result, {
|
|
621
|
+
action: "accept",
|
|
622
|
+
content: {
|
|
623
|
+
TEST_PASSWORD: "super-secret",
|
|
624
|
+
},
|
|
625
|
+
});
|
|
626
|
+
assert.equal(inputCalls.length, 1);
|
|
627
|
+
assert.equal(inputCalls[0]?.opts?.secure, true, "secure_env_collect fields should request secure input");
|
|
628
|
+
});
|
|
312
629
|
});
|
|
313
630
|
|
|
314
631
|
describe("stream-adapter — Windows Claude path lookup (#3770)", () => {
|
|
@@ -126,7 +126,7 @@ async function collectOneSecret(
|
|
|
126
126
|
): Promise<string | null> {
|
|
127
127
|
if (!ctx.hasUI) return null;
|
|
128
128
|
|
|
129
|
-
|
|
129
|
+
const customResult = await ctx.ui.custom((tui: any, theme: any, _kb: any, done: (r: string | null) => void) => {
|
|
130
130
|
let value = "";
|
|
131
131
|
let cachedLines: string[] | undefined;
|
|
132
132
|
|
|
@@ -223,6 +223,29 @@ async function collectOneSecret(
|
|
|
223
223
|
handleInput,
|
|
224
224
|
};
|
|
225
225
|
});
|
|
226
|
+
|
|
227
|
+
// RPC/web surfaces may not implement ctx.ui.custom(). Fall back to a
|
|
228
|
+
// standard input prompt so users can still provide the secret.
|
|
229
|
+
if (customResult !== undefined) {
|
|
230
|
+
return customResult;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (typeof ctx.ui?.input !== "function") {
|
|
234
|
+
return null;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const inputTitle = `Secure value for ${keyName} (${pageIndex + 1}/${totalPages})`;
|
|
238
|
+
const inputPlaceholder = hint || "Enter secret value";
|
|
239
|
+
const inputResult = await ctx.ui.input(
|
|
240
|
+
inputTitle,
|
|
241
|
+
inputPlaceholder,
|
|
242
|
+
{ secure: true },
|
|
243
|
+
);
|
|
244
|
+
if (typeof inputResult !== "string") {
|
|
245
|
+
return null;
|
|
246
|
+
}
|
|
247
|
+
const trimmed = inputResult.trim();
|
|
248
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
226
249
|
}
|
|
227
250
|
|
|
228
251
|
/**
|
|
@@ -25,10 +25,17 @@ export interface ModelSelectionResult {
|
|
|
25
25
|
export function resolvePreferredModelConfig(
|
|
26
26
|
unitType: string,
|
|
27
27
|
autoModeStartModel: { provider: string; id: string } | null,
|
|
28
|
+
/** When false, only return explicit per-phase model configs — do not
|
|
29
|
+
* synthesize a routing ceiling from dynamic_routing.tier_models (#3962). */
|
|
30
|
+
isAutoMode = true,
|
|
28
31
|
) {
|
|
29
32
|
const explicitConfig = resolveModelWithFallbacksForUnit(unitType);
|
|
30
33
|
if (explicitConfig) return explicitConfig;
|
|
31
34
|
|
|
35
|
+
// In interactive mode, don't synthesize a routing-based model config.
|
|
36
|
+
// The user's session model (/model) should be used as-is (#3962).
|
|
37
|
+
if (!isAutoMode) return undefined;
|
|
38
|
+
|
|
32
39
|
const routingConfig = resolveDynamicRoutingConfig();
|
|
33
40
|
if (!routingConfig.enabled || !routingConfig.tier_models) return undefined;
|
|
34
41
|
|
|
@@ -62,8 +69,11 @@ export async function selectAndApplyModel(
|
|
|
62
69
|
verbose: boolean,
|
|
63
70
|
autoModeStartModel: { provider: string; id: string } | null,
|
|
64
71
|
retryContext?: { isRetry: boolean; previousTier?: string },
|
|
72
|
+
/** When false (interactive/guided-flow), skip dynamic routing and use the session model.
|
|
73
|
+
* Dynamic routing only applies in auto-mode where cost optimization is expected. (#3962) */
|
|
74
|
+
isAutoMode = true,
|
|
65
75
|
): Promise<ModelSelectionResult> {
|
|
66
|
-
const modelConfig = resolvePreferredModelConfig(unitType, autoModeStartModel);
|
|
76
|
+
const modelConfig = resolvePreferredModelConfig(unitType, autoModeStartModel, isAutoMode);
|
|
67
77
|
let routing: { tier: string; modelDowngraded: boolean } | null = null;
|
|
68
78
|
let appliedModel: Model<Api> | null = null;
|
|
69
79
|
|
|
@@ -71,7 +81,13 @@ export async function selectAndApplyModel(
|
|
|
71
81
|
const availableModels = ctx.modelRegistry.getAvailable();
|
|
72
82
|
|
|
73
83
|
// ─── Dynamic Model Routing ─────────────────────────────────────────
|
|
84
|
+
// Dynamic routing (complexity-based downgrading) only applies in auto-mode.
|
|
85
|
+
// Interactive/guided-flow dispatches use the user's session model directly,
|
|
86
|
+
// respecting their /model selection without silent downgrades (#3962).
|
|
74
87
|
const routingConfig = resolveDynamicRoutingConfig();
|
|
88
|
+
if (!isAutoMode) {
|
|
89
|
+
routingConfig.enabled = false;
|
|
90
|
+
}
|
|
75
91
|
let effectiveModelConfig = modelConfig;
|
|
76
92
|
let routingTierLabel = "";
|
|
77
93
|
|
|
@@ -123,12 +139,11 @@ export async function selectAndApplyModel(
|
|
|
123
139
|
const escalated = escalateTier(retryContext.previousTier as ComplexityTier);
|
|
124
140
|
if (escalated) {
|
|
125
141
|
classification = { ...classification, tier: escalated, reason: "escalated after failure" };
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
}
|
|
142
|
+
// Always notify on tier escalation — model changes should be visible (#3962)
|
|
143
|
+
ctx.ui.notify(
|
|
144
|
+
`Tier escalation: ${retryContext.previousTier} → ${escalated} (retry after failure)`,
|
|
145
|
+
"info",
|
|
146
|
+
);
|
|
132
147
|
}
|
|
133
148
|
}
|
|
134
149
|
|
|
@@ -195,24 +210,23 @@ export async function selectAndApplyModel(
|
|
|
195
210
|
primary: routingResult.modelId,
|
|
196
211
|
fallbacks: routingResult.fallbacks,
|
|
197
212
|
};
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
}
|
|
213
|
+
// Always notify on model downgrade — users should see when their
|
|
214
|
+
// model selection is overridden, not just in verbose mode (#3962).
|
|
215
|
+
if (routingResult.selectionMethod === "capability-scored" && routingResult.capabilityScores) {
|
|
216
|
+
const tierLbl = tierLabel(classification.tier);
|
|
217
|
+
const scores = Object.entries(routingResult.capabilityScores)
|
|
218
|
+
.sort(([, a], [, b]) => b - a)
|
|
219
|
+
.map(([id, score]) => `${id}: ${score.toFixed(1)}`)
|
|
220
|
+
.join(", ");
|
|
221
|
+
ctx.ui.notify(
|
|
222
|
+
`Dynamic routing [${tierLbl}]: ${routingResult.modelId} (capability-scored) — ${scores}`,
|
|
223
|
+
"info",
|
|
224
|
+
);
|
|
225
|
+
} else {
|
|
226
|
+
ctx.ui.notify(
|
|
227
|
+
`Dynamic routing [${tierLabel(classification.tier)}]: ${routingResult.modelId} (${classification.reason})`,
|
|
228
|
+
"info",
|
|
229
|
+
);
|
|
216
230
|
}
|
|
217
231
|
}
|
|
218
232
|
routingTierLabel = ` [${tierLabel(classification.tier)}]`;
|
|
@@ -997,7 +997,7 @@ export async function buildDiscussMilestonePrompt(mid: string, midTitle: string,
|
|
|
997
997
|
milestoneId: mid,
|
|
998
998
|
milestoneTitle: midTitle,
|
|
999
999
|
inlinedTemplates: discussTemplates,
|
|
1000
|
-
structuredQuestionsAvailable: "
|
|
1000
|
+
structuredQuestionsAvailable: "false",
|
|
1001
1001
|
commitInstruction: "Do not commit planning artifacts — .gsd/ is managed externally.",
|
|
1002
1002
|
fastPathInstruction: "",
|
|
1003
1003
|
});
|
|
@@ -1503,7 +1503,9 @@ export async function buildCompleteMilestonePrompt(
|
|
|
1503
1503
|
try {
|
|
1504
1504
|
const { isDbAvailable, getMilestoneSlices } = await import("./gsd-db.js");
|
|
1505
1505
|
if (isDbAvailable()) {
|
|
1506
|
-
sliceIds = getMilestoneSlices(mid)
|
|
1506
|
+
sliceIds = getMilestoneSlices(mid)
|
|
1507
|
+
.filter(s => s.status !== "skipped")
|
|
1508
|
+
.map(s => s.id);
|
|
1507
1509
|
}
|
|
1508
1510
|
} catch (err) {
|
|
1509
1511
|
logWarning("prompt", `buildCompleteMilestonePrompt DB lookup failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -1597,7 +1599,9 @@ export async function buildValidateMilestonePrompt(
|
|
|
1597
1599
|
try {
|
|
1598
1600
|
const { isDbAvailable, getMilestoneSlices } = await import("./gsd-db.js");
|
|
1599
1601
|
if (isDbAvailable()) {
|
|
1600
|
-
valSliceIds = getMilestoneSlices(mid)
|
|
1602
|
+
valSliceIds = getMilestoneSlices(mid)
|
|
1603
|
+
.filter(s => s.status !== "skipped")
|
|
1604
|
+
.map(s => s.id);
|
|
1601
1605
|
}
|
|
1602
1606
|
} catch (err) {
|
|
1603
1607
|
logWarning("prompt", `buildValidateMilestonePrompt slice IDs lookup failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -83,7 +83,7 @@ import { join } from "node:path";
|
|
|
83
83
|
import { sep as pathSep } from "node:path";
|
|
84
84
|
|
|
85
85
|
import { resolveProjectRootDbPath } from "./bootstrap/dynamic-tools.js";
|
|
86
|
-
import { resolveDefaultSessionModel } from "./preferences-models.js";
|
|
86
|
+
import { resolveDefaultSessionModel, resolveDynamicRoutingConfig } from "./preferences-models.js";
|
|
87
87
|
import type { WorktreeResolver } from "./worktree-resolver.js";
|
|
88
88
|
|
|
89
89
|
export interface BootstrapDeps {
|
|
@@ -335,19 +335,9 @@ export async function bootstrapAutoSession(
|
|
|
335
335
|
}
|
|
336
336
|
}
|
|
337
337
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
const result = ensureProjectWorkflowMcpConfig(base);
|
|
342
|
-
if (result.status !== "unchanged") {
|
|
343
|
-
ctx.ui.notify(`Claude Code MCP prepared at ${result.configPath}`, "info");
|
|
344
|
-
}
|
|
345
|
-
} catch (err) {
|
|
346
|
-
ctx.ui.notify(
|
|
347
|
-
`Claude Code MCP prep failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
348
|
-
"warning",
|
|
349
|
-
);
|
|
350
|
-
}
|
|
338
|
+
{
|
|
339
|
+
const { prepareWorkflowMcpForProject } = await import("./workflow-mcp-auto-prep.js");
|
|
340
|
+
prepareWorkflowMcpForProject(ctx, base);
|
|
351
341
|
}
|
|
352
342
|
|
|
353
343
|
// Initialize GitServiceImpl
|
|
@@ -778,6 +768,39 @@ export async function bootstrapAutoSession(
|
|
|
778
768
|
: "Will loop until milestone complete.";
|
|
779
769
|
ctx.ui.notify(`${modeLabel} started. ${scopeMsg}`, "info");
|
|
780
770
|
|
|
771
|
+
// Show dynamic routing status so users know upfront if models will be
|
|
772
|
+
// downgraded for simple tasks (#3962).
|
|
773
|
+
// Use the same effective logic as selectAndApplyModel: check flat-rate
|
|
774
|
+
// provider suppression and resolve the actual ceiling model.
|
|
775
|
+
const routingConfig = resolveDynamicRoutingConfig();
|
|
776
|
+
const startModelLabel = s.autoModeStartModel
|
|
777
|
+
? `${s.autoModeStartModel.provider}/${s.autoModeStartModel.id}`
|
|
778
|
+
: ctx.model ? `${ctx.model.provider}/${ctx.model.id}` : "default";
|
|
779
|
+
|
|
780
|
+
// Flat-rate providers (e.g. GitHub Copilot, claude-code) suppress routing
|
|
781
|
+
// at dispatch time (#3453) — reflect that in the banner.
|
|
782
|
+
const { isFlatRateProvider } = await import("./auto-model-selection.js");
|
|
783
|
+
const effectiveProvider = s.autoModeStartModel?.provider ?? ctx.model?.provider;
|
|
784
|
+
const effectivelyEnabled = routingConfig.enabled
|
|
785
|
+
&& !(effectiveProvider && isFlatRateProvider(effectiveProvider));
|
|
786
|
+
|
|
787
|
+
// The actual ceiling may come from tier_models.heavy, not the start model.
|
|
788
|
+
const effectiveCeiling = (routingConfig.enabled && routingConfig.tier_models?.heavy)
|
|
789
|
+
? routingConfig.tier_models.heavy
|
|
790
|
+
: startModelLabel;
|
|
791
|
+
|
|
792
|
+
if (effectivelyEnabled) {
|
|
793
|
+
ctx.ui.notify(
|
|
794
|
+
`Dynamic routing: enabled — simple tasks may use cheaper models (ceiling: ${effectiveCeiling})`,
|
|
795
|
+
"info",
|
|
796
|
+
);
|
|
797
|
+
} else {
|
|
798
|
+
ctx.ui.notify(
|
|
799
|
+
`Dynamic routing: disabled — all tasks will use ${startModelLabel}`,
|
|
800
|
+
"info",
|
|
801
|
+
);
|
|
802
|
+
}
|
|
803
|
+
|
|
781
804
|
updateSessionLock(
|
|
782
805
|
lockBase(),
|
|
783
806
|
"starting",
|
|
@@ -125,9 +125,9 @@ import {
|
|
|
125
125
|
} from "./metrics.js";
|
|
126
126
|
import { setLogBasePath, logWarning, logError } from "./workflow-logger.js";
|
|
127
127
|
import { homedir } from "node:os";
|
|
128
|
-
import { join
|
|
128
|
+
import { join } from "node:path";
|
|
129
|
+
import { pathToFileURL } from "node:url";
|
|
129
130
|
import { readFileSync, existsSync, mkdirSync, writeFileSync, unlinkSync } from "node:fs";
|
|
130
|
-
import { createRequire } from "node:module";
|
|
131
131
|
import { atomicWriteSync } from "./atomic-write.js";
|
|
132
132
|
import {
|
|
133
133
|
autoCommitCurrentBranch,
|
|
@@ -1334,13 +1334,17 @@ export async function startAuto(
|
|
|
1334
1334
|
restoreHookState(s.basePath);
|
|
1335
1335
|
// Re-sync managed resources on resume so long-lived auto sessions pick up
|
|
1336
1336
|
// bundled extension updates before resume-time verification/state logic runs.
|
|
1337
|
+
// GSD_PKG_ROOT is set by loader.ts and points to the gsd-pi package root.
|
|
1338
|
+
// The relative import ("../../../resource-loader.js") only works from the source
|
|
1339
|
+
// tree; deployed extensions live at ~/.gsd/agent/extensions/gsd/ where the
|
|
1340
|
+
// relative path resolves to ~/.gsd/agent/resource-loader.js which doesn't exist.
|
|
1341
|
+
// Using GSD_PKG_ROOT constructs a correct absolute path in both contexts (#3949).
|
|
1337
1342
|
const agentDir = process.env.GSD_CODING_AGENT_DIR || join(process.env.GSD_HOME || homedir(), ".gsd", "agent");
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
const
|
|
1343
|
-
const { initResources } = await import(join(pkgRoot, "dist", "resource-loader.js"));
|
|
1343
|
+
const pkgRoot = process.env.GSD_PKG_ROOT;
|
|
1344
|
+
const resourceLoaderPath = pkgRoot
|
|
1345
|
+
? pathToFileURL(join(pkgRoot, "dist", "resource-loader.js")).href
|
|
1346
|
+
: new URL("../../../resource-loader.js", import.meta.url).href;
|
|
1347
|
+
const { initResources } = await import(resourceLoaderPath);
|
|
1344
1348
|
initResources(agentDir);
|
|
1345
1349
|
// Open the project DB before rebuild/derive so resume uses DB-backed
|
|
1346
1350
|
// state instead of falling back to stale markdown parsing (#2940).
|
|
@@ -45,6 +45,8 @@ export function registerHooks(pi: ExtensionAPI): void {
|
|
|
45
45
|
resetToolCallLoopGuard();
|
|
46
46
|
resetAskUserQuestionsCache();
|
|
47
47
|
await syncServiceTierStatus(ctx);
|
|
48
|
+
const { prepareWorkflowMcpForProject } = await import("../workflow-mcp-auto-prep.js");
|
|
49
|
+
prepareWorkflowMcpForProject(ctx, process.cwd());
|
|
48
50
|
|
|
49
51
|
// Apply show_token_cost preference (#1515)
|
|
50
52
|
try {
|
|
@@ -85,6 +87,8 @@ export function registerHooks(pi: ExtensionAPI): void {
|
|
|
85
87
|
resetAskUserQuestionsCache();
|
|
86
88
|
clearDiscussionFlowState();
|
|
87
89
|
await syncServiceTierStatus(ctx);
|
|
90
|
+
const { prepareWorkflowMcpForProject } = await import("../workflow-mcp-auto-prep.js");
|
|
91
|
+
prepareWorkflowMcpForProject(ctx, process.cwd());
|
|
88
92
|
loadToolApiKeys();
|
|
89
93
|
});
|
|
90
94
|
|