gsd-pi 2.69.0 → 2.70.0-dev.7ebda5e
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 +150 -2
- 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 +25 -1
- package/dist/resources/extensions/gsd/auto.js +12 -8
- package/dist/resources/extensions/gsd/bootstrap/system-context.js +6 -2
- package/dist/resources/extensions/gsd/commands-cmux.js +30 -1
- package/dist/resources/extensions/gsd/commands-handlers.js +22 -8
- 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/guided-flow.js +21 -10
- package/dist/resources/extensions/gsd/pre-execution-checks.js +5 -3
- package/dist/resources/extensions/gsd/validate-directory.js +30 -12
- package/dist/resources/extensions/gsd/workflow-mcp.js +64 -6
- 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 +12 -12
- package/dist/web/standalone/.next/build-manifest.json +3 -3
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/required-server-files.json +4 -4
- 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 +12 -12
- package/dist/web/standalone/.next/server/chunks/63.js +3 -3
- package/dist/web/standalone/.next/server/chunks/6897.js +3 -3
- package/dist/web/standalone/.next/server/middleware-build-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/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/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/daemon/src/orchestrator.ts +9 -84
- package/packages/mcp-server/README.md +25 -3
- package/packages/mcp-server/dist/cli.d.ts +0 -1
- package/packages/mcp-server/dist/cli.d.ts.map +1 -1
- package/packages/mcp-server/dist/cli.js +4 -2
- package/packages/mcp-server/dist/cli.js.map +1 -1
- package/packages/mcp-server/dist/server.d.ts +32 -1
- package/packages/mcp-server/dist/server.d.ts.map +1 -1
- package/packages/mcp-server/dist/server.js +118 -1
- package/packages/mcp-server/dist/server.js.map +1 -1
- package/packages/mcp-server/dist/tool-credentials.d.ts +6 -0
- package/packages/mcp-server/dist/tool-credentials.d.ts.map +1 -0
- package/packages/mcp-server/dist/tool-credentials.js +90 -0
- package/packages/mcp-server/dist/tool-credentials.js.map +1 -0
- package/packages/mcp-server/dist/workflow-tools.d.ts +3 -0
- package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.js +308 -4
- package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
- package/packages/mcp-server/src/cli.ts +5 -3
- package/packages/mcp-server/src/import-candidates.test.ts +48 -0
- package/packages/mcp-server/src/mcp-server.test.ts +85 -1
- package/packages/mcp-server/src/server.ts +188 -1
- package/packages/mcp-server/src/tool-credentials.test.ts +95 -0
- package/packages/mcp-server/src/tool-credentials.ts +97 -0
- package/packages/mcp-server/src/workflow-tools.test.ts +32 -25
- package/packages/mcp-server/src/workflow-tools.ts +398 -2
- 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-ai/dist/providers/anthropic.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic.js +1 -23
- package/packages/pi-ai/dist/providers/anthropic.js.map +1 -1
- package/packages/pi-ai/dist/utils/oauth/index.d.ts +3 -2
- package/packages/pi-ai/dist/utils/oauth/index.d.ts.map +1 -1
- package/packages/pi-ai/dist/utils/oauth/index.js +3 -5
- package/packages/pi-ai/dist/utils/oauth/index.js.map +1 -1
- package/packages/pi-ai/src/providers/anthropic.ts +1 -31
- package/packages/pi-ai/src/utils/oauth/index.ts +3 -5
- 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/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/package.json +1 -1
- 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/slash-command-handlers.ts +7 -5
- package/pkg/package.json +1 -1
- package/src/resources/extensions/claude-code-cli/stream-adapter.ts +227 -2
- package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +172 -0
- 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 +34 -1
- package/src/resources/extensions/gsd/auto.ts +12 -8
- package/src/resources/extensions/gsd/bootstrap/system-context.ts +9 -5
- package/src/resources/extensions/gsd/commands-cmux.ts +32 -1
- package/src/resources/extensions/gsd/commands-handlers.ts +22 -7
- 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/guided-flow.ts +24 -8
- package/src/resources/extensions/gsd/pre-execution-checks.ts +6 -3
- package/src/resources/extensions/gsd/tests/cmux.test.ts +67 -1
- package/src/resources/extensions/gsd/tests/doctor-scope-db-unavailable.test.ts +43 -0
- package/src/resources/extensions/gsd/tests/interactive-routing-bypass.test.ts +207 -0
- package/src/resources/extensions/gsd/tests/mcp-project-config.test.ts +6 -2
- 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/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.test.ts +48 -7
- package/src/resources/extensions/gsd/validate-directory.ts +33 -11
- package/src/resources/extensions/gsd/workflow-mcp.ts +74 -5
- 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/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/packages/pi-ai/dist/utils/oauth/anthropic.d.ts +0 -17
- package/packages/pi-ai/dist/utils/oauth/anthropic.d.ts.map +0 -1
- package/packages/pi-ai/dist/utils/oauth/anthropic.js +0 -106
- package/packages/pi-ai/dist/utils/oauth/anthropic.js.map +0 -1
- package/packages/pi-ai/src/utils/oauth/anthropic.ts +0 -140
- /package/dist/web/standalone/.next/static/{DrWdzskk28E5Qz-Wjw1mj → yvFbuOJuph5517lR7HBt2}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{DrWdzskk28E5Qz-Wjw1mj → yvFbuOJuph5517lR7HBt2}/_ssgManifest.js +0 -0
|
@@ -172,16 +172,49 @@ export function hasRootMarkers(cwd: string, markers: string[]): boolean {
|
|
|
172
172
|
// Local Binary Resolution
|
|
173
173
|
// =============================================================================
|
|
174
174
|
|
|
175
|
-
const LOCAL_BIN_PATHS: Array<{ markers: string[];
|
|
176
|
-
{ markers: ["package.json", "package-lock.json", "yarn.lock", "pnpm-lock.yaml"],
|
|
177
|
-
{ markers: ["pyproject.toml", "requirements.txt", "setup.py", "Pipfile"],
|
|
178
|
-
{ markers: ["pyproject.toml", "requirements.txt", "setup.py", "Pipfile"],
|
|
179
|
-
{ markers: ["pyproject.toml", "requirements.txt", "setup.py", "Pipfile"],
|
|
180
|
-
{ markers: ["Gemfile", "Gemfile.lock"],
|
|
181
|
-
{ markers: ["Gemfile", "Gemfile.lock"],
|
|
182
|
-
{ markers: ["go.mod", "go.sum"],
|
|
175
|
+
const LOCAL_BIN_PATHS: Array<{ markers: string[]; binDirs: string[] }> = [
|
|
176
|
+
{ markers: ["package.json", "package-lock.json", "yarn.lock", "pnpm-lock.yaml"], binDirs: ["node_modules/.bin"] },
|
|
177
|
+
{ markers: ["pyproject.toml", "requirements.txt", "setup.py", "Pipfile"], binDirs: [".venv/bin", ".venv/Scripts"] },
|
|
178
|
+
{ markers: ["pyproject.toml", "requirements.txt", "setup.py", "Pipfile"], binDirs: ["venv/bin", "venv/Scripts"] },
|
|
179
|
+
{ markers: ["pyproject.toml", "requirements.txt", "setup.py", "Pipfile"], binDirs: [".env/bin", ".env/Scripts"] },
|
|
180
|
+
{ markers: ["Gemfile", "Gemfile.lock"], binDirs: ["vendor/bundle/bin"] },
|
|
181
|
+
{ markers: ["Gemfile", "Gemfile.lock"], binDirs: ["bin"] },
|
|
182
|
+
{ markers: ["go.mod", "go.sum"], binDirs: ["bin"] },
|
|
183
183
|
];
|
|
184
184
|
|
|
185
|
+
function getWindowsBinaryCandidates(command: string): string[] {
|
|
186
|
+
const ext = path.extname(command).toLowerCase();
|
|
187
|
+
if (ext) {
|
|
188
|
+
return [command];
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
return [
|
|
192
|
+
command,
|
|
193
|
+
`${command}.cmd`,
|
|
194
|
+
`${command}.bat`,
|
|
195
|
+
`${command}.exe`,
|
|
196
|
+
];
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
export function resolveLocalBinaryPath(command: string, cwd: string, isWindows: boolean): string | null {
|
|
200
|
+
for (const { markers, binDirs } of LOCAL_BIN_PATHS) {
|
|
201
|
+
if (!hasRootMarkers(cwd, markers)) continue;
|
|
202
|
+
|
|
203
|
+
for (const binDir of binDirs) {
|
|
204
|
+
const basePath = path.join(cwd, binDir, command);
|
|
205
|
+
const candidates = isWindows ? getWindowsBinaryCandidates(basePath) : [basePath];
|
|
206
|
+
|
|
207
|
+
for (const candidate of candidates) {
|
|
208
|
+
if (fs.existsSync(candidate)) {
|
|
209
|
+
return candidate;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return null;
|
|
216
|
+
}
|
|
217
|
+
|
|
185
218
|
export function which(command: string): string | null {
|
|
186
219
|
// On Windows, prefer `where.exe` over `which` — MSYS/Git Bash's `which`
|
|
187
220
|
// returns POSIX paths (/c/Users/...) that Node's spawn() can't execute.
|
|
@@ -196,15 +229,8 @@ export function which(command: string): string | null {
|
|
|
196
229
|
}
|
|
197
230
|
|
|
198
231
|
export function resolveCommand(command: string, cwd: string): string | null {
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
const localPath = path.join(cwd, binDir, command);
|
|
202
|
-
if (fs.existsSync(localPath)) {
|
|
203
|
-
return localPath;
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
|
|
232
|
+
const localPath = resolveLocalBinaryPath(command, cwd, process.platform === "win32");
|
|
233
|
+
if (localPath) return localPath;
|
|
208
234
|
return which(command);
|
|
209
235
|
}
|
|
210
236
|
|
|
@@ -341,6 +341,14 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
341
341
|
thinkingBudgets: settingsManager.getThinkingBudgets(),
|
|
342
342
|
maxRetryDelayMs: settingsManager.getRetrySettings().maxDelayMs,
|
|
343
343
|
externalToolExecution: (m) => modelRegistry.getProviderAuthMode(m.provider) === "externalCli",
|
|
344
|
+
getProviderOptions: async (currentModel) => {
|
|
345
|
+
if (currentModel.provider !== "claude-code") return undefined;
|
|
346
|
+
const runner = extensionRunnerRef.current;
|
|
347
|
+
if (!runner?.hasUI()) return undefined;
|
|
348
|
+
return {
|
|
349
|
+
extensionUIContext: runner.getUIContext(),
|
|
350
|
+
};
|
|
351
|
+
},
|
|
344
352
|
getApiKey: async (provider) => {
|
|
345
353
|
// Use the provider argument from the in-flight request;
|
|
346
354
|
// agent.state.model may already be switched mid-turn.
|
|
@@ -305,11 +305,13 @@ async function handleShareCommand(ctx: SlashCommandContext): Promise<void> {
|
|
|
305
305
|
ctx.showStatus("Share cancelled");
|
|
306
306
|
};
|
|
307
307
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
308
|
+
try {
|
|
309
|
+
const result = await new Promise<{ stdout: string; stderr: string; code: number | null }>((resolve) => {
|
|
310
|
+
proc = spawn("gh", ["gist", "create", "--public=false", tmpFile], {
|
|
311
|
+
shell: process.platform === "win32",
|
|
312
|
+
});
|
|
313
|
+
let stdout = "";
|
|
314
|
+
let stderr = "";
|
|
313
315
|
proc.stdout?.on("data", (data) => {
|
|
314
316
|
stdout += data.toString();
|
|
315
317
|
});
|
package/pkg/package.json
CHANGED
|
@@ -16,10 +16,12 @@ import type {
|
|
|
16
16
|
SimpleStreamOptions,
|
|
17
17
|
ToolCall,
|
|
18
18
|
} from "@gsd/pi-ai";
|
|
19
|
+
import type { ExtensionUIContext } from "@gsd/pi-coding-agent";
|
|
19
20
|
import { EventStream } from "@gsd/pi-ai";
|
|
20
21
|
import { execSync } from "node:child_process";
|
|
21
22
|
import { PartialMessageBuilder, ZERO_USAGE, mapUsage } from "./partial-builder.js";
|
|
22
23
|
import { buildWorkflowMcpServers } from "../gsd/workflow-mcp.js";
|
|
24
|
+
import { showInterviewRound, type Question, type RoundResult } from "../shared/tui.js";
|
|
23
25
|
import type {
|
|
24
26
|
SDKAssistantMessage,
|
|
25
27
|
SDKMessage,
|
|
@@ -45,6 +47,46 @@ type ToolCallWithExternalResult = ToolCall & {
|
|
|
45
47
|
externalResult?: ExternalToolResultPayload;
|
|
46
48
|
};
|
|
47
49
|
|
|
50
|
+
interface ClaudeCodeStreamOptions extends SimpleStreamOptions {
|
|
51
|
+
extensionUIContext?: ExtensionUIContext;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
interface SdkElicitationRequestOption {
|
|
55
|
+
const?: string;
|
|
56
|
+
title?: string;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
interface SdkElicitationFieldSchema {
|
|
60
|
+
type?: string;
|
|
61
|
+
title?: string;
|
|
62
|
+
description?: string;
|
|
63
|
+
oneOf?: SdkElicitationRequestOption[];
|
|
64
|
+
items?: {
|
|
65
|
+
anyOf?: SdkElicitationRequestOption[];
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
interface SdkElicitationRequest {
|
|
70
|
+
serverName: string;
|
|
71
|
+
message: string;
|
|
72
|
+
mode?: "form" | "url";
|
|
73
|
+
requestedSchema?: {
|
|
74
|
+
type?: string;
|
|
75
|
+
properties?: Record<string, SdkElicitationFieldSchema>;
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
interface SdkElicitationResult {
|
|
80
|
+
action: "accept" | "decline" | "cancel";
|
|
81
|
+
content?: Record<string, string | string[]>;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
interface ParsedElicitationQuestion extends Question {
|
|
85
|
+
noteFieldId?: string;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const OTHER_OPTION_LABEL = "None of the above";
|
|
89
|
+
|
|
48
90
|
// ---------------------------------------------------------------------------
|
|
49
91
|
// Stream factory
|
|
50
92
|
// ---------------------------------------------------------------------------
|
|
@@ -172,6 +214,174 @@ export function makeStreamExhaustedErrorMessage(model: string, lastTextContent:
|
|
|
172
214
|
return message;
|
|
173
215
|
}
|
|
174
216
|
|
|
217
|
+
function readElicitationChoices(options: SdkElicitationRequestOption[] | undefined): string[] {
|
|
218
|
+
if (!Array.isArray(options)) return [];
|
|
219
|
+
return options
|
|
220
|
+
.map((option) => (typeof option?.const === "string" ? option.const : typeof option?.title === "string" ? option.title : ""))
|
|
221
|
+
.filter((option): option is string => option.length > 0);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
export function parseAskUserQuestionsElicitation(
|
|
225
|
+
request: Pick<SdkElicitationRequest, "mode" | "requestedSchema">,
|
|
226
|
+
): ParsedElicitationQuestion[] | null {
|
|
227
|
+
if (request.mode && request.mode !== "form") return null;
|
|
228
|
+
const properties = request.requestedSchema?.properties;
|
|
229
|
+
if (!properties || typeof properties !== "object") return null;
|
|
230
|
+
|
|
231
|
+
const questions: ParsedElicitationQuestion[] = [];
|
|
232
|
+
|
|
233
|
+
for (const [fieldId, rawField] of Object.entries(properties)) {
|
|
234
|
+
if (fieldId.endsWith("__note")) continue;
|
|
235
|
+
if (!rawField || typeof rawField !== "object") return null;
|
|
236
|
+
|
|
237
|
+
const header = typeof rawField.title === "string" && rawField.title.length > 0 ? rawField.title : fieldId;
|
|
238
|
+
const question = typeof rawField.description === "string" ? rawField.description : "";
|
|
239
|
+
|
|
240
|
+
if (rawField.type === "array") {
|
|
241
|
+
const options = readElicitationChoices(rawField.items?.anyOf).map((label) => ({ label, description: "" }));
|
|
242
|
+
if (options.length === 0) return null;
|
|
243
|
+
questions.push({
|
|
244
|
+
id: fieldId,
|
|
245
|
+
header,
|
|
246
|
+
question,
|
|
247
|
+
options,
|
|
248
|
+
allowMultiple: true,
|
|
249
|
+
});
|
|
250
|
+
continue;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
if (rawField.type === "string") {
|
|
254
|
+
const noteFieldId = Object.prototype.hasOwnProperty.call(properties, `${fieldId}__note`)
|
|
255
|
+
? `${fieldId}__note`
|
|
256
|
+
: undefined;
|
|
257
|
+
const options = readElicitationChoices(rawField.oneOf)
|
|
258
|
+
.filter((label) => label !== OTHER_OPTION_LABEL)
|
|
259
|
+
.map((label) => ({ label, description: "" }));
|
|
260
|
+
if (options.length === 0) return null;
|
|
261
|
+
questions.push({
|
|
262
|
+
id: fieldId,
|
|
263
|
+
header,
|
|
264
|
+
question,
|
|
265
|
+
options,
|
|
266
|
+
noteFieldId,
|
|
267
|
+
});
|
|
268
|
+
continue;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
return null;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
return questions.length > 0 ? questions : null;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
export function roundResultToElicitationContent(
|
|
278
|
+
questions: ParsedElicitationQuestion[],
|
|
279
|
+
result: RoundResult,
|
|
280
|
+
): Record<string, string | string[]> {
|
|
281
|
+
const content: Record<string, string | string[]> = {};
|
|
282
|
+
|
|
283
|
+
for (const question of questions) {
|
|
284
|
+
const answer = result.answers[question.id];
|
|
285
|
+
if (!answer) continue;
|
|
286
|
+
|
|
287
|
+
if (question.allowMultiple) {
|
|
288
|
+
const selected = Array.isArray(answer.selected) ? answer.selected : [answer.selected];
|
|
289
|
+
content[question.id] = selected;
|
|
290
|
+
continue;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
const selected = Array.isArray(answer.selected) ? answer.selected[0] ?? "" : answer.selected;
|
|
294
|
+
content[question.id] = selected;
|
|
295
|
+
if (question.noteFieldId && selected === OTHER_OPTION_LABEL && answer.notes.trim().length > 0) {
|
|
296
|
+
content[question.noteFieldId] = answer.notes.trim();
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
return content;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
function buildElicitationPromptTitle(request: SdkElicitationRequest, question: ParsedElicitationQuestion): string {
|
|
304
|
+
const parts = [
|
|
305
|
+
request.serverName ? `[${request.serverName}]` : "",
|
|
306
|
+
question.header,
|
|
307
|
+
question.question,
|
|
308
|
+
].filter((part) => part && part.trim().length > 0);
|
|
309
|
+
return parts.join("\n\n");
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
async function promptElicitationWithDialogs(
|
|
313
|
+
request: SdkElicitationRequest,
|
|
314
|
+
questions: ParsedElicitationQuestion[],
|
|
315
|
+
ui: ExtensionUIContext,
|
|
316
|
+
signal: AbortSignal,
|
|
317
|
+
): Promise<SdkElicitationResult> {
|
|
318
|
+
const content: Record<string, string | string[]> = {};
|
|
319
|
+
|
|
320
|
+
for (const question of questions) {
|
|
321
|
+
const title = buildElicitationPromptTitle(request, question);
|
|
322
|
+
|
|
323
|
+
if (question.allowMultiple) {
|
|
324
|
+
const selected = await ui.select(title, question.options.map((option) => option.label), {
|
|
325
|
+
allowMultiple: true,
|
|
326
|
+
signal,
|
|
327
|
+
});
|
|
328
|
+
if (Array.isArray(selected)) {
|
|
329
|
+
if (selected.length === 0) return { action: "cancel" };
|
|
330
|
+
content[question.id] = selected;
|
|
331
|
+
continue;
|
|
332
|
+
}
|
|
333
|
+
if (typeof selected === "string" && selected.length > 0) {
|
|
334
|
+
content[question.id] = [selected];
|
|
335
|
+
continue;
|
|
336
|
+
}
|
|
337
|
+
return { action: "cancel" };
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
const selected = await ui.select(title, [...question.options.map((option) => option.label), OTHER_OPTION_LABEL], { signal });
|
|
341
|
+
if (typeof selected !== "string" || selected.length === 0) {
|
|
342
|
+
return { action: "cancel" };
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
content[question.id] = selected;
|
|
346
|
+
if (question.noteFieldId && selected === OTHER_OPTION_LABEL) {
|
|
347
|
+
const note = await ui.input(`${question.header} note`, "Explain your answer", { signal });
|
|
348
|
+
if (note === undefined) return { action: "cancel" };
|
|
349
|
+
if (note.trim().length > 0) {
|
|
350
|
+
content[question.noteFieldId] = note.trim();
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
return { action: "accept", content };
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
export function createClaudeCodeElicitationHandler(
|
|
359
|
+
ui: ExtensionUIContext | undefined,
|
|
360
|
+
): ((request: SdkElicitationRequest, options: { signal: AbortSignal }) => Promise<SdkElicitationResult>) | undefined {
|
|
361
|
+
if (!ui) return undefined;
|
|
362
|
+
|
|
363
|
+
return async (request, { signal }) => {
|
|
364
|
+
if (request.mode === "url") {
|
|
365
|
+
return { action: "decline" };
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
const questions = parseAskUserQuestionsElicitation(request);
|
|
369
|
+
if (!questions) {
|
|
370
|
+
return { action: "decline" };
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
const interviewResult = await showInterviewRound(questions, { signal }, { ui } as any).catch(() => undefined);
|
|
374
|
+
if (interviewResult && Object.keys(interviewResult.answers).length > 0) {
|
|
375
|
+
return {
|
|
376
|
+
action: "accept",
|
|
377
|
+
content: roundResultToElicitationContent(questions, interviewResult),
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
return promptElicitationWithDialogs(request, questions, ui, signal);
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
|
|
175
385
|
// ---------------------------------------------------------------------------
|
|
176
386
|
// SDK options builder
|
|
177
387
|
// ---------------------------------------------------------------------------
|
|
@@ -182,7 +392,11 @@ export function makeStreamExhaustedErrorMessage(model: string, lastTextContent:
|
|
|
182
392
|
* Extracted for testability — callers can verify session persistence,
|
|
183
393
|
* beta flags, and other configuration without mocking the full SDK.
|
|
184
394
|
*/
|
|
185
|
-
export function buildSdkOptions(
|
|
395
|
+
export function buildSdkOptions(
|
|
396
|
+
modelId: string,
|
|
397
|
+
prompt: string,
|
|
398
|
+
extraOptions: Record<string, unknown> = {},
|
|
399
|
+
): Record<string, unknown> {
|
|
186
400
|
const mcpServers = buildWorkflowMcpServers();
|
|
187
401
|
return {
|
|
188
402
|
pathToClaudeCodeExecutable: getClaudePath(),
|
|
@@ -196,6 +410,7 @@ export function buildSdkOptions(modelId: string, prompt: string): Record<string,
|
|
|
196
410
|
systemPrompt: { type: "preset", preset: "claude_code" },
|
|
197
411
|
...(mcpServers ? { mcpServers } : {}),
|
|
198
412
|
betas: modelId.includes("sonnet") ? ["context-1m-2025-08-07"] : [],
|
|
413
|
+
...extraOptions,
|
|
199
414
|
};
|
|
200
415
|
}
|
|
201
416
|
|
|
@@ -359,7 +574,17 @@ async function pumpSdkMessages(
|
|
|
359
574
|
}
|
|
360
575
|
|
|
361
576
|
const prompt = buildPromptFromContext(context);
|
|
362
|
-
const sdkOpts = buildSdkOptions(
|
|
577
|
+
const sdkOpts = buildSdkOptions(
|
|
578
|
+
modelId,
|
|
579
|
+
prompt,
|
|
580
|
+
typeof (options as ClaudeCodeStreamOptions | undefined)?.extensionUIContext === "object"
|
|
581
|
+
? {
|
|
582
|
+
onElicitation: createClaudeCodeElicitationHandler(
|
|
583
|
+
(options as ClaudeCodeStreamOptions | undefined)?.extensionUIContext,
|
|
584
|
+
),
|
|
585
|
+
}
|
|
586
|
+
: {},
|
|
587
|
+
);
|
|
363
588
|
|
|
364
589
|
const queryResult = sdk.query({
|
|
365
590
|
prompt,
|
|
@@ -7,9 +7,12 @@ import {
|
|
|
7
7
|
makeStreamExhaustedErrorMessage,
|
|
8
8
|
buildPromptFromContext,
|
|
9
9
|
buildSdkOptions,
|
|
10
|
+
createClaudeCodeElicitationHandler,
|
|
10
11
|
extractToolResultsFromSdkUserMessage,
|
|
11
12
|
getClaudeLookupCommand,
|
|
13
|
+
parseAskUserQuestionsElicitation,
|
|
12
14
|
parseClaudeLookupOutput,
|
|
15
|
+
roundResultToElicitationContent,
|
|
13
16
|
} from "../stream-adapter.ts";
|
|
14
17
|
import type { Context, Message } from "@gsd/pi-ai";
|
|
15
18
|
import type { SDKUserMessage } from "../sdk-types.ts";
|
|
@@ -309,6 +312,175 @@ describe("stream-adapter — session persistence (#2859)", () => {
|
|
|
309
312
|
process.env.GSD_CLI_PATH = prev.GSD_CLI_PATH;
|
|
310
313
|
}
|
|
311
314
|
});
|
|
315
|
+
|
|
316
|
+
test("buildSdkOptions preserves runtime callbacks such as onElicitation", () => {
|
|
317
|
+
const prev = {
|
|
318
|
+
GSD_WORKFLOW_MCP_COMMAND: process.env.GSD_WORKFLOW_MCP_COMMAND,
|
|
319
|
+
GSD_WORKFLOW_MCP_NAME: process.env.GSD_WORKFLOW_MCP_NAME,
|
|
320
|
+
GSD_WORKFLOW_MCP_ARGS: process.env.GSD_WORKFLOW_MCP_ARGS,
|
|
321
|
+
GSD_WORKFLOW_MCP_ENV: process.env.GSD_WORKFLOW_MCP_ENV,
|
|
322
|
+
GSD_WORKFLOW_MCP_CWD: process.env.GSD_WORKFLOW_MCP_CWD,
|
|
323
|
+
};
|
|
324
|
+
const onElicitation = async () => ({ action: "decline" as const });
|
|
325
|
+
try {
|
|
326
|
+
delete process.env.GSD_WORKFLOW_MCP_COMMAND;
|
|
327
|
+
delete process.env.GSD_WORKFLOW_MCP_NAME;
|
|
328
|
+
delete process.env.GSD_WORKFLOW_MCP_ARGS;
|
|
329
|
+
delete process.env.GSD_WORKFLOW_MCP_ENV;
|
|
330
|
+
delete process.env.GSD_WORKFLOW_MCP_CWD;
|
|
331
|
+
const options = buildSdkOptions("claude-sonnet-4-20250514", "test", { onElicitation });
|
|
332
|
+
assert.equal(options.onElicitation, onElicitation);
|
|
333
|
+
} finally {
|
|
334
|
+
process.env.GSD_WORKFLOW_MCP_COMMAND = prev.GSD_WORKFLOW_MCP_COMMAND;
|
|
335
|
+
process.env.GSD_WORKFLOW_MCP_NAME = prev.GSD_WORKFLOW_MCP_NAME;
|
|
336
|
+
process.env.GSD_WORKFLOW_MCP_ARGS = prev.GSD_WORKFLOW_MCP_ARGS;
|
|
337
|
+
process.env.GSD_WORKFLOW_MCP_ENV = prev.GSD_WORKFLOW_MCP_ENV;
|
|
338
|
+
process.env.GSD_WORKFLOW_MCP_CWD = prev.GSD_WORKFLOW_MCP_CWD;
|
|
339
|
+
}
|
|
340
|
+
});
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
describe("stream-adapter — MCP elicitation bridge", () => {
|
|
344
|
+
const askUserQuestionsRequest = {
|
|
345
|
+
serverName: "gsd-workflow",
|
|
346
|
+
message: "Please answer the following question(s).",
|
|
347
|
+
mode: "form" as const,
|
|
348
|
+
requestedSchema: {
|
|
349
|
+
type: "object" as const,
|
|
350
|
+
properties: {
|
|
351
|
+
storage_scope: {
|
|
352
|
+
type: "string",
|
|
353
|
+
title: "Storage",
|
|
354
|
+
description: "Does this app need to sync across devices?",
|
|
355
|
+
oneOf: [
|
|
356
|
+
{ const: "Local-only (Recommended)", title: "Local-only (Recommended)" },
|
|
357
|
+
{ const: "Cloud-synced", title: "Cloud-synced" },
|
|
358
|
+
{ const: "None of the above", title: "None of the above" },
|
|
359
|
+
],
|
|
360
|
+
},
|
|
361
|
+
storage_scope__note: {
|
|
362
|
+
type: "string",
|
|
363
|
+
title: "Storage Note",
|
|
364
|
+
description: "Optional note for None of the above.",
|
|
365
|
+
},
|
|
366
|
+
platform: {
|
|
367
|
+
type: "array",
|
|
368
|
+
title: "Platform",
|
|
369
|
+
description: "Where should it run?",
|
|
370
|
+
items: {
|
|
371
|
+
anyOf: [
|
|
372
|
+
{ const: "Web", title: "Web" },
|
|
373
|
+
{ const: "Desktop", title: "Desktop" },
|
|
374
|
+
{ const: "Mobile", title: "Mobile" },
|
|
375
|
+
],
|
|
376
|
+
},
|
|
377
|
+
},
|
|
378
|
+
},
|
|
379
|
+
},
|
|
380
|
+
};
|
|
381
|
+
|
|
382
|
+
test("parseAskUserQuestionsElicitation rebuilds interview questions from the MCP schema", () => {
|
|
383
|
+
const questions = parseAskUserQuestionsElicitation(askUserQuestionsRequest);
|
|
384
|
+
assert.deepEqual(questions, [
|
|
385
|
+
{
|
|
386
|
+
id: "storage_scope",
|
|
387
|
+
header: "Storage",
|
|
388
|
+
question: "Does this app need to sync across devices?",
|
|
389
|
+
options: [
|
|
390
|
+
{ label: "Local-only (Recommended)", description: "" },
|
|
391
|
+
{ label: "Cloud-synced", description: "" },
|
|
392
|
+
],
|
|
393
|
+
noteFieldId: "storage_scope__note",
|
|
394
|
+
},
|
|
395
|
+
{
|
|
396
|
+
id: "platform",
|
|
397
|
+
header: "Platform",
|
|
398
|
+
question: "Where should it run?",
|
|
399
|
+
options: [
|
|
400
|
+
{ label: "Web", description: "" },
|
|
401
|
+
{ label: "Desktop", description: "" },
|
|
402
|
+
{ label: "Mobile", description: "" },
|
|
403
|
+
],
|
|
404
|
+
allowMultiple: true,
|
|
405
|
+
},
|
|
406
|
+
]);
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
test("roundResultToElicitationContent preserves notes for None of the above", () => {
|
|
410
|
+
const questions = parseAskUserQuestionsElicitation(askUserQuestionsRequest);
|
|
411
|
+
assert.ok(questions);
|
|
412
|
+
|
|
413
|
+
const content = roundResultToElicitationContent(questions, {
|
|
414
|
+
endInterview: false,
|
|
415
|
+
answers: {
|
|
416
|
+
storage_scope: {
|
|
417
|
+
selected: "None of the above",
|
|
418
|
+
notes: "Needs selective sync later",
|
|
419
|
+
},
|
|
420
|
+
platform: {
|
|
421
|
+
selected: ["Web", "Desktop"],
|
|
422
|
+
notes: "",
|
|
423
|
+
},
|
|
424
|
+
},
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
assert.deepEqual(content, {
|
|
428
|
+
storage_scope: "None of the above",
|
|
429
|
+
storage_scope__note: "Needs selective sync later",
|
|
430
|
+
platform: ["Web", "Desktop"],
|
|
431
|
+
});
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
test("createClaudeCodeElicitationHandler accepts interview-style answers from custom UI", async () => {
|
|
435
|
+
const handler = createClaudeCodeElicitationHandler({
|
|
436
|
+
custom: async (_factory: any) => ({
|
|
437
|
+
endInterview: false,
|
|
438
|
+
answers: {
|
|
439
|
+
storage_scope: {
|
|
440
|
+
selected: "Cloud-synced",
|
|
441
|
+
notes: "",
|
|
442
|
+
},
|
|
443
|
+
platform: {
|
|
444
|
+
selected: ["Web", "Mobile"],
|
|
445
|
+
notes: "",
|
|
446
|
+
},
|
|
447
|
+
},
|
|
448
|
+
}),
|
|
449
|
+
} as any);
|
|
450
|
+
|
|
451
|
+
assert.ok(handler);
|
|
452
|
+
const result = await handler!(askUserQuestionsRequest, { signal: new AbortController().signal });
|
|
453
|
+
assert.deepEqual(result, {
|
|
454
|
+
action: "accept",
|
|
455
|
+
content: {
|
|
456
|
+
storage_scope: "Cloud-synced",
|
|
457
|
+
platform: ["Web", "Mobile"],
|
|
458
|
+
},
|
|
459
|
+
});
|
|
460
|
+
});
|
|
461
|
+
|
|
462
|
+
test("createClaudeCodeElicitationHandler falls back to dialog prompts when custom UI is unavailable", async () => {
|
|
463
|
+
const ui = {
|
|
464
|
+
custom: async () => undefined,
|
|
465
|
+
select: async (_title: string, options: string[], opts?: { allowMultiple?: boolean }) => {
|
|
466
|
+
if (opts?.allowMultiple) return ["Desktop", "Mobile"];
|
|
467
|
+
return options.includes("None of the above") ? "None of the above" : options[0];
|
|
468
|
+
},
|
|
469
|
+
input: async () => "CLI-only deployment target",
|
|
470
|
+
};
|
|
471
|
+
const handler = createClaudeCodeElicitationHandler(ui as any);
|
|
472
|
+
assert.ok(handler);
|
|
473
|
+
|
|
474
|
+
const result = await handler!(askUserQuestionsRequest, { signal: new AbortController().signal });
|
|
475
|
+
assert.deepEqual(result, {
|
|
476
|
+
action: "accept",
|
|
477
|
+
content: {
|
|
478
|
+
storage_scope: "None of the above",
|
|
479
|
+
storage_scope__note: "CLI-only deployment target",
|
|
480
|
+
platform: ["Desktop", "Mobile"],
|
|
481
|
+
},
|
|
482
|
+
});
|
|
483
|
+
});
|
|
312
484
|
});
|
|
313
485
|
|
|
314
486
|
describe("stream-adapter — Windows Claude path lookup (#3770)", () => {
|