gsd-pi 2.70.1 → 2.71.0-dev.06b86c6
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 +24 -17
- package/dist/cli.js +12 -3
- package/dist/mcp-server.js +6 -6
- package/dist/provider-migrations.d.ts +10 -0
- package/dist/provider-migrations.js +12 -0
- package/dist/resource-loader.js +136 -13
- package/dist/resources/GSD-WORKFLOW.md +1 -1
- package/dist/resources/extensions/claude-code-cli/stream-adapter.js +129 -30
- package/dist/resources/extensions/get-secrets-from-user.js +17 -1
- package/dist/resources/extensions/gsd/auto-start.js +4 -12
- package/dist/resources/extensions/gsd/auto-tool-tracking.js +1 -1
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +6 -0
- package/dist/resources/extensions/gsd/bootstrap/system-context.js +6 -0
- package/dist/resources/extensions/gsd/commands/context.js +15 -6
- package/dist/resources/extensions/gsd/commands/dispatcher.js +12 -2
- package/dist/resources/extensions/gsd/custom-workflow-engine.js +16 -12
- package/dist/resources/extensions/gsd/dispatch-guard.js +18 -1
- package/dist/resources/extensions/gsd/error-classifier.js +1 -1
- package/dist/resources/extensions/gsd/file-lock.js +60 -0
- package/dist/resources/extensions/gsd/guided-flow.js +12 -10
- package/dist/resources/extensions/gsd/init-wizard.js +3 -11
- package/dist/resources/extensions/gsd/notification-store.js +21 -1
- package/dist/resources/extensions/gsd/notification-widget.js +1 -1
- package/dist/resources/extensions/gsd/pre-execution-checks.js +35 -2
- package/dist/resources/extensions/gsd/prompts/complete-slice.md +2 -2
- package/dist/resources/extensions/gsd/prompts/discuss.md +33 -13
- package/dist/resources/extensions/gsd/prompts/execute-task.md +20 -19
- package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +2 -0
- package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +2 -0
- package/dist/resources/extensions/gsd/prompts/guided-resume-task.md +1 -1
- package/dist/resources/extensions/gsd/prompts/queue.md +3 -2
- package/dist/resources/extensions/gsd/prompts/system.md +1 -0
- package/dist/resources/extensions/gsd/prompts/validate-milestone.md +2 -1
- 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/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 +1 -1
- package/dist/resources/skills/create-skill/SKILL.md +2 -0
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +15 -15
- 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 +15 -15
- 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/package.json +1 -1
- 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.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.js +21 -11
- 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/secure-env-collect.test.ts +265 -0
- package/packages/mcp-server/src/server.ts +137 -3
- package/packages/mcp-server/src/workflow-tools.test.ts +110 -0
- package/packages/mcp-server/src/workflow-tools.ts +31 -11
- package/packages/pi-ai/dist/providers/amazon-bedrock.js +11 -2
- package/packages/pi-ai/dist/providers/amazon-bedrock.js.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic-shared.d.ts +4 -1
- package/packages/pi-ai/dist/providers/anthropic-shared.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic-shared.js +8 -3
- package/packages/pi-ai/dist/providers/anthropic-shared.js.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic-shared.test.js +44 -1
- package/packages/pi-ai/dist/providers/anthropic-shared.test.js.map +1 -1
- package/packages/pi-ai/dist/providers/openai-completions.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/openai-completions.js +11 -0
- package/packages/pi-ai/dist/providers/openai-completions.js.map +1 -1
- package/packages/pi-ai/src/providers/amazon-bedrock.ts +13 -1
- package/packages/pi-ai/src/providers/anthropic-shared.test.ts +55 -1
- package/packages/pi-ai/src/providers/anthropic-shared.ts +14 -3
- package/packages/pi-ai/src/providers/openai-completions.ts +14 -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 +388 -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/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 +168 -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/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 +468 -0
- package/packages/pi-coding-agent/src/core/extensions/types.ts +2 -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 +198 -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/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/GSD-WORKFLOW.md +1 -1
- package/src/resources/extensions/claude-code-cli/stream-adapter.ts +166 -31
- package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +145 -0
- package/src/resources/extensions/get-secrets-from-user.ts +24 -1
- package/src/resources/extensions/gsd/auto-start.ts +4 -14
- package/src/resources/extensions/gsd/auto-tool-tracking.ts +1 -1
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +6 -0
- package/src/resources/extensions/gsd/bootstrap/system-context.ts +7 -0
- package/src/resources/extensions/gsd/commands/context.ts +16 -5
- package/src/resources/extensions/gsd/commands/dispatcher.ts +14 -2
- package/src/resources/extensions/gsd/custom-workflow-engine.ts +19 -14
- package/src/resources/extensions/gsd/dispatch-guard.ts +18 -1
- package/src/resources/extensions/gsd/error-classifier.ts +1 -1
- package/src/resources/extensions/gsd/file-lock.ts +59 -0
- package/src/resources/extensions/gsd/guided-flow.ts +12 -9
- package/src/resources/extensions/gsd/init-wizard.ts +3 -13
- package/src/resources/extensions/gsd/notification-store.ts +19 -1
- package/src/resources/extensions/gsd/notification-widget.ts +1 -1
- package/src/resources/extensions/gsd/pre-execution-checks.ts +39 -2
- package/src/resources/extensions/gsd/prompts/complete-slice.md +2 -2
- package/src/resources/extensions/gsd/prompts/discuss.md +33 -13
- package/src/resources/extensions/gsd/prompts/execute-task.md +20 -19
- package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +2 -0
- package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +2 -0
- package/src/resources/extensions/gsd/prompts/guided-resume-task.md +1 -1
- package/src/resources/extensions/gsd/prompts/queue.md +3 -2
- package/src/resources/extensions/gsd/prompts/system.md +1 -0
- package/src/resources/extensions/gsd/prompts/validate-milestone.md +2 -1
- package/src/resources/extensions/gsd/state.ts +274 -344
- package/src/resources/extensions/gsd/tests/auto-start-worktree-db-path.test.ts +28 -0
- package/src/resources/extensions/gsd/tests/bootstrap-derive-state-db-open.test.ts +39 -0
- package/src/resources/extensions/gsd/tests/complete-slice-prompt-task-summary-layout.test.ts +18 -0
- 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/dispatch-guard.test.ts +27 -0
- package/src/resources/extensions/gsd/tests/execute-task-prompt-existing-artifact-guard.test.ts +33 -0
- package/src/resources/extensions/gsd/tests/file-lock.test.ts +103 -0
- package/src/resources/extensions/gsd/tests/gsd-no-project-error.test.ts +73 -0
- package/src/resources/extensions/gsd/tests/notification-store.test.ts +17 -0
- package/src/resources/extensions/gsd/tests/notification-widget.test.ts +25 -0
- package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +49 -0
- package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +19 -0
- package/src/resources/extensions/gsd/tests/provider-errors.test.ts +7 -0
- package/src/resources/extensions/gsd/tests/secure-env-collect.test.ts +45 -0
- package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +7 -0
- package/src/resources/extensions/gsd/tests/validate-milestone-prompt-verification-classes.test.ts +18 -0
- package/src/resources/extensions/gsd/tests/workflow-mcp-auto-prep.test.ts +76 -0
- package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +155 -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/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 +1 -1
- package/src/resources/skills/create-skill/SKILL.md +2 -0
- 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/{9pw9EXtXjdM7EFrCXUEPf → dYVdRaunb2ZSEA8fjkT-V}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{9pw9EXtXjdM7EFrCXUEPf → dYVdRaunb2ZSEA8fjkT-V}/_ssgManifest.js +0 -0
package/README.md
CHANGED
|
@@ -27,36 +27,43 @@ One command. Walk away. Come back to a built project with clean git history.
|
|
|
27
27
|
|
|
28
28
|
---
|
|
29
29
|
|
|
30
|
-
## What's New in v2.
|
|
30
|
+
## What's New in v2.71
|
|
31
31
|
|
|
32
|
-
### MCP
|
|
32
|
+
### MCP Secure Env Collect
|
|
33
33
|
|
|
34
|
-
- **
|
|
35
|
-
- **
|
|
36
|
-
- **Write gate enforcement** — workflow MCP respects write gates, preventing unauthorized state mutations from external clients.
|
|
34
|
+
- **Secure credential collection over MCP** — the new `secure_env_collect` tool uses MCP form elicitation to collect secrets (API keys, tokens) from external clients without exposing values in tool output. Masks input in interactive mode.
|
|
35
|
+
- **Hardened elicitation schema** — MCP elicitation schema handling is stricter, with proper validation and fallback for providers that don't support forms.
|
|
37
36
|
|
|
38
|
-
### Reliability
|
|
37
|
+
### MCP Reliability
|
|
39
38
|
|
|
40
|
-
- **
|
|
41
|
-
- **
|
|
42
|
-
- **
|
|
43
|
-
- **Auto-resume hardening** — `autoStartTime` restored on resume, managed resources resynced on auto resume.
|
|
39
|
+
- **Stream ordering preserved** — MCP tool output now renders in the correct order, fixing interleaved output in Claude Code and other MCP clients.
|
|
40
|
+
- **isError flag propagation** — workflow tool execution failures now correctly return `isError: true`, so MCP clients can distinguish success from failure.
|
|
41
|
+
- **Multi-round discuss questions** — new-project discuss phase supports multi-round questioning with structured question gates.
|
|
44
42
|
|
|
45
|
-
### TUI
|
|
43
|
+
### TUI Fixes
|
|
46
44
|
|
|
47
|
-
- **
|
|
48
|
-
- **
|
|
45
|
+
- **Pinned output restored** — pinned output bar displays above the editor during tool execution again.
|
|
46
|
+
- **Turn completion cleanup** — pinned latest output is cleared on turn completion, preventing stale output from persisting.
|
|
47
|
+
- **Secure input masking** — extension input values are masked in interactive mode when collecting secrets.
|
|
49
48
|
|
|
50
|
-
###
|
|
49
|
+
### Reliability & Internals
|
|
51
50
|
|
|
52
|
-
- **
|
|
53
|
-
- **
|
|
51
|
+
- **TOCTOU file locking** — race conditions in event log and custom workflow graph file locking are fixed with proper atomic lock acquisition.
|
|
52
|
+
- **State derive refactor** — `deriveStateFromDb` god function extracted into composable, testable helpers.
|
|
53
|
+
- **Windows portability** — hardened cross-platform portability across runtime, tooling, and CI.
|
|
54
|
+
- **Model routing transparency** — dynamic routing is skipped for interactive dispatches; model changes are always shown in the banner.
|
|
55
|
+
- **Capability-aware routing (ADR-004)** — full implementation of capability scoring, `before_model_select` hook, and task metadata extraction.
|
|
56
|
+
- **Multi-model provider strategy (ADR-005)** — infrastructure for multi-provider model selection wired into live paths.
|
|
54
57
|
|
|
55
58
|
See the full [Changelog](./CHANGELOG.md) for details on every release.
|
|
56
59
|
|
|
57
60
|
<details>
|
|
58
|
-
<summary>Previous highlights (v2.
|
|
61
|
+
<summary>Previous highlights (v2.70 and earlier)</summary>
|
|
59
62
|
|
|
63
|
+
- **Full workflow over MCP (v2.68)** — slice replanning, milestone management, slice completion, task completion, and core planning tools exposed over MCP
|
|
64
|
+
- **Transport-gated MCP (v2.68)** — workflow tool availability adapts to provider transport capabilities automatically
|
|
65
|
+
- **Contextual tips system (v2.68)** — TUI and web terminal surface contextual tips based on workflow state
|
|
66
|
+
- **Ask user questions over MCP (v2.70)** — interactive questions exposed via elicitation for external integrations
|
|
60
67
|
- **Tiered Context Injection (M005)** — relevance-scoped context with 65%+ token reduction
|
|
61
68
|
- **Resilient transient error recovery** — defers to Core RetryHandler and fixes cmdCtx race conditions
|
|
62
69
|
- **Anthropic subscription routing** — auto-routed through Claude Code CLI provider with proper display names
|
package/dist/cli.js
CHANGED
|
@@ -7,6 +7,7 @@ import { ensureManagedTools } from './tool-bootstrap.js';
|
|
|
7
7
|
import { loadStoredEnvKeys } from './wizard.js';
|
|
8
8
|
import { migratePiCredentials } from './pi-migration.js';
|
|
9
9
|
import { validateConfiguredModel } from './startup-model-validation.js';
|
|
10
|
+
import { shouldMigrateAnthropicToClaudeCode } from './provider-migrations.js';
|
|
10
11
|
import { shouldRunOnboarding, runOnboarding } from './onboarding.js';
|
|
11
12
|
import chalk from 'chalk';
|
|
12
13
|
import { checkForUpdates } from './update-check.js';
|
|
@@ -285,7 +286,7 @@ const { resolveModelsJsonPath } = await import('./models-resolver.js');
|
|
|
285
286
|
const modelsJsonPath = resolveModelsJsonPath();
|
|
286
287
|
const modelRegistry = new ModelRegistry(authStorage, modelsJsonPath);
|
|
287
288
|
markStartup('ModelRegistry');
|
|
288
|
-
const settingsManager = SettingsManager.create(agentDir);
|
|
289
|
+
const settingsManager = SettingsManager.create(process.cwd(), agentDir);
|
|
289
290
|
applySecurityOverrides(settingsManager);
|
|
290
291
|
markStartup('SettingsManager.create');
|
|
291
292
|
// Run onboarding wizard on first launch (no LLM provider configured)
|
|
@@ -401,7 +402,11 @@ if (isPrintMode) {
|
|
|
401
402
|
// Migrate anthropic OAuth users to claude-code provider when CLI is available (#3772).
|
|
402
403
|
// Anthropic blocks third-party apps from using subscription quotas — routing through
|
|
403
404
|
// the local claude CLI binary is TOS-compliant.
|
|
404
|
-
if (
|
|
405
|
+
if (shouldMigrateAnthropicToClaudeCode({
|
|
406
|
+
authStorage,
|
|
407
|
+
isClaudeCodeReady: modelRegistry.isProviderRequestReady('claude-code'),
|
|
408
|
+
defaultProvider: settingsManager.getDefaultProvider(),
|
|
409
|
+
})) {
|
|
405
410
|
const currentModelId = settingsManager.getDefaultModel();
|
|
406
411
|
if (currentModelId) {
|
|
407
412
|
const ccModel = modelRegistry.find('claude-code', currentModelId);
|
|
@@ -576,7 +581,11 @@ markStartup('createAgentSession');
|
|
|
576
581
|
// Migrate anthropic OAuth users to claude-code provider when CLI is available (#3772).
|
|
577
582
|
// Anthropic blocks third-party apps from using subscription quotas — routing through
|
|
578
583
|
// the local claude CLI binary is TOS-compliant.
|
|
579
|
-
if (
|
|
584
|
+
if (shouldMigrateAnthropicToClaudeCode({
|
|
585
|
+
authStorage,
|
|
586
|
+
isClaudeCodeReady: modelRegistry.isProviderRequestReady('claude-code'),
|
|
587
|
+
defaultProvider: settingsManager.getDefaultProvider(),
|
|
588
|
+
})) {
|
|
580
589
|
const currentModelId = settingsManager.getDefaultModel();
|
|
581
590
|
if (currentModelId) {
|
|
582
591
|
const ccModel = modelRegistry.find('claude-code', currentModelId);
|
package/dist/mcp-server.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
// MCP SDK subpath imports use wildcard exports (./*) that NodeNext resolves
|
|
2
2
|
// at runtime but TypeScript cannot statically type-check. We construct the
|
|
3
3
|
// specifiers dynamically so tsc treats them as `any`.
|
|
4
|
-
//
|
|
5
|
-
// .js
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
//
|
|
5
|
+
// Use explicit .js subpaths for modules that are loaded dynamically at runtime.
|
|
6
|
+
// Recent Node / SDK combinations do not reliably resolve the extensionless
|
|
7
|
+
// wildcard targets for `server/stdio` and `types` (#3914).
|
|
8
8
|
const MCP_PKG = '@modelcontextprotocol/sdk';
|
|
9
9
|
/**
|
|
10
10
|
* Starts a native MCP (Model Context Protocol) server over stdin/stdout.
|
|
@@ -23,8 +23,8 @@ const MCP_PKG = '@modelcontextprotocol/sdk';
|
|
|
23
23
|
export async function startMcpServer(options) {
|
|
24
24
|
const { tools, version = '0.0.0' } = options;
|
|
25
25
|
const serverMod = await import(`${MCP_PKG}/server`);
|
|
26
|
-
const stdioMod = await import(
|
|
27
|
-
const typesMod = await import(
|
|
26
|
+
const stdioMod = await import(`${MCP_PKG}/server/stdio.js`);
|
|
27
|
+
const typesMod = await import(`${MCP_PKG}/types.js`);
|
|
28
28
|
const Server = serverMod.Server;
|
|
29
29
|
const StdioServerTransport = stdioMod.StdioServerTransport;
|
|
30
30
|
const { ListToolsRequestSchema, CallToolRequestSchema } = typesMod;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { AuthStorage } from "@gsd/pi-coding-agent";
|
|
2
|
+
type AnthropicMigrationDeps = {
|
|
3
|
+
authStorage: Pick<AuthStorage, "getCredentialsForProvider">;
|
|
4
|
+
isClaudeCodeReady: boolean;
|
|
5
|
+
defaultProvider: string | undefined;
|
|
6
|
+
env?: NodeJS.ProcessEnv;
|
|
7
|
+
};
|
|
8
|
+
export declare function hasDirectAnthropicApiKey(authStorage: Pick<AuthStorage, "getCredentialsForProvider">, env?: NodeJS.ProcessEnv): boolean;
|
|
9
|
+
export declare function shouldMigrateAnthropicToClaudeCode({ authStorage, isClaudeCodeReady, defaultProvider, env, }: AnthropicMigrationDeps): boolean;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export function hasDirectAnthropicApiKey(authStorage, env = process.env) {
|
|
2
|
+
if ((env.ANTHROPIC_API_KEY ?? "").trim()) {
|
|
3
|
+
return true;
|
|
4
|
+
}
|
|
5
|
+
return authStorage.getCredentialsForProvider("anthropic").some((credential) => credential?.type === "api_key" && typeof credential?.key === "string" && credential.key.trim().length > 0);
|
|
6
|
+
}
|
|
7
|
+
export function shouldMigrateAnthropicToClaudeCode({ authStorage, isClaudeCodeReady, defaultProvider, env = process.env, }) {
|
|
8
|
+
if (!isClaudeCodeReady || defaultProvider !== "anthropic") {
|
|
9
|
+
return false;
|
|
10
|
+
}
|
|
11
|
+
return !hasDirectAnthropicApiKey(authStorage, env);
|
|
12
|
+
}
|
package/dist/resource-loader.js
CHANGED
|
@@ -2,7 +2,7 @@ import { DefaultResourceLoader, sortExtensionPaths } from '@gsd/pi-coding-agent'
|
|
|
2
2
|
import { createHash } from 'node:crypto';
|
|
3
3
|
import { homedir } from 'node:os';
|
|
4
4
|
import { chmodSync, copyFileSync, cpSync, existsSync, lstatSync, mkdirSync, openSync, closeSync, readFileSync, readlinkSync, readdirSync, rmSync, statSync, symlinkSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
5
|
-
import { dirname, join, relative, resolve } from 'node:path';
|
|
5
|
+
import { basename, dirname, join, relative, resolve } from 'node:path';
|
|
6
6
|
import { fileURLToPath } from 'node:url';
|
|
7
7
|
import { compareSemver } from './update-check.js';
|
|
8
8
|
import { discoverExtensionEntryPaths } from './extension-discovery.js';
|
|
@@ -254,34 +254,157 @@ function copyDirRecursive(src, dest) {
|
|
|
254
254
|
* ~/.gsd/agent/extensions/ have no ancestor node_modules, so imports of
|
|
255
255
|
* @gsd/* packages fail. The symlink makes Node's standard resolution find
|
|
256
256
|
* them without requiring every call site to use jiti.
|
|
257
|
+
*
|
|
258
|
+
* Layout differences by install method:
|
|
259
|
+
* - Source/monorepo: packageRoot/node_modules has everything → simple symlink
|
|
260
|
+
* - npm/bun global: deps hoisted to dirname(packageRoot), including @gsd/* → simple symlink
|
|
261
|
+
* - pnpm global: external deps hoisted, but @gsd/* stays in packageRoot/node_modules
|
|
262
|
+
* → merged directory with symlinks from both roots (#3529, #3564)
|
|
257
263
|
*/
|
|
258
264
|
function ensureNodeModulesSymlink(agentDir) {
|
|
259
265
|
const agentNodeModules = join(agentDir, 'node_modules');
|
|
260
|
-
const
|
|
266
|
+
const internalNodeModules = join(packageRoot, 'node_modules');
|
|
267
|
+
const hoistedNodeModules = dirname(packageRoot);
|
|
268
|
+
const isGlobalInstall = basename(hoistedNodeModules) === 'node_modules';
|
|
269
|
+
if (!isGlobalInstall) {
|
|
270
|
+
// Source/monorepo: internal node_modules has everything
|
|
271
|
+
reconcileSymlink(agentNodeModules, internalNodeModules);
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
// Global install: check if workspace scopes (@gsd/*) are hoisted.
|
|
275
|
+
// npm/bun hoist everything; pnpm keeps workspace packages internal.
|
|
276
|
+
if (!hasMissingWorkspaceScopes(hoistedNodeModules, internalNodeModules)) {
|
|
277
|
+
// Everything is hoisted — simple symlink to parent node_modules
|
|
278
|
+
reconcileSymlink(agentNodeModules, hoistedNodeModules);
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
// pnpm-style layout: create a real directory merging both roots
|
|
282
|
+
reconcileMergedNodeModules(agentNodeModules, hoistedNodeModules, internalNodeModules);
|
|
283
|
+
}
|
|
284
|
+
/** Check if any @gsd* scopes exist in internal but not in hoisted node_modules */
|
|
285
|
+
function hasMissingWorkspaceScopes(hoisted, internal) {
|
|
286
|
+
if (!existsSync(internal))
|
|
287
|
+
return false;
|
|
261
288
|
try {
|
|
262
|
-
const
|
|
289
|
+
for (const entry of readdirSync(internal, { withFileTypes: true })) {
|
|
290
|
+
if (entry.isDirectory() && entry.name.startsWith('@gsd') &&
|
|
291
|
+
!existsSync(join(hoisted, entry.name))) {
|
|
292
|
+
return true;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
catch { /* non-fatal */ }
|
|
297
|
+
return false;
|
|
298
|
+
}
|
|
299
|
+
/** Ensure a symlink at `link` points to `target`, fixing stale/wrong entries */
|
|
300
|
+
function reconcileSymlink(link, target) {
|
|
301
|
+
try {
|
|
302
|
+
const stat = lstatSync(link);
|
|
263
303
|
if (stat.isSymbolicLink()) {
|
|
264
|
-
const existing = readlinkSync(
|
|
265
|
-
|
|
266
|
-
if (existing === gsdNodeModules && existsSync(agentNodeModules))
|
|
304
|
+
const existing = readlinkSync(link);
|
|
305
|
+
if (existing === target && existsSync(link))
|
|
267
306
|
return; // correct and target exists
|
|
268
|
-
|
|
307
|
+
unlinkSync(link);
|
|
308
|
+
}
|
|
309
|
+
else {
|
|
310
|
+
// Real directory (or merged dir from previous pnpm fix) — remove it
|
|
311
|
+
rmSync(link, { recursive: true, force: true });
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
catch {
|
|
315
|
+
// lstatSync throws if path doesn't exist — fine, we'll create below
|
|
316
|
+
}
|
|
317
|
+
try {
|
|
318
|
+
symlinkSync(target, link, 'junction');
|
|
319
|
+
}
|
|
320
|
+
catch (err) {
|
|
321
|
+
console.error(`[gsd] WARN: Failed to symlink ${link} → ${target}: ${err instanceof Error ? err.message : err}`);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
/**
|
|
325
|
+
* Create a real node_modules directory containing symlinks from both the
|
|
326
|
+
* hoisted root (external deps) and internal root (@gsd/* workspace packages).
|
|
327
|
+
* Used for pnpm global installs where @gsd/* isn't hoisted.
|
|
328
|
+
*/
|
|
329
|
+
function reconcileMergedNodeModules(agentNodeModules, hoisted, internal) {
|
|
330
|
+
// Fast path: if already merged for this packageRoot + same directory contents, skip.
|
|
331
|
+
// The fingerprint includes entry names from both roots so `pnpm add/remove` triggers rebuild.
|
|
332
|
+
const marker = join(agentNodeModules, '.gsd-merged');
|
|
333
|
+
const fingerprint = mergedFingerprint(hoisted, internal);
|
|
334
|
+
try {
|
|
335
|
+
if (existsSync(marker) && readFileSync(marker, 'utf-8').trim() === fingerprint)
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
catch { /* rebuild */ }
|
|
339
|
+
// Remove any existing symlink or stale merged directory
|
|
340
|
+
try {
|
|
341
|
+
const stat = lstatSync(agentNodeModules);
|
|
342
|
+
if (stat.isSymbolicLink()) {
|
|
269
343
|
unlinkSync(agentNodeModules);
|
|
270
344
|
}
|
|
271
345
|
else {
|
|
272
|
-
// Real directory (not a symlink) is blocking — remove it
|
|
273
346
|
rmSync(agentNodeModules, { recursive: true, force: true });
|
|
274
347
|
}
|
|
275
348
|
}
|
|
276
|
-
catch {
|
|
277
|
-
|
|
349
|
+
catch { /* doesn't exist */ }
|
|
350
|
+
mkdirSync(agentNodeModules, { recursive: true });
|
|
351
|
+
let linkedCount = 0;
|
|
352
|
+
// Symlink entries from the hoisted node_modules (external deps)
|
|
353
|
+
try {
|
|
354
|
+
for (const entry of readdirSync(hoisted, { withFileTypes: true })) {
|
|
355
|
+
// Skip the gsd-pi package itself and dotfiles
|
|
356
|
+
if (entry.name === basename(packageRoot))
|
|
357
|
+
continue;
|
|
358
|
+
if (entry.name.startsWith('.'))
|
|
359
|
+
continue;
|
|
360
|
+
try {
|
|
361
|
+
symlinkSync(join(hoisted, entry.name), join(agentNodeModules, entry.name));
|
|
362
|
+
linkedCount++;
|
|
363
|
+
}
|
|
364
|
+
catch { /* skip individual */ }
|
|
365
|
+
}
|
|
278
366
|
}
|
|
367
|
+
catch (err) {
|
|
368
|
+
console.error(`[gsd] WARN: Failed to read hoisted node_modules at ${hoisted}: ${err instanceof Error ? err.message : err}`);
|
|
369
|
+
}
|
|
370
|
+
// Overlay @gsd* workspace scopes from internal node_modules
|
|
279
371
|
try {
|
|
280
|
-
|
|
372
|
+
for (const entry of readdirSync(internal, { withFileTypes: true })) {
|
|
373
|
+
if (!entry.name.startsWith('@gsd'))
|
|
374
|
+
continue;
|
|
375
|
+
const link = join(agentNodeModules, entry.name);
|
|
376
|
+
try {
|
|
377
|
+
lstatSync(link);
|
|
378
|
+
unlinkSync(link);
|
|
379
|
+
}
|
|
380
|
+
catch { /* didn't exist */ }
|
|
381
|
+
try {
|
|
382
|
+
symlinkSync(join(internal, entry.name), link);
|
|
383
|
+
linkedCount++;
|
|
384
|
+
}
|
|
385
|
+
catch { /* skip individual */ }
|
|
386
|
+
}
|
|
281
387
|
}
|
|
282
388
|
catch (err) {
|
|
283
|
-
|
|
284
|
-
|
|
389
|
+
console.error(`[gsd] WARN: Failed to read internal node_modules at ${internal}: ${err instanceof Error ? err.message : err}`);
|
|
390
|
+
}
|
|
391
|
+
// Only stamp marker if we actually linked something — avoids caching a broken state
|
|
392
|
+
if (linkedCount > 0) {
|
|
393
|
+
try {
|
|
394
|
+
writeFileSync(marker, fingerprint);
|
|
395
|
+
}
|
|
396
|
+
catch { /* non-fatal */ }
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
/** Build a cache fingerprint from packageRoot + sorted entry names of both directories */
|
|
400
|
+
function mergedFingerprint(hoisted, internal) {
|
|
401
|
+
try {
|
|
402
|
+
const h = readdirSync(hoisted).sort().join(',');
|
|
403
|
+
const i = readdirSync(internal).sort().join(',');
|
|
404
|
+
return `${packageRoot}\n${h}\n${i}`;
|
|
405
|
+
}
|
|
406
|
+
catch {
|
|
407
|
+
return packageRoot; // fallback: at least invalidate on version change
|
|
285
408
|
}
|
|
286
409
|
}
|
|
287
410
|
/**
|
|
@@ -275,7 +275,7 @@ Work flows through these phases. Each phase produces a file.
|
|
|
275
275
|
**How to do it manually:**
|
|
276
276
|
1. Read the roadmap to understand the scope.
|
|
277
277
|
2. Identify 3-5 gray areas — implementation decisions the user cares about.
|
|
278
|
-
3. Use `ask_user_questions` to discuss each area.
|
|
278
|
+
3. Use `ask_user_questions` to discuss each area, one round at a time. Never fabricate user input; wait for the user's actual response before the next round.
|
|
279
279
|
4. Write decisions to the appropriate context file (`M###-CONTEXT.md` or `S##-CONTEXT.md`).
|
|
280
280
|
5. Do NOT discuss how to implement — only what the user wants.
|
|
281
281
|
|
|
@@ -12,6 +12,7 @@ import { PartialMessageBuilder, ZERO_USAGE, mapUsage } from "./partial-builder.j
|
|
|
12
12
|
import { buildWorkflowMcpServers } from "../gsd/workflow-mcp.js";
|
|
13
13
|
import { showInterviewRound } from "../shared/tui.js";
|
|
14
14
|
const OTHER_OPTION_LABEL = "None of the above";
|
|
15
|
+
const SENSITIVE_FIELD_PATTERN = /(password|passphrase|secret|token|api[_\s-]*key|private[_\s-]*key|credential)/i;
|
|
15
16
|
// ---------------------------------------------------------------------------
|
|
16
17
|
// Stream factory
|
|
17
18
|
// ---------------------------------------------------------------------------
|
|
@@ -182,6 +183,56 @@ export function parseAskUserQuestionsElicitation(request) {
|
|
|
182
183
|
}
|
|
183
184
|
return questions.length > 0 ? questions : null;
|
|
184
185
|
}
|
|
186
|
+
function isSecureElicitationField(requestMessage, fieldId, field) {
|
|
187
|
+
if (field.format === "password")
|
|
188
|
+
return true;
|
|
189
|
+
if (field.writeOnly === true)
|
|
190
|
+
return true;
|
|
191
|
+
const rawField = field;
|
|
192
|
+
if (rawField.sensitive === true || rawField["x-sensitive"] === true)
|
|
193
|
+
return true;
|
|
194
|
+
const haystack = [
|
|
195
|
+
requestMessage,
|
|
196
|
+
fieldId.replace(/[_-]+/g, " "),
|
|
197
|
+
typeof field.title === "string" ? field.title : "",
|
|
198
|
+
typeof field.description === "string" ? field.description : "",
|
|
199
|
+
]
|
|
200
|
+
.join(" ")
|
|
201
|
+
.toLowerCase();
|
|
202
|
+
return SENSITIVE_FIELD_PATTERN.test(haystack);
|
|
203
|
+
}
|
|
204
|
+
export function parseTextInputElicitation(request) {
|
|
205
|
+
if (request.mode && request.mode !== "form")
|
|
206
|
+
return null;
|
|
207
|
+
const schema = request.requestedSchema;
|
|
208
|
+
const fieldsSource = schema?.properties && typeof schema.properties === "object"
|
|
209
|
+
? schema.properties
|
|
210
|
+
: schema?.keys && typeof schema.keys === "object"
|
|
211
|
+
? schema.keys
|
|
212
|
+
: undefined;
|
|
213
|
+
if (!fieldsSource)
|
|
214
|
+
return null;
|
|
215
|
+
const requiredSet = new Set(Array.isArray(request.requestedSchema?.required)
|
|
216
|
+
? request.requestedSchema.required.filter((value) => typeof value === "string")
|
|
217
|
+
: []);
|
|
218
|
+
const fields = [];
|
|
219
|
+
for (const [fieldId, field] of Object.entries(fieldsSource)) {
|
|
220
|
+
if (!field || typeof field !== "object")
|
|
221
|
+
continue;
|
|
222
|
+
if (field.type !== "string")
|
|
223
|
+
continue;
|
|
224
|
+
if (Array.isArray(field.oneOf) && field.oneOf.length > 0)
|
|
225
|
+
continue;
|
|
226
|
+
fields.push({
|
|
227
|
+
id: fieldId,
|
|
228
|
+
title: typeof field.title === "string" && field.title.length > 0 ? field.title : fieldId,
|
|
229
|
+
description: typeof field.description === "string" ? field.description : "",
|
|
230
|
+
required: requiredSet.has(fieldId),
|
|
231
|
+
secure: isSecureElicitationField(request.message, fieldId, field),
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
return fields.length > 0 ? fields : null;
|
|
235
|
+
}
|
|
185
236
|
export function roundResultToElicitationContent(questions, result) {
|
|
186
237
|
const content = {};
|
|
187
238
|
for (const question of questions) {
|
|
@@ -246,6 +297,38 @@ async function promptElicitationWithDialogs(request, questions, ui, signal) {
|
|
|
246
297
|
}
|
|
247
298
|
return { action: "accept", content };
|
|
248
299
|
}
|
|
300
|
+
function buildTextInputPromptTitle(request, field) {
|
|
301
|
+
const parts = [
|
|
302
|
+
request.serverName ? `[${request.serverName}]` : "",
|
|
303
|
+
field.title,
|
|
304
|
+
field.description,
|
|
305
|
+
].filter((part) => typeof part === "string" && part.trim().length > 0);
|
|
306
|
+
return parts.join("\n\n");
|
|
307
|
+
}
|
|
308
|
+
function buildTextInputPlaceholder(field) {
|
|
309
|
+
const desc = field.description.trim();
|
|
310
|
+
if (!desc)
|
|
311
|
+
return field.required ? "Required" : "Leave empty to skip";
|
|
312
|
+
const formatLine = desc
|
|
313
|
+
.split(/\r?\n/)
|
|
314
|
+
.map((line) => line.trim())
|
|
315
|
+
.find((line) => /^format:/i.test(line));
|
|
316
|
+
if (!formatLine)
|
|
317
|
+
return field.required ? "Required" : "Leave empty to skip";
|
|
318
|
+
const hint = formatLine.replace(/^format:\s*/i, "").trim();
|
|
319
|
+
return hint.length > 0 ? hint : field.required ? "Required" : "Leave empty to skip";
|
|
320
|
+
}
|
|
321
|
+
async function promptTextInputElicitation(request, fields, ui, signal) {
|
|
322
|
+
const content = {};
|
|
323
|
+
for (const field of fields) {
|
|
324
|
+
const value = await ui.input(buildTextInputPromptTitle(request, field), buildTextInputPlaceholder(field), { signal, ...(field.secure ? { secure: true } : {}) });
|
|
325
|
+
if (value === undefined) {
|
|
326
|
+
return { action: "cancel" };
|
|
327
|
+
}
|
|
328
|
+
content[field.id] = value;
|
|
329
|
+
}
|
|
330
|
+
return { action: "accept", content };
|
|
331
|
+
}
|
|
249
332
|
export function createClaudeCodeElicitationHandler(ui) {
|
|
250
333
|
if (!ui)
|
|
251
334
|
return undefined;
|
|
@@ -254,17 +337,21 @@ export function createClaudeCodeElicitationHandler(ui) {
|
|
|
254
337
|
return { action: "decline" };
|
|
255
338
|
}
|
|
256
339
|
const questions = parseAskUserQuestionsElicitation(request);
|
|
257
|
-
if (
|
|
258
|
-
|
|
340
|
+
if (questions) {
|
|
341
|
+
const interviewResult = await showInterviewRound(questions, { signal }, { ui }).catch(() => undefined);
|
|
342
|
+
if (interviewResult && Object.keys(interviewResult.answers).length > 0) {
|
|
343
|
+
return {
|
|
344
|
+
action: "accept",
|
|
345
|
+
content: roundResultToElicitationContent(questions, interviewResult),
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
return promptElicitationWithDialogs(request, questions, ui, signal);
|
|
259
349
|
}
|
|
260
|
-
const
|
|
261
|
-
if (
|
|
262
|
-
return
|
|
263
|
-
action: "accept",
|
|
264
|
-
content: roundResultToElicitationContent(questions, interviewResult),
|
|
265
|
-
};
|
|
350
|
+
const textFields = parseTextInputElicitation(request);
|
|
351
|
+
if (textFields) {
|
|
352
|
+
return promptTextInputElicitation(request, textFields, ui, signal);
|
|
266
353
|
}
|
|
267
|
-
return
|
|
354
|
+
return { action: "decline" };
|
|
268
355
|
};
|
|
269
356
|
}
|
|
270
357
|
// ---------------------------------------------------------------------------
|
|
@@ -278,6 +365,7 @@ export function createClaudeCodeElicitationHandler(ui) {
|
|
|
278
365
|
*/
|
|
279
366
|
export function buildSdkOptions(modelId, prompt, extraOptions = {}) {
|
|
280
367
|
const mcpServers = buildWorkflowMcpServers();
|
|
368
|
+
const disallowedTools = ["AskUserQuestion"];
|
|
281
369
|
return {
|
|
282
370
|
pathToClaudeCodeExecutable: getClaudePath(),
|
|
283
371
|
model: modelId,
|
|
@@ -288,6 +376,7 @@ export function buildSdkOptions(modelId, prompt, extraOptions = {}) {
|
|
|
288
376
|
allowDangerouslySkipPermissions: true,
|
|
289
377
|
settingSources: ["project"],
|
|
290
378
|
systemPrompt: { type: "preset", preset: "claude_code" },
|
|
379
|
+
disallowedTools,
|
|
291
380
|
...(mcpServers ? { mcpServers } : {}),
|
|
292
381
|
betas: modelId.includes("sonnet") ? ["context-1m-2025-08-07"] : [],
|
|
293
382
|
...extraOptions,
|
|
@@ -371,9 +460,9 @@ export function extractToolResultsFromSdkUserMessage(message) {
|
|
|
371
460
|
}
|
|
372
461
|
return extracted;
|
|
373
462
|
}
|
|
374
|
-
function
|
|
375
|
-
for (const block of
|
|
376
|
-
if (block.type !== "toolCall")
|
|
463
|
+
function attachExternalResultsToToolBlocks(toolBlocks, toolResultsById) {
|
|
464
|
+
for (const block of toolBlocks) {
|
|
465
|
+
if (block.type !== "toolCall" && block.type !== "serverToolUse")
|
|
377
466
|
continue;
|
|
378
467
|
const externalResult = toolResultsById.get(block.id);
|
|
379
468
|
if (!externalResult)
|
|
@@ -402,8 +491,8 @@ async function pumpSdkMessages(model, context, options, stream) {
|
|
|
402
491
|
/** Track the last text content seen across all assistant turns for the final message. */
|
|
403
492
|
let lastTextContent = "";
|
|
404
493
|
let lastThinkingContent = "";
|
|
405
|
-
/** Collect tool
|
|
406
|
-
const
|
|
494
|
+
/** Collect tool blocks from intermediate SDK turns for tool execution rendering. */
|
|
495
|
+
const intermediateToolBlocks = [];
|
|
407
496
|
/** Preserve real external tool results from Claude Code's synthetic user messages. */
|
|
408
497
|
const toolResultsById = new Map();
|
|
409
498
|
try {
|
|
@@ -491,9 +580,9 @@ async function pumpSdkMessages(model, context, options, stream) {
|
|
|
491
580
|
else if (block.type === "thinking" && block.thinking) {
|
|
492
581
|
lastThinkingContent = block.thinking;
|
|
493
582
|
}
|
|
494
|
-
else if (block.type === "toolCall") {
|
|
495
|
-
// Collect tool
|
|
496
|
-
|
|
583
|
+
else if (block.type === "toolCall" || block.type === "serverToolUse") {
|
|
584
|
+
// Collect tool blocks for externalToolExecution rendering
|
|
585
|
+
intermediateToolBlocks.push(block);
|
|
497
586
|
}
|
|
498
587
|
}
|
|
499
588
|
}
|
|
@@ -502,25 +591,35 @@ async function pumpSdkMessages(model, context, options, stream) {
|
|
|
502
591
|
for (const { toolUseId, result } of extractToolResultsFromSdkUserMessage(msg)) {
|
|
503
592
|
toolResultsById.set(toolUseId, result);
|
|
504
593
|
}
|
|
505
|
-
|
|
594
|
+
attachExternalResultsToToolBlocks(intermediateToolBlocks, toolResultsById);
|
|
506
595
|
// Push a synthetic toolcall_end for each tool call from this turn
|
|
507
596
|
// so the TUI can render tool results in real-time during the SDK
|
|
508
597
|
// session instead of waiting until the entire session completes.
|
|
509
598
|
if (builder) {
|
|
510
599
|
for (const block of builder.message.content) {
|
|
511
|
-
if (block.type !== "toolCall")
|
|
512
|
-
continue;
|
|
513
600
|
const extResult = block.externalResult;
|
|
514
601
|
if (!extResult)
|
|
515
602
|
continue;
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
603
|
+
const contentIndex = builder.message.content.indexOf(block);
|
|
604
|
+
if (contentIndex < 0)
|
|
605
|
+
continue;
|
|
606
|
+
// Push synthetic completion events with result attached so the
|
|
607
|
+
// chat-controller can update pending ToolExecutionComponents.
|
|
608
|
+
if (block.type === "toolCall") {
|
|
609
|
+
stream.push({
|
|
610
|
+
type: "toolcall_end",
|
|
611
|
+
contentIndex,
|
|
612
|
+
toolCall: block,
|
|
613
|
+
partial: builder.message,
|
|
614
|
+
});
|
|
615
|
+
}
|
|
616
|
+
else if (block.type === "serverToolUse") {
|
|
617
|
+
stream.push({
|
|
618
|
+
type: "server_tool_use",
|
|
619
|
+
contentIndex,
|
|
620
|
+
partial: builder.message,
|
|
621
|
+
});
|
|
622
|
+
}
|
|
524
623
|
}
|
|
525
624
|
}
|
|
526
625
|
builder = null;
|
|
@@ -534,8 +633,8 @@ async function pumpSdkMessages(model, context, options, stream) {
|
|
|
534
633
|
// events for proper TUI rendering, followed by the text response.
|
|
535
634
|
const finalContent = [];
|
|
536
635
|
// Add tool calls from intermediate turns first (renders above text)
|
|
537
|
-
|
|
538
|
-
finalContent.push(...
|
|
636
|
+
attachExternalResultsToToolBlocks(intermediateToolBlocks, toolResultsById);
|
|
637
|
+
finalContent.push(...intermediateToolBlocks);
|
|
539
638
|
// Add text/thinking from the last turn
|
|
540
639
|
if (builder && builder.message.content.length > 0) {
|
|
541
640
|
for (const block of builder.message.content) {
|
|
@@ -93,7 +93,7 @@ export function detectDestination(basePath) {
|
|
|
93
93
|
async function collectOneSecret(ctx, pageIndex, totalPages, keyName, hint, guidance) {
|
|
94
94
|
if (!ctx.hasUI)
|
|
95
95
|
return null;
|
|
96
|
-
|
|
96
|
+
const customResult = await ctx.ui.custom((tui, theme, _kb, done) => {
|
|
97
97
|
let value = "";
|
|
98
98
|
let cachedLines;
|
|
99
99
|
const editorTheme = {
|
|
@@ -178,6 +178,22 @@ async function collectOneSecret(ctx, pageIndex, totalPages, keyName, hint, guida
|
|
|
178
178
|
handleInput,
|
|
179
179
|
};
|
|
180
180
|
});
|
|
181
|
+
// RPC/web surfaces may not implement ctx.ui.custom(). Fall back to a
|
|
182
|
+
// standard input prompt so users can still provide the secret.
|
|
183
|
+
if (customResult !== undefined) {
|
|
184
|
+
return customResult;
|
|
185
|
+
}
|
|
186
|
+
if (typeof ctx.ui?.input !== "function") {
|
|
187
|
+
return null;
|
|
188
|
+
}
|
|
189
|
+
const inputTitle = `Secure value for ${keyName} (${pageIndex + 1}/${totalPages})`;
|
|
190
|
+
const inputPlaceholder = hint || "Enter secret value";
|
|
191
|
+
const inputResult = await ctx.ui.input(inputTitle, inputPlaceholder, { secure: true });
|
|
192
|
+
if (typeof inputResult !== "string") {
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
195
|
+
const trimmed = inputResult.trim();
|
|
196
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
181
197
|
}
|
|
182
198
|
/**
|
|
183
199
|
* Exported wrapper around collectOneSecret for testing.
|