gsd-pi 2.70.1 → 2.71.0-dev.4c35d99
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 +57 -17
- package/dist/cli.js +29 -3
- package/dist/headless-events.d.ts +2 -0
- package/dist/headless-events.js +7 -0
- package/dist/headless.js +16 -3
- package/dist/mcp-server.js +40 -17
- package/dist/provider-migrations.d.ts +10 -0
- package/dist/provider-migrations.js +12 -0
- package/dist/resource-loader.js +139 -13
- package/dist/resources/GSD-WORKFLOW.md +1 -1
- package/dist/resources/agents/debugger.md +58 -0
- package/dist/resources/agents/doc-writer.md +43 -0
- package/dist/resources/agents/git-ops.md +56 -0
- package/dist/resources/agents/javascript-pro.md +46 -271
- package/dist/resources/agents/planner.md +55 -0
- package/dist/resources/agents/refactorer.md +47 -0
- package/dist/resources/agents/reviewer.md +48 -0
- package/dist/resources/agents/security.md +59 -0
- package/dist/resources/agents/tester.md +50 -0
- package/dist/resources/agents/typescript-pro.md +41 -235
- package/dist/resources/extensions/claude-code-cli/stream-adapter.js +242 -40
- package/dist/resources/extensions/get-secrets-from-user.js +17 -1
- package/dist/resources/extensions/gsd/auto/infra-errors.js +34 -0
- package/dist/resources/extensions/gsd/auto/loop.js +32 -1
- package/dist/resources/extensions/gsd/auto/phases.js +5 -1
- package/dist/resources/extensions/gsd/auto/session.js +11 -0
- package/dist/resources/extensions/gsd/auto-dashboard.js +22 -16
- package/dist/resources/extensions/gsd/auto-model-selection.js +10 -2
- package/dist/resources/extensions/gsd/auto-prompts.js +88 -33
- package/dist/resources/extensions/gsd/auto-start.js +37 -18
- package/dist/resources/extensions/gsd/auto-tool-tracking.js +1 -1
- package/dist/resources/extensions/gsd/auto-worktree.js +1 -1
- package/dist/resources/extensions/gsd/auto.js +56 -0
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +3 -3
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +6 -0
- package/dist/resources/extensions/gsd/bootstrap/register-shortcuts.js +63 -51
- 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/commands/handlers/auto.js +10 -33
- package/dist/resources/extensions/gsd/commands/handlers/core.js +56 -11
- package/dist/resources/extensions/gsd/commands/handlers/notifications-handler.js +15 -6
- package/dist/resources/extensions/gsd/commands/handlers/workflow.js +4 -10
- package/dist/resources/extensions/gsd/custom-workflow-engine.js +16 -12
- package/dist/resources/extensions/gsd/dashboard-overlay.js +8 -3
- package/dist/resources/extensions/gsd/dispatch-guard.js +18 -1
- package/dist/resources/extensions/gsd/doctor-providers.js +23 -0
- 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/forensics.js +19 -6
- package/dist/resources/extensions/gsd/gate-registry.js +208 -0
- package/dist/resources/extensions/gsd/gsd-db.js +41 -0
- package/dist/resources/extensions/gsd/guided-flow.js +17 -20
- package/dist/resources/extensions/gsd/init-wizard.js +3 -11
- package/dist/resources/extensions/gsd/metrics.js +1 -0
- package/dist/resources/extensions/gsd/milestone-actions.js +10 -4
- package/dist/resources/extensions/gsd/milestone-validation-gates.js +11 -12
- package/dist/resources/extensions/gsd/notification-overlay.js +42 -13
- package/dist/resources/extensions/gsd/notification-store.js +56 -5
- package/dist/resources/extensions/gsd/notification-widget.js +5 -13
- package/dist/resources/extensions/gsd/parallel-monitor-overlay.js +8 -3
- package/dist/resources/extensions/gsd/pre-execution-checks.js +35 -2
- package/dist/resources/extensions/gsd/prompt-validation.js +126 -0
- package/dist/resources/extensions/gsd/prompts/complete-slice.md +5 -3
- package/dist/resources/extensions/gsd/prompts/discuss.md +33 -13
- package/dist/resources/extensions/gsd/prompts/execute-task.md +22 -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 +4 -1
- package/dist/resources/extensions/gsd/session-model-override.js +25 -0
- package/dist/resources/extensions/gsd/shortcut-defs.js +40 -0
- package/dist/resources/extensions/gsd/state.js +241 -332
- package/dist/resources/extensions/gsd/tools/complete-slice.js +52 -1
- package/dist/resources/extensions/gsd/tools/complete-task.js +51 -1
- package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +38 -1
- 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/extensions/ollama/index.js +13 -5
- package/dist/resources/extensions/shared/gsd-phase-state.js +35 -0
- package/dist/resources/extensions/subagent/agents.js +8 -0
- package/dist/resources/extensions/subagent/index.js +17 -0
- package/dist/resources/skills/create-skill/SKILL.md +2 -0
- package/dist/startup-model-validation.d.ts +0 -1
- package/dist/startup-model-validation.js +6 -2
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +17 -17
- 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 +17 -17
- 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 +23 -3
- package/packages/mcp-server/dist/server.d.ts.map +1 -1
- package/packages/mcp-server/dist/server.js +192 -44
- 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 +22 -12
- 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 +247 -41
- package/packages/mcp-server/src/workflow-tools.test.ts +110 -0
- package/packages/mcp-server/src/workflow-tools.ts +32 -12
- 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-auth.test.d.ts +2 -0
- package/packages/pi-ai/dist/providers/anthropic-auth.test.d.ts.map +1 -0
- package/packages/pi-ai/dist/providers/anthropic-auth.test.js +20 -0
- package/packages/pi-ai/dist/providers/anthropic-auth.test.js.map +1 -0
- 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/anthropic.d.ts +2 -1
- package/packages/pi-ai/dist/providers/anthropic.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic.js +7 -4
- package/packages/pi-ai/dist/providers/anthropic.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-auth.test.ts +32 -0
- 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/anthropic.ts +8 -4
- package/packages/pi-ai/src/providers/openai-completions.ts +14 -0
- package/packages/pi-coding-agent/dist/core/agent-session-renderable-tools.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/agent-session-renderable-tools.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/agent-session-renderable-tools.test.js +61 -0
- package/packages/pi-coding-agent/dist/core/agent-session-renderable-tools.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.js +2 -1
- package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.d.ts +10 -0
- package/packages/pi-coding-agent/dist/core/auth-storage.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.js +27 -0
- package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.test.js +85 -0
- package/packages/pi-coding-agent/dist/core/auth-storage.test.js.map +1 -1
- 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/core/model-resolver-initial-model-auth.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/model-resolver-initial-model-auth.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-resolver-initial-model-auth.test.js +64 -0
- package/packages/pi-coding-agent/dist/core/model-resolver-initial-model-auth.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-resolver.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-resolver.js +22 -18
- package/packages/pi-coding-agent/dist/core/model-resolver.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-resolver.test.d.ts +8 -0
- package/packages/pi-coding-agent/dist/core/model-resolver.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-resolver.test.js +75 -0
- package/packages/pi-coding-agent/dist/core/model-resolver.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/sdk.d.ts +11 -0
- package/packages/pi-coding-agent/dist/core/sdk.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/sdk.js +38 -5
- package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/sdk.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/sdk.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/sdk.test.js +71 -0
- package/packages/pi-coding-agent/dist/core/sdk.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/index.d.ts +1 -1
- package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/index.js +1 -1
- package/packages/pi-coding-agent/dist/index.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/login-dialog.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/login-dialog.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/login-dialog.test.js +13 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/login-dialog.test.js.map +1 -0
- 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/components/login-dialog.d.ts +4 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.js +24 -2
- package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.js +9 -2
- package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts +4 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +43 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.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 +175 -25
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/model-controller.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/model-controller.js +6 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/model-controller.js.map +1 -1
- package/packages/pi-coding-agent/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 +62 -5
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js +4 -2
- package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-types.d.ts +1 -0
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-types.js.map +1 -1
- package/packages/pi-coding-agent/package.json +1 -1
- package/packages/pi-coding-agent/src/core/agent-session-renderable-tools.test.ts +70 -0
- package/packages/pi-coding-agent/src/core/agent-session.ts +2 -1
- package/packages/pi-coding-agent/src/core/auth-storage.test.ts +108 -0
- package/packages/pi-coding-agent/src/core/auth-storage.ts +30 -0
- 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/core/model-resolver-initial-model-auth.test.ts +78 -0
- package/packages/pi-coding-agent/src/core/model-resolver.test.ts +85 -0
- package/packages/pi-coding-agent/src/core/model-resolver.ts +22 -18
- package/packages/pi-coding-agent/src/core/sdk.test.ts +89 -0
- package/packages/pi-coding-agent/src/core/sdk.ts +45 -9
- package/packages/pi-coding-agent/src/index.ts +1 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/login-dialog.test.ts +24 -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/components/login-dialog.ts +30 -2
- package/packages/pi-coding-agent/src/modes/interactive/components/model-selector.ts +15 -6
- package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +47 -0
- package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +205 -31
- package/packages/pi-coding-agent/src/modes/interactive/controllers/model-controller.ts +6 -1
- 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 +70 -5
- package/packages/pi-coding-agent/src/modes/interactive/slash-command-handlers.ts +4 -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/agents/debugger.md +58 -0
- package/src/resources/agents/doc-writer.md +43 -0
- package/src/resources/agents/git-ops.md +56 -0
- package/src/resources/agents/javascript-pro.md +46 -271
- package/src/resources/agents/planner.md +55 -0
- package/src/resources/agents/refactorer.md +47 -0
- package/src/resources/agents/reviewer.md +48 -0
- package/src/resources/agents/security.md +59 -0
- package/src/resources/agents/tester.md +50 -0
- package/src/resources/agents/typescript-pro.md +41 -235
- package/src/resources/extensions/claude-code-cli/stream-adapter.ts +288 -39
- package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +330 -2
- package/src/resources/extensions/get-secrets-from-user.ts +24 -1
- package/src/resources/extensions/gsd/auto/infra-errors.ts +38 -0
- package/src/resources/extensions/gsd/auto/loop-deps.ts +2 -0
- package/src/resources/extensions/gsd/auto/loop.ts +45 -1
- package/src/resources/extensions/gsd/auto/phases.ts +6 -0
- package/src/resources/extensions/gsd/auto/session.ts +11 -0
- package/src/resources/extensions/gsd/auto-dashboard.ts +29 -18
- package/src/resources/extensions/gsd/auto-model-selection.ts +9 -1
- package/src/resources/extensions/gsd/auto-prompts.ts +111 -33
- package/src/resources/extensions/gsd/auto-start.ts +44 -20
- package/src/resources/extensions/gsd/auto-tool-tracking.ts +1 -1
- package/src/resources/extensions/gsd/auto-worktree.ts +1 -1
- package/src/resources/extensions/gsd/auto.ts +72 -0
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +3 -3
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +6 -0
- package/src/resources/extensions/gsd/bootstrap/register-shortcuts.ts +79 -60
- 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/commands/handlers/auto.ts +10 -36
- package/src/resources/extensions/gsd/commands/handlers/core.ts +58 -11
- package/src/resources/extensions/gsd/commands/handlers/notifications-handler.ts +17 -7
- package/src/resources/extensions/gsd/commands/handlers/workflow.ts +4 -10
- package/src/resources/extensions/gsd/custom-workflow-engine.ts +19 -14
- package/src/resources/extensions/gsd/dashboard-overlay.ts +10 -3
- package/src/resources/extensions/gsd/dispatch-guard.ts +18 -1
- package/src/resources/extensions/gsd/doctor-providers.ts +24 -0
- 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/forensics.ts +23 -7
- package/src/resources/extensions/gsd/gate-registry.ts +251 -0
- package/src/resources/extensions/gsd/gsd-db.ts +51 -0
- package/src/resources/extensions/gsd/guided-flow.ts +17 -19
- package/src/resources/extensions/gsd/init-wizard.ts +3 -13
- package/src/resources/extensions/gsd/interrupted-session.ts +1 -0
- package/src/resources/extensions/gsd/metrics.ts +12 -1
- package/src/resources/extensions/gsd/milestone-actions.ts +10 -3
- package/src/resources/extensions/gsd/milestone-validation-gates.ts +11 -13
- package/src/resources/extensions/gsd/notification-overlay.ts +47 -14
- package/src/resources/extensions/gsd/notification-store.ts +54 -5
- package/src/resources/extensions/gsd/notification-widget.ts +5 -14
- package/src/resources/extensions/gsd/parallel-monitor-overlay.ts +10 -3
- package/src/resources/extensions/gsd/pre-execution-checks.ts +39 -2
- package/src/resources/extensions/gsd/prompt-validation.ts +157 -0
- package/src/resources/extensions/gsd/prompts/complete-slice.md +5 -3
- package/src/resources/extensions/gsd/prompts/discuss.md +33 -13
- package/src/resources/extensions/gsd/prompts/execute-task.md +22 -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 +4 -1
- package/src/resources/extensions/gsd/session-model-override.ts +36 -0
- package/src/resources/extensions/gsd/shortcut-defs.ts +56 -0
- package/src/resources/extensions/gsd/state.ts +285 -344
- package/src/resources/extensions/gsd/tests/auto-start-model-capture.test.ts +25 -9
- 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-gate-closure.test.ts +167 -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/doctor-providers.test.ts +36 -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/forensics-stuck-loops.test.ts +62 -0
- package/src/resources/extensions/gsd/tests/format-shortcut.test.ts +31 -0
- package/src/resources/extensions/gsd/tests/gate-dispatch.test.ts +27 -0
- package/src/resources/extensions/gsd/tests/gate-registry.test.ts +140 -0
- package/src/resources/extensions/gsd/tests/gsd-no-project-error.test.ts +73 -0
- package/src/resources/extensions/gsd/tests/infra-errors-cooldown.test.ts +180 -0
- package/src/resources/extensions/gsd/tests/integration/auto-worktree-milestone-merge.test.ts +66 -1
- package/src/resources/extensions/gsd/tests/model-isolation.test.ts +36 -51
- package/src/resources/extensions/gsd/tests/notification-store.test.ts +35 -0
- package/src/resources/extensions/gsd/tests/notification-widget.test.ts +26 -0
- package/src/resources/extensions/gsd/tests/notifications-handler.test.ts +90 -0
- package/src/resources/extensions/gsd/tests/parallel-monitor-overlay.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/park-db-sync.test.ts +18 -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/prompt-system-gate-coverage.test.ts +208 -0
- package/src/resources/extensions/gsd/tests/provider-errors.test.ts +7 -0
- package/src/resources/extensions/gsd/tests/register-shortcuts.test.ts +63 -5
- package/src/resources/extensions/gsd/tests/secure-env-collect.test.ts +45 -0
- package/src/resources/extensions/gsd/tests/session-model-override.test.ts +35 -0
- package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +90 -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/complete-slice.ts +63 -0
- package/src/resources/extensions/gsd/tools/complete-task.ts +63 -0
- package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +64 -26
- package/src/resources/extensions/gsd/types.ts +26 -0
- 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/extensions/ollama/index.ts +13 -3
- package/src/resources/extensions/ollama/ollama-status-indicator.test.ts +28 -0
- package/src/resources/extensions/shared/gsd-phase-state.ts +42 -0
- package/src/resources/extensions/shared/tests/gsd-phase-state.test.ts +48 -0
- package/src/resources/extensions/subagent/agents.ts +10 -0
- package/src/resources/extensions/subagent/index.ts +18 -0
- package/src/resources/extensions/subagent/tests/agents-conflicts.test.ts +33 -0
- package/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 → OI4n_CKC-lM8IQbvGJ_tK}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{9pw9EXtXjdM7EFrCXUEPf → OI4n_CKC-lM8IQbvGJ_tK}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
// @gsd-build/mcp-server — Tests for secure_env_collect MCP tool
|
|
2
|
+
// Copyright (c) 2026 Jeremy McSpadden <jeremy@fluxlabs.net>
|
|
3
|
+
//
|
|
4
|
+
// Tests the secure_env_collect tool registered in createMcpServer.
|
|
5
|
+
// Uses a mock MCP server to intercept tool registration and elicitInput calls.
|
|
6
|
+
|
|
7
|
+
import { describe, it, beforeEach } from 'node:test';
|
|
8
|
+
import assert from 'node:assert/strict';
|
|
9
|
+
import { mkdtempSync, mkdirSync, rmSync, writeFileSync, readFileSync } from 'node:fs';
|
|
10
|
+
import { tmpdir } from 'node:os';
|
|
11
|
+
import { join } from 'node:path';
|
|
12
|
+
|
|
13
|
+
import { createMcpServer } from './server.js';
|
|
14
|
+
import { SessionManager } from './session-manager.js';
|
|
15
|
+
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
// Mock infrastructure
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* We intercept McpServer construction by monkey-patching the dynamic import.
|
|
22
|
+
* Instead, we'll test the tool handler indirectly through the exported
|
|
23
|
+
* createMcpServer function — capturing the registered tool handlers.
|
|
24
|
+
*
|
|
25
|
+
* Since createMcpServer dynamically imports McpServer, we need to test at
|
|
26
|
+
* a level that exercises the tool handler logic. We do this by extracting
|
|
27
|
+
* the tool handler through the server.tool() calls.
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
interface RegisteredTool {
|
|
31
|
+
name: string;
|
|
32
|
+
description: string;
|
|
33
|
+
params: Record<string, unknown>;
|
|
34
|
+
handler: (args: Record<string, unknown>) => Promise<unknown>;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
interface ToolResult {
|
|
38
|
+
content?: Array<{ type: string; text: string }>;
|
|
39
|
+
isError?: boolean;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Mock McpServer that captures tool registrations and provides
|
|
44
|
+
* a controllable elicitInput response.
|
|
45
|
+
*/
|
|
46
|
+
class MockMcpServer {
|
|
47
|
+
registeredTools: RegisteredTool[] = [];
|
|
48
|
+
elicitResponse: { action: string; content?: Record<string, unknown> } = { action: 'accept', content: {} };
|
|
49
|
+
|
|
50
|
+
server = {
|
|
51
|
+
elicitInput: async (_params: unknown) => {
|
|
52
|
+
return this.elicitResponse;
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
tool(name: string, description: string, params: Record<string, unknown>, handler: (args: Record<string, unknown>) => Promise<unknown>) {
|
|
57
|
+
this.registeredTools.push({ name, description, params, handler });
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async connect(_transport: unknown) { /* no-op */ }
|
|
61
|
+
async close() { /* no-op */ }
|
|
62
|
+
|
|
63
|
+
getToolHandler(name: string): ((args: Record<string, unknown>) => Promise<unknown>) | undefined {
|
|
64
|
+
return this.registeredTools.find((t) => t.name === name)?.handler;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// ---------------------------------------------------------------------------
|
|
69
|
+
// Helper to create a mock MCP server with secure_env_collect registered
|
|
70
|
+
// ---------------------------------------------------------------------------
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Since createMcpServer uses dynamic import for McpServer, we can't easily
|
|
74
|
+
* mock it. Instead, we test the env-writer utilities directly (in env-writer.test.ts)
|
|
75
|
+
* and test the tool integration by verifying:
|
|
76
|
+
* 1. The tool exists in the registered tools list
|
|
77
|
+
* 2. The handler produces correct results with mock data
|
|
78
|
+
*
|
|
79
|
+
* For handler-level testing, we create a standalone test that replicates
|
|
80
|
+
* the tool handler logic with a controllable mock.
|
|
81
|
+
*/
|
|
82
|
+
|
|
83
|
+
function makeTempDir(prefix: string): string {
|
|
84
|
+
return mkdtempSync(join(tmpdir(), `${prefix}-`));
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// ---------------------------------------------------------------------------
|
|
88
|
+
// Integration test — verify tool is registered
|
|
89
|
+
// ---------------------------------------------------------------------------
|
|
90
|
+
|
|
91
|
+
describe('secure_env_collect tool registration', () => {
|
|
92
|
+
it('createMcpServer registers secure_env_collect tool', async () => {
|
|
93
|
+
// This test verifies the tool exists — createMcpServer internally calls
|
|
94
|
+
// server.tool('secure_env_collect', ...) which we can't intercept without
|
|
95
|
+
// module mocking, but we can verify the server creates successfully
|
|
96
|
+
const sm = new SessionManager();
|
|
97
|
+
try {
|
|
98
|
+
const { server } = await createMcpServer(sm);
|
|
99
|
+
assert.ok(server, 'server should be created');
|
|
100
|
+
// The McpServer internally tracks registered tools — we verify no error
|
|
101
|
+
} finally {
|
|
102
|
+
await sm.cleanup();
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
// ---------------------------------------------------------------------------
|
|
108
|
+
// Handler logic tests — using env-writer directly to test the flow
|
|
109
|
+
// ---------------------------------------------------------------------------
|
|
110
|
+
|
|
111
|
+
describe('secure_env_collect handler logic', () => {
|
|
112
|
+
it('skips keys that already exist in .env', async () => {
|
|
113
|
+
const tmp = makeTempDir('sec-collect');
|
|
114
|
+
try {
|
|
115
|
+
const envPath = join(tmp, '.env');
|
|
116
|
+
writeFileSync(envPath, 'ALREADY_SET=existing-value\n');
|
|
117
|
+
|
|
118
|
+
// Import the utility directly to test the pre-check logic
|
|
119
|
+
const { checkExistingEnvKeys } = await import('./env-writer.js');
|
|
120
|
+
const existing = await checkExistingEnvKeys(['ALREADY_SET', 'NEW_KEY'], envPath);
|
|
121
|
+
assert.deepStrictEqual(existing, ['ALREADY_SET']);
|
|
122
|
+
} finally {
|
|
123
|
+
rmSync(tmp, { recursive: true, force: true });
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it('writes collected values to .env without returning secret values', async () => {
|
|
128
|
+
const tmp = makeTempDir('sec-collect');
|
|
129
|
+
try {
|
|
130
|
+
const envPath = join(tmp, '.env');
|
|
131
|
+
const savedKey = process.env.SEC_COLLECT_TEST_KEY;
|
|
132
|
+
|
|
133
|
+
const { applySecrets } = await import('./env-writer.js');
|
|
134
|
+
const { applied, errors } = await applySecrets(
|
|
135
|
+
[{ key: 'SEC_COLLECT_TEST_KEY', value: 'super-secret-value' }],
|
|
136
|
+
'dotenv',
|
|
137
|
+
{ envFilePath: envPath },
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
assert.deepStrictEqual(applied, ['SEC_COLLECT_TEST_KEY']);
|
|
141
|
+
assert.deepStrictEqual(errors, []);
|
|
142
|
+
|
|
143
|
+
// Verify the value was written
|
|
144
|
+
const content = readFileSync(envPath, 'utf8');
|
|
145
|
+
assert.ok(content.includes('SEC_COLLECT_TEST_KEY=super-secret-value'));
|
|
146
|
+
|
|
147
|
+
// Verify process.env was hydrated
|
|
148
|
+
assert.equal(process.env.SEC_COLLECT_TEST_KEY, 'super-secret-value');
|
|
149
|
+
|
|
150
|
+
// Cleanup
|
|
151
|
+
if (savedKey === undefined) delete process.env.SEC_COLLECT_TEST_KEY;
|
|
152
|
+
else process.env.SEC_COLLECT_TEST_KEY = savedKey;
|
|
153
|
+
} finally {
|
|
154
|
+
rmSync(tmp, { recursive: true, force: true });
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it('auto-detects vercel destination from vercel.json', async () => {
|
|
159
|
+
const tmp = makeTempDir('sec-collect');
|
|
160
|
+
try {
|
|
161
|
+
writeFileSync(join(tmp, 'vercel.json'), '{}');
|
|
162
|
+
const { detectDestination } = await import('./env-writer.js');
|
|
163
|
+
assert.equal(detectDestination(tmp), 'vercel');
|
|
164
|
+
} finally {
|
|
165
|
+
rmSync(tmp, { recursive: true, force: true });
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it('handles empty form values as skipped', async () => {
|
|
170
|
+
// Simulate what happens when user leaves a field empty in the form
|
|
171
|
+
const formContent: Record<string, string> = {
|
|
172
|
+
'API_KEY': 'provided-value',
|
|
173
|
+
'OPTIONAL_KEY': '', // empty = skip
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
const provided: Array<{ key: string; value: string }> = [];
|
|
177
|
+
const skipped: string[] = [];
|
|
178
|
+
|
|
179
|
+
for (const [key, raw] of Object.entries(formContent)) {
|
|
180
|
+
const value = typeof raw === 'string' ? raw.trim() : '';
|
|
181
|
+
if (value.length > 0) {
|
|
182
|
+
provided.push({ key, value });
|
|
183
|
+
} else {
|
|
184
|
+
skipped.push(key);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
assert.deepStrictEqual(provided, [{ key: 'API_KEY', value: 'provided-value' }]);
|
|
189
|
+
assert.deepStrictEqual(skipped, ['OPTIONAL_KEY']);
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
it('result text never contains secret values', async () => {
|
|
193
|
+
const tmp = makeTempDir('sec-collect');
|
|
194
|
+
try {
|
|
195
|
+
const envPath = join(tmp, '.env');
|
|
196
|
+
const savedKey = process.env.RESULT_TEXT_TEST;
|
|
197
|
+
|
|
198
|
+
const { applySecrets } = await import('./env-writer.js');
|
|
199
|
+
const { applied } = await applySecrets(
|
|
200
|
+
[{ key: 'RESULT_TEXT_TEST', value: 'sk-super-secret-abc123' }],
|
|
201
|
+
'dotenv',
|
|
202
|
+
{ envFilePath: envPath },
|
|
203
|
+
);
|
|
204
|
+
|
|
205
|
+
// Simulate building result text (same logic as the tool handler)
|
|
206
|
+
const lines: string[] = [
|
|
207
|
+
'destination: dotenv (auto-detected)',
|
|
208
|
+
...applied.map((k) => `✓ ${k}: applied`),
|
|
209
|
+
];
|
|
210
|
+
const resultText = lines.join('\n');
|
|
211
|
+
|
|
212
|
+
// The result MUST NOT contain the secret value
|
|
213
|
+
assert.ok(!resultText.includes('sk-super-secret-abc123'), 'result text must not contain secret value');
|
|
214
|
+
assert.ok(resultText.includes('RESULT_TEXT_TEST'), 'result text should contain key name');
|
|
215
|
+
|
|
216
|
+
// Cleanup
|
|
217
|
+
if (savedKey === undefined) delete process.env.RESULT_TEXT_TEST;
|
|
218
|
+
else process.env.RESULT_TEXT_TEST = savedKey;
|
|
219
|
+
} finally {
|
|
220
|
+
rmSync(tmp, { recursive: true, force: true });
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
it('handles multiple keys with mixed existing/new/skipped', async () => {
|
|
225
|
+
const tmp = makeTempDir('sec-collect');
|
|
226
|
+
try {
|
|
227
|
+
const envPath = join(tmp, '.env');
|
|
228
|
+
writeFileSync(envPath, 'EXISTING_A=already-here\n');
|
|
229
|
+
const savedB = process.env.NEW_B;
|
|
230
|
+
const savedC = process.env.SKIP_C;
|
|
231
|
+
|
|
232
|
+
const { checkExistingEnvKeys, applySecrets } = await import('./env-writer.js');
|
|
233
|
+
|
|
234
|
+
const allKeys = ['EXISTING_A', 'NEW_B', 'SKIP_C'];
|
|
235
|
+
const existing = await checkExistingEnvKeys(allKeys, envPath);
|
|
236
|
+
assert.deepStrictEqual(existing, ['EXISTING_A']);
|
|
237
|
+
|
|
238
|
+
// Simulate form response: NEW_B has value, SKIP_C is empty
|
|
239
|
+
const formContent = { NEW_B: 'new-value', SKIP_C: '' };
|
|
240
|
+
const provided: Array<{ key: string; value: string }> = [];
|
|
241
|
+
const skipped: string[] = [];
|
|
242
|
+
|
|
243
|
+
for (const key of allKeys.filter((k) => !existing.includes(k))) {
|
|
244
|
+
const raw = formContent[key as keyof typeof formContent] ?? '';
|
|
245
|
+
if (raw.trim().length > 0) provided.push({ key, value: raw.trim() });
|
|
246
|
+
else skipped.push(key);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const { applied, errors } = await applySecrets(provided, 'dotenv', { envFilePath: envPath });
|
|
250
|
+
|
|
251
|
+
assert.deepStrictEqual(applied, ['NEW_B']);
|
|
252
|
+
assert.deepStrictEqual(skipped, ['SKIP_C']);
|
|
253
|
+
assert.deepStrictEqual(errors, []);
|
|
254
|
+
assert.deepStrictEqual(existing, ['EXISTING_A']);
|
|
255
|
+
|
|
256
|
+
// Cleanup
|
|
257
|
+
if (savedB === undefined) delete process.env.NEW_B;
|
|
258
|
+
else process.env.NEW_B = savedB;
|
|
259
|
+
if (savedC === undefined) delete process.env.SKIP_C;
|
|
260
|
+
else process.env.SKIP_C = savedC;
|
|
261
|
+
} finally {
|
|
262
|
+
rmSync(tmp, { recursive: true, force: true });
|
|
263
|
+
}
|
|
264
|
+
});
|
|
265
|
+
});
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* MCP Server — registers GSD orchestration, project-state, and workflow tools.
|
|
3
3
|
*
|
|
4
4
|
* Session tools (6): gsd_execute, gsd_status, gsd_result, gsd_cancel, gsd_query, gsd_resolve_blocker
|
|
5
|
-
* Interactive tools (
|
|
5
|
+
* Interactive tools (2): ask_user_questions, secure_env_collect via MCP form elicitation
|
|
6
6
|
* Read-only tools (6): gsd_progress, gsd_roadmap, gsd_history, gsd_doctor, gsd_captures, gsd_knowledge
|
|
7
7
|
* Workflow tools (29): headless-safe planning, metadata persistence, replanning, completion, validation, reassessment, gate result, status, and journal tools
|
|
8
8
|
*
|
|
@@ -22,6 +22,7 @@ import { readCaptures } from './readers/captures.js';
|
|
|
22
22
|
import { readKnowledge } from './readers/knowledge.js';
|
|
23
23
|
import { runDoctorLite } from './readers/doctor-lite.js';
|
|
24
24
|
import { registerWorkflowTools } from './workflow-tools.js';
|
|
25
|
+
import { applySecrets, checkExistingEnvKeys, detectDestination } from './env-writer.js';
|
|
25
26
|
|
|
26
27
|
// ---------------------------------------------------------------------------
|
|
27
28
|
// Constants
|
|
@@ -54,46 +55,80 @@ function textContent(text: string): { content: Array<{ type: 'text'; text: strin
|
|
|
54
55
|
// gsd_query filesystem reader
|
|
55
56
|
// ---------------------------------------------------------------------------
|
|
56
57
|
|
|
57
|
-
|
|
58
|
+
/**
|
|
59
|
+
* Normalized query categories for {@link readProjectState}.
|
|
60
|
+
*
|
|
61
|
+
* Maps user-supplied query strings (or empty) to the set of fields we return.
|
|
62
|
+
* Accepts common synonyms so the MCP client can pass intuitive values.
|
|
63
|
+
*/
|
|
64
|
+
const QUERY_FIELDS = {
|
|
65
|
+
all: ['state', 'project', 'requirements', 'milestones'] as const,
|
|
66
|
+
state: ['state'] as const,
|
|
67
|
+
status: ['state'] as const,
|
|
68
|
+
project: ['project'] as const,
|
|
69
|
+
requirements: ['requirements'] as const,
|
|
70
|
+
milestones: ['milestones'] as const,
|
|
71
|
+
} as const;
|
|
72
|
+
|
|
73
|
+
type QueryCategory = keyof typeof QUERY_FIELDS;
|
|
74
|
+
type ProjectStateField = (typeof QUERY_FIELDS)[QueryCategory][number];
|
|
75
|
+
|
|
76
|
+
function normalizeQuery(query: string | undefined): QueryCategory {
|
|
77
|
+
const key = (query ?? 'all').trim().toLowerCase();
|
|
78
|
+
if (key in QUERY_FIELDS) return key as QueryCategory;
|
|
79
|
+
return 'all';
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async function readProjectState(projectDir: string, query: string | undefined): Promise<Record<string, unknown>> {
|
|
58
83
|
const gsdDir = join(resolve(projectDir), '.gsd');
|
|
59
|
-
const
|
|
84
|
+
const category = normalizeQuery(query);
|
|
85
|
+
const wanted = new Set<ProjectStateField>(QUERY_FIELDS[category]);
|
|
60
86
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
}
|
|
65
|
-
|
|
87
|
+
const result: Record<string, unknown> = {
|
|
88
|
+
projectDir: resolve(projectDir),
|
|
89
|
+
query: category,
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
if (wanted.has('state')) {
|
|
93
|
+
try {
|
|
94
|
+
result.state = await readFile(join(gsdDir, 'STATE.md'), 'utf-8');
|
|
95
|
+
} catch {
|
|
96
|
+
result.state = null;
|
|
97
|
+
}
|
|
66
98
|
}
|
|
67
99
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
100
|
+
if (wanted.has('project')) {
|
|
101
|
+
try {
|
|
102
|
+
result.project = await readFile(join(gsdDir, 'PROJECT.md'), 'utf-8');
|
|
103
|
+
} catch {
|
|
104
|
+
result.project = null;
|
|
105
|
+
}
|
|
73
106
|
}
|
|
74
107
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
108
|
+
if (wanted.has('requirements')) {
|
|
109
|
+
try {
|
|
110
|
+
result.requirements = await readFile(join(gsdDir, 'REQUIREMENTS.md'), 'utf-8');
|
|
111
|
+
} catch {
|
|
112
|
+
result.requirements = null;
|
|
113
|
+
}
|
|
80
114
|
}
|
|
81
115
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
116
|
+
if (wanted.has('milestones')) {
|
|
117
|
+
const milestonesDir = join(gsdDir, 'milestones');
|
|
118
|
+
try {
|
|
119
|
+
const entries = await readdir(milestonesDir, { withFileTypes: true });
|
|
120
|
+
const milestones: Array<{ id: string; hasRoadmap: boolean; hasSummary: boolean }> = [];
|
|
121
|
+
for (const entry of entries) {
|
|
122
|
+
if (!entry.isDirectory()) continue;
|
|
123
|
+
const mDir = join(milestonesDir, entry.name);
|
|
124
|
+
const hasRoadmap = await fileExists(join(mDir, `${entry.name}-ROADMAP.md`));
|
|
125
|
+
const hasSummary = await fileExists(join(mDir, `${entry.name}-SUMMARY.md`));
|
|
126
|
+
milestones.push({ id: entry.name, hasRoadmap, hasSummary });
|
|
127
|
+
}
|
|
128
|
+
result.milestones = milestones;
|
|
129
|
+
} catch {
|
|
130
|
+
result.milestones = [];
|
|
93
131
|
}
|
|
94
|
-
result.milestones = milestones;
|
|
95
|
-
} catch {
|
|
96
|
-
result.milestones = [];
|
|
97
132
|
}
|
|
98
133
|
|
|
99
134
|
return result;
|
|
@@ -112,11 +147,43 @@ async function fileExists(path: string): Promise<boolean> {
|
|
|
112
147
|
// MCP Server type — minimal interface for the dynamically-imported McpServer
|
|
113
148
|
// ---------------------------------------------------------------------------
|
|
114
149
|
|
|
150
|
+
interface ElicitResult {
|
|
151
|
+
action: 'accept' | 'decline' | 'cancel';
|
|
152
|
+
content?: Record<string, string | number | boolean | string[]>;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
interface ElicitRequestFormParams {
|
|
156
|
+
mode?: 'form';
|
|
157
|
+
message: string;
|
|
158
|
+
requestedSchema: {
|
|
159
|
+
type: 'object';
|
|
160
|
+
properties: Record<string, Record<string, unknown>>;
|
|
161
|
+
required?: string[];
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Handler extra — the second argument passed by McpServer.tool handlers.
|
|
167
|
+
* Contains an AbortSignal scoped to the JSON-RPC request (cancelled when
|
|
168
|
+
* the client cancels the `tools/call`) plus other per-request metadata.
|
|
169
|
+
* Tools that can actually be stopped mid-flight should honour `signal`.
|
|
170
|
+
*/
|
|
171
|
+
export interface McpToolExtra {
|
|
172
|
+
signal?: AbortSignal;
|
|
173
|
+
requestId?: string | number;
|
|
174
|
+
sendNotification?: (notification: unknown) => void | Promise<void>;
|
|
175
|
+
}
|
|
176
|
+
|
|
115
177
|
interface McpServerInstance {
|
|
116
|
-
tool(
|
|
178
|
+
tool(
|
|
179
|
+
name: string,
|
|
180
|
+
description: string,
|
|
181
|
+
params: Record<string, unknown>,
|
|
182
|
+
handler: (args: Record<string, unknown>, extra?: McpToolExtra) => Promise<unknown>,
|
|
183
|
+
): unknown;
|
|
117
184
|
server: {
|
|
118
185
|
elicitInput(
|
|
119
|
-
params: AskUserQuestionsElicitRequest,
|
|
186
|
+
params: AskUserQuestionsElicitRequest | ElicitRequestFormParams,
|
|
120
187
|
options?: unknown,
|
|
121
188
|
): Promise<AskUserQuestionsElicitResult>;
|
|
122
189
|
};
|
|
@@ -282,11 +349,16 @@ export async function createMcpServer(sessionManager: SessionManager): Promise<{
|
|
|
282
349
|
|
|
283
350
|
const server: McpServerInstance = new McpServer(
|
|
284
351
|
{ name: SERVER_NAME, version: SERVER_VERSION },
|
|
285
|
-
{ capabilities: { tools: {} } },
|
|
352
|
+
{ capabilities: { tools: {}, elicitation: {} } },
|
|
286
353
|
);
|
|
287
354
|
|
|
288
355
|
// -----------------------------------------------------------------------
|
|
289
|
-
// gsd_execute — start a new GSD auto-mode session
|
|
356
|
+
// gsd_execute — start a new GSD auto-mode session.
|
|
357
|
+
//
|
|
358
|
+
// If the JSON-RPC request is aborted while the session is starting (or
|
|
359
|
+
// immediately after), we cancel the session so we don't leak a background
|
|
360
|
+
// RpcClient process. Once the session is running the caller should use
|
|
361
|
+
// `gsd_cancel` to stop it via sessionId.
|
|
290
362
|
// -----------------------------------------------------------------------
|
|
291
363
|
server.tool(
|
|
292
364
|
'gsd_execute',
|
|
@@ -297,12 +369,20 @@ export async function createMcpServer(sessionManager: SessionManager): Promise<{
|
|
|
297
369
|
model: z.string().optional().describe('Model ID override'),
|
|
298
370
|
bare: z.boolean().optional().describe('Run in bare mode (skip user config)'),
|
|
299
371
|
},
|
|
300
|
-
async (args: Record<string, unknown
|
|
372
|
+
async (args: Record<string, unknown>, extra?: McpToolExtra) => {
|
|
301
373
|
const { projectDir, command, model, bare } = args as {
|
|
302
374
|
projectDir: string; command?: string; model?: string; bare?: boolean;
|
|
303
375
|
};
|
|
304
376
|
try {
|
|
305
377
|
const sessionId = await sessionManager.startSession(projectDir, { command, model, bare });
|
|
378
|
+
|
|
379
|
+
// If the client aborted while startSession was running, cancel the
|
|
380
|
+
// newly-created session rather than leaving an orphaned process.
|
|
381
|
+
if (extra?.signal?.aborted) {
|
|
382
|
+
await sessionManager.cancelSession(sessionId).catch(() => { /* swallow */ });
|
|
383
|
+
return errorContent('gsd_execute aborted by client before returning');
|
|
384
|
+
}
|
|
385
|
+
|
|
306
386
|
return jsonContent({ sessionId, status: 'started' });
|
|
307
387
|
} catch (err) {
|
|
308
388
|
return errorContent(err instanceof Error ? err.message : String(err));
|
|
@@ -395,17 +475,25 @@ export async function createMcpServer(sessionManager: SessionManager): Promise<{
|
|
|
395
475
|
);
|
|
396
476
|
|
|
397
477
|
// -----------------------------------------------------------------------
|
|
398
|
-
// gsd_query — read project state from filesystem (no session needed)
|
|
478
|
+
// gsd_query — read project state from filesystem (no session needed).
|
|
479
|
+
//
|
|
480
|
+
// `query` is optional: when omitted the tool returns all fields (STATE.md,
|
|
481
|
+
// PROJECT.md, requirements, milestone listing). Accepted narrow values:
|
|
482
|
+
// "state" / "status", "project", "requirements", "milestones", "all".
|
|
483
|
+
// Unknown values fall back to "all" for forward-compatibility.
|
|
399
484
|
// -----------------------------------------------------------------------
|
|
400
485
|
server.tool(
|
|
401
486
|
'gsd_query',
|
|
402
|
-
'Query GSD project state from the filesystem.
|
|
487
|
+
'Query GSD project state from the filesystem. By default returns STATE.md, PROJECT.md, requirements, and milestone listing. Pass `query` to narrow the response (accepted: "state"/"status", "project", "requirements", "milestones", "all"). Does not require an active session.',
|
|
403
488
|
{
|
|
404
489
|
projectDir: z.string().describe('Absolute path to the project directory'),
|
|
405
|
-
query: z
|
|
490
|
+
query: z
|
|
491
|
+
.enum(['all', 'state', 'status', 'project', 'requirements', 'milestones'])
|
|
492
|
+
.optional()
|
|
493
|
+
.describe('Narrow the response to a single field (default: "all")'),
|
|
406
494
|
},
|
|
407
495
|
async (args: Record<string, unknown>) => {
|
|
408
|
-
const { projectDir, query } = args as { projectDir: string; query
|
|
496
|
+
const { projectDir, query } = args as { projectDir: string; query?: string };
|
|
409
497
|
try {
|
|
410
498
|
const state = await readProjectState(projectDir, query);
|
|
411
499
|
return jsonContent(state);
|
|
@@ -472,6 +560,124 @@ export async function createMcpServer(sessionManager: SessionManager): Promise<{
|
|
|
472
560
|
},
|
|
473
561
|
);
|
|
474
562
|
|
|
563
|
+
// -----------------------------------------------------------------------
|
|
564
|
+
// secure_env_collect — collect secrets via MCP form elicitation
|
|
565
|
+
// -----------------------------------------------------------------------
|
|
566
|
+
server.tool(
|
|
567
|
+
'secure_env_collect',
|
|
568
|
+
'Collect environment variables securely via form input. Values are written directly to .env (or Vercel/Convex) and NEVER appear in tool output — only key names and applied/skipped status are returned. Use this instead of asking users to manually edit .env files or paste secrets into chat.',
|
|
569
|
+
{
|
|
570
|
+
projectDir: z.string().describe('Absolute path to the project directory'),
|
|
571
|
+
keys: z.array(z.object({
|
|
572
|
+
key: z.string().describe('Env var name, e.g. OPENAI_API_KEY'),
|
|
573
|
+
hint: z.string().optional().describe('Format hint shown to user, e.g. "starts with sk-"'),
|
|
574
|
+
guidance: z.array(z.string()).optional().describe('Step-by-step instructions for obtaining this key'),
|
|
575
|
+
})).min(1).describe('Environment variables to collect'),
|
|
576
|
+
destination: z.enum(['dotenv', 'vercel', 'convex']).optional().describe('Where to write secrets. Auto-detected from project files if omitted.'),
|
|
577
|
+
envFilePath: z.string().optional().describe('Path to .env file (dotenv only). Defaults to .env in projectDir.'),
|
|
578
|
+
environment: z.enum(['development', 'preview', 'production']).optional().describe('Target environment (vercel/convex only)'),
|
|
579
|
+
},
|
|
580
|
+
async (args: Record<string, unknown>) => {
|
|
581
|
+
const { projectDir, keys, destination, envFilePath, environment } = args as {
|
|
582
|
+
projectDir: string;
|
|
583
|
+
keys: Array<{ key: string; hint?: string; guidance?: string[] }>;
|
|
584
|
+
destination?: 'dotenv' | 'vercel' | 'convex';
|
|
585
|
+
envFilePath?: string;
|
|
586
|
+
environment?: 'development' | 'preview' | 'production';
|
|
587
|
+
};
|
|
588
|
+
|
|
589
|
+
try {
|
|
590
|
+
const resolvedProjectDir = resolve(projectDir);
|
|
591
|
+
const resolvedEnvPath = resolve(resolvedProjectDir, envFilePath ?? '.env');
|
|
592
|
+
|
|
593
|
+
// (1) Check which keys already exist
|
|
594
|
+
const allKeyNames = keys.map((k) => k.key);
|
|
595
|
+
const existingKeys = await checkExistingEnvKeys(allKeyNames, resolvedEnvPath);
|
|
596
|
+
const existingSet = new Set(existingKeys);
|
|
597
|
+
const pendingKeys = keys.filter((k) => !existingSet.has(k.key));
|
|
598
|
+
|
|
599
|
+
// If all keys already exist, return immediately
|
|
600
|
+
if (pendingKeys.length === 0) {
|
|
601
|
+
const lines = existingKeys.map((k) => `• ${k}: already set`);
|
|
602
|
+
return textContent(`All ${existingKeys.length} key(s) already set.\n${lines.join('\n')}`);
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
// (2) Build elicitation form — one string field per pending key
|
|
606
|
+
const properties: Record<string, Record<string, unknown>> = {};
|
|
607
|
+
const required: string[] = [];
|
|
608
|
+
|
|
609
|
+
for (const item of pendingKeys) {
|
|
610
|
+
const descParts: string[] = [];
|
|
611
|
+
if (item.hint) descParts.push(`Format: ${item.hint}`);
|
|
612
|
+
if (item.guidance && item.guidance.length > 0) {
|
|
613
|
+
descParts.push('How to get this:');
|
|
614
|
+
item.guidance.forEach((step, i) => descParts.push(`${i + 1}. ${step}`));
|
|
615
|
+
}
|
|
616
|
+
descParts.push('Leave empty to skip.');
|
|
617
|
+
|
|
618
|
+
properties[item.key] = {
|
|
619
|
+
type: 'string',
|
|
620
|
+
title: item.key,
|
|
621
|
+
description: descParts.join('\n'),
|
|
622
|
+
};
|
|
623
|
+
// Don't mark as required — empty string = skip
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
// (3) Elicit input from the MCP client
|
|
627
|
+
const elicitation = await server.server.elicitInput({
|
|
628
|
+
message: `Enter values for ${pendingKeys.length} environment variable(s). Values are written directly to the project and never shown to the AI.`,
|
|
629
|
+
requestedSchema: {
|
|
630
|
+
type: 'object',
|
|
631
|
+
properties,
|
|
632
|
+
required,
|
|
633
|
+
},
|
|
634
|
+
});
|
|
635
|
+
|
|
636
|
+
if (elicitation.action !== 'accept' || !elicitation.content) {
|
|
637
|
+
return textContent('secure_env_collect was cancelled by user.');
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
// (4) Separate provided vs skipped from form response
|
|
641
|
+
const provided: Array<{ key: string; value: string }> = [];
|
|
642
|
+
const skipped: string[] = [];
|
|
643
|
+
|
|
644
|
+
for (const item of pendingKeys) {
|
|
645
|
+
const raw = elicitation.content[item.key];
|
|
646
|
+
const value = typeof raw === 'string' ? raw.trim() : '';
|
|
647
|
+
if (value.length > 0) {
|
|
648
|
+
provided.push({ key: item.key, value });
|
|
649
|
+
} else {
|
|
650
|
+
skipped.push(item.key);
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
// (5) Auto-detect destination if not specified
|
|
655
|
+
const resolvedDestination = destination ?? detectDestination(resolvedProjectDir);
|
|
656
|
+
|
|
657
|
+
// (6) Write secrets to destination
|
|
658
|
+
const { applied, errors } = await applySecrets(provided, resolvedDestination, {
|
|
659
|
+
envFilePath: resolvedEnvPath,
|
|
660
|
+
environment,
|
|
661
|
+
});
|
|
662
|
+
|
|
663
|
+
// (7) Build result — NEVER include secret values
|
|
664
|
+
const lines: string[] = [
|
|
665
|
+
`destination: ${resolvedDestination}${!destination ? ' (auto-detected)' : ''}${environment ? ` (${environment})` : ''}`,
|
|
666
|
+
];
|
|
667
|
+
for (const k of applied) lines.push(`✓ ${k}: applied`);
|
|
668
|
+
for (const k of skipped) lines.push(`• ${k}: skipped`);
|
|
669
|
+
for (const k of existingKeys) lines.push(`• ${k}: already set`);
|
|
670
|
+
for (const e of errors) lines.push(`✗ ${e}`);
|
|
671
|
+
|
|
672
|
+
return errors.length > 0 && applied.length === 0
|
|
673
|
+
? errorContent(lines.join('\n'))
|
|
674
|
+
: textContent(lines.join('\n'));
|
|
675
|
+
} catch (err) {
|
|
676
|
+
return errorContent(err instanceof Error ? err.message : String(err));
|
|
677
|
+
}
|
|
678
|
+
},
|
|
679
|
+
);
|
|
680
|
+
|
|
475
681
|
// =======================================================================
|
|
476
682
|
// READ-ONLY TOOLS — no session required, pure filesystem reads
|
|
477
683
|
// =======================================================================
|