gsd-pi 2.70.1 → 2.71.0-dev.977c553
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -17
- package/dist/resources/extensions/claude-code-cli/stream-adapter.js +129 -30
- package/dist/resources/extensions/get-secrets-from-user.js +17 -1
- package/dist/resources/extensions/gsd/auto-start.js +3 -11
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +4 -0
- package/dist/resources/extensions/gsd/custom-workflow-engine.js +16 -12
- package/dist/resources/extensions/gsd/file-lock.js +60 -0
- package/dist/resources/extensions/gsd/guided-flow.js +12 -10
- package/dist/resources/extensions/gsd/init-wizard.js +3 -11
- package/dist/resources/extensions/gsd/prompts/discuss.md +31 -13
- package/dist/resources/extensions/gsd/state.js +234 -332
- package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +34 -0
- package/dist/resources/extensions/gsd/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/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +13 -13
- 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 +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 +13 -13
- package/dist/web/standalone/.next/server/chunks/63.js +3 -3
- package/dist/web/standalone/.next/server/chunks/6897.js +1 -1
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware.js +2 -2
- package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
- package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +1 -1
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/dist/web/standalone/.next/static/chunks/2826.dd3dc8bbd3025fa5.js +9 -0
- package/dist/web/standalone/.next/static/chunks/app/_not-found/{page-2f24283c162b6ab3.js → page-f2a7482d42a5614b.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/app/{layout-9ecfd95f343793f0.js → layout-a16c7a7ecdf0c2cf.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/app/page-f1e30ab6bb269149.js +1 -0
- package/dist/web/standalone/.next/static/chunks/main-app-fdab67f7802d7832.js +1 -0
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +1 -0
- package/dist/web/standalone/.next/static/chunks/{webpack-6e4d7e9a4f57bed4.js → webpack-b868033a5834586d.js} +1 -1
- package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
- package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
- package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
- package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api.target.mk +14 -14
- package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_except.target.mk +14 -14
- package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_maybe.target.mk +14 -14
- package/dist/web/standalone/server.js +1 -1
- package/package.json +1 -1
- package/packages/mcp-server/dist/env-writer.d.ts +39 -0
- package/packages/mcp-server/dist/env-writer.d.ts.map +1 -0
- package/packages/mcp-server/dist/env-writer.js +158 -0
- package/packages/mcp-server/dist/env-writer.js.map +1 -0
- package/packages/mcp-server/dist/server.d.ts +11 -2
- package/packages/mcp-server/dist/server.d.ts.map +1 -1
- package/packages/mcp-server/dist/server.js +102 -2
- package/packages/mcp-server/dist/server.js.map +1 -1
- package/packages/mcp-server/src/env-writer.test.ts +280 -0
- package/packages/mcp-server/src/env-writer.ts +183 -0
- package/packages/mcp-server/src/secure-env-collect.test.ts +265 -0
- package/packages/mcp-server/src/server.ts +137 -3
- package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js +388 -0
- package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.d.ts +19 -2
- package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.js +50 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/extension-input.d.ts +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/extension-input.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/extension-input.js +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/extension-input.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +168 -23
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +6 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +58 -2
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-types.d.ts +1 -0
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-types.js.map +1 -1
- package/packages/pi-coding-agent/package.json +1 -1
- package/packages/pi-coding-agent/src/core/chat-controller-ordering.test.ts +468 -0
- package/packages/pi-coding-agent/src/core/extensions/types.ts +2 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/dynamic-border.ts +58 -2
- package/packages/pi-coding-agent/src/modes/interactive/components/extension-input.ts +2 -0
- package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +198 -29
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode-state.ts +1 -0
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +66 -2
- package/packages/pi-coding-agent/src/modes/rpc/rpc-mode.ts +1 -1
- package/packages/pi-coding-agent/src/modes/rpc/rpc-types.ts +1 -0
- package/packages/pi-tui/dist/components/__tests__/input.test.js +9 -0
- package/packages/pi-tui/dist/components/__tests__/input.test.js.map +1 -1
- package/packages/pi-tui/dist/components/__tests__/markdown-maxlines.test.d.ts +2 -0
- package/packages/pi-tui/dist/components/__tests__/markdown-maxlines.test.d.ts.map +1 -0
- package/packages/pi-tui/dist/components/__tests__/markdown-maxlines.test.js +66 -0
- package/packages/pi-tui/dist/components/__tests__/markdown-maxlines.test.js.map +1 -0
- package/packages/pi-tui/dist/components/input.d.ts +2 -0
- package/packages/pi-tui/dist/components/input.d.ts.map +1 -1
- package/packages/pi-tui/dist/components/input.js +7 -4
- package/packages/pi-tui/dist/components/input.js.map +1 -1
- package/packages/pi-tui/dist/components/markdown.d.ts +3 -0
- package/packages/pi-tui/dist/components/markdown.d.ts.map +1 -1
- package/packages/pi-tui/dist/components/markdown.js +17 -1
- package/packages/pi-tui/dist/components/markdown.js.map +1 -1
- package/packages/pi-tui/src/components/__tests__/input.test.ts +11 -0
- package/packages/pi-tui/src/components/__tests__/markdown-maxlines.test.ts +75 -0
- package/packages/pi-tui/src/components/input.ts +7 -4
- package/packages/pi-tui/src/components/markdown.ts +22 -1
- package/pkg/package.json +1 -1
- package/src/resources/extensions/claude-code-cli/stream-adapter.ts +166 -31
- package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +145 -0
- package/src/resources/extensions/get-secrets-from-user.ts +24 -1
- package/src/resources/extensions/gsd/auto-start.ts +3 -13
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +4 -0
- package/src/resources/extensions/gsd/custom-workflow-engine.ts +19 -14
- package/src/resources/extensions/gsd/file-lock.ts +59 -0
- package/src/resources/extensions/gsd/guided-flow.ts +12 -9
- package/src/resources/extensions/gsd/init-wizard.ts +3 -13
- package/src/resources/extensions/gsd/prompts/discuss.md +31 -13
- package/src/resources/extensions/gsd/state.ts +274 -344
- package/src/resources/extensions/gsd/tests/derive-state-helpers.test.ts +436 -0
- package/src/resources/extensions/gsd/tests/discuss-incremental-persistence.test.ts +9 -0
- package/src/resources/extensions/gsd/tests/file-lock.test.ts +103 -0
- package/src/resources/extensions/gsd/tests/secure-env-collect.test.ts +45 -0
- package/src/resources/extensions/gsd/tests/workflow-mcp-auto-prep.test.ts +76 -0
- package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +155 -1
- package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +22 -0
- package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +60 -25
- package/src/resources/extensions/gsd/workflow-events.ts +34 -25
- package/src/resources/extensions/gsd/workflow-mcp-auto-prep.ts +76 -0
- package/src/resources/extensions/gsd/workflow-mcp.ts +1 -1
- package/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 → 4xyaXTn7-shVHaGMcl75o}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{9pw9EXtXjdM7EFrCXUEPf → 4xyaXTn7-shVHaGMcl75o}/_ssgManifest.js +0 -0
package/README.md
CHANGED
|
@@ -27,36 +27,43 @@ One command. Walk away. Come back to a built project with clean git history.
|
|
|
27
27
|
|
|
28
28
|
---
|
|
29
29
|
|
|
30
|
-
## What's New in v2.
|
|
30
|
+
## What's New in v2.71
|
|
31
31
|
|
|
32
|
-
### MCP
|
|
32
|
+
### MCP Secure Env Collect
|
|
33
33
|
|
|
34
|
-
- **
|
|
35
|
-
- **
|
|
36
|
-
- **Write gate enforcement** — workflow MCP respects write gates, preventing unauthorized state mutations from external clients.
|
|
34
|
+
- **Secure credential collection over MCP** — the new `secure_env_collect` tool uses MCP form elicitation to collect secrets (API keys, tokens) from external clients without exposing values in tool output. Masks input in interactive mode.
|
|
35
|
+
- **Hardened elicitation schema** — MCP elicitation schema handling is stricter, with proper validation and fallback for providers that don't support forms.
|
|
37
36
|
|
|
38
|
-
### Reliability
|
|
37
|
+
### MCP Reliability
|
|
39
38
|
|
|
40
|
-
- **
|
|
41
|
-
- **
|
|
42
|
-
- **
|
|
43
|
-
- **Auto-resume hardening** — `autoStartTime` restored on resume, managed resources resynced on auto resume.
|
|
39
|
+
- **Stream ordering preserved** — MCP tool output now renders in the correct order, fixing interleaved output in Claude Code and other MCP clients.
|
|
40
|
+
- **isError flag propagation** — workflow tool execution failures now correctly return `isError: true`, so MCP clients can distinguish success from failure.
|
|
41
|
+
- **Multi-round discuss questions** — new-project discuss phase supports multi-round questioning with structured question gates.
|
|
44
42
|
|
|
45
|
-
### TUI
|
|
43
|
+
### TUI Fixes
|
|
46
44
|
|
|
47
|
-
- **
|
|
48
|
-
- **
|
|
45
|
+
- **Pinned output restored** — pinned output bar displays above the editor during tool execution again.
|
|
46
|
+
- **Turn completion cleanup** — pinned latest output is cleared on turn completion, preventing stale output from persisting.
|
|
47
|
+
- **Secure input masking** — extension input values are masked in interactive mode when collecting secrets.
|
|
49
48
|
|
|
50
|
-
###
|
|
49
|
+
### Reliability & Internals
|
|
51
50
|
|
|
52
|
-
- **
|
|
53
|
-
- **
|
|
51
|
+
- **TOCTOU file locking** — race conditions in event log and custom workflow graph file locking are fixed with proper atomic lock acquisition.
|
|
52
|
+
- **State derive refactor** — `deriveStateFromDb` god function extracted into composable, testable helpers.
|
|
53
|
+
- **Windows portability** — hardened cross-platform portability across runtime, tooling, and CI.
|
|
54
|
+
- **Model routing transparency** — dynamic routing is skipped for interactive dispatches; model changes are always shown in the banner.
|
|
55
|
+
- **Capability-aware routing (ADR-004)** — full implementation of capability scoring, `before_model_select` hook, and task metadata extraction.
|
|
56
|
+
- **Multi-model provider strategy (ADR-005)** — infrastructure for multi-provider model selection wired into live paths.
|
|
54
57
|
|
|
55
58
|
See the full [Changelog](./CHANGELOG.md) for details on every release.
|
|
56
59
|
|
|
57
60
|
<details>
|
|
58
|
-
<summary>Previous highlights (v2.
|
|
61
|
+
<summary>Previous highlights (v2.70 and earlier)</summary>
|
|
59
62
|
|
|
63
|
+
- **Full workflow over MCP (v2.68)** — slice replanning, milestone management, slice completion, task completion, and core planning tools exposed over MCP
|
|
64
|
+
- **Transport-gated MCP (v2.68)** — workflow tool availability adapts to provider transport capabilities automatically
|
|
65
|
+
- **Contextual tips system (v2.68)** — TUI and web terminal surface contextual tips based on workflow state
|
|
66
|
+
- **Ask user questions over MCP (v2.70)** — interactive questions exposed via elicitation for external integrations
|
|
60
67
|
- **Tiered Context Injection (M005)** — relevance-scoped context with 65%+ token reduction
|
|
61
68
|
- **Resilient transient error recovery** — defers to Core RetryHandler and fixes cmdCtx race conditions
|
|
62
69
|
- **Anthropic subscription routing** — auto-routed through Claude Code CLI provider with proper display names
|
|
@@ -12,6 +12,7 @@ import { PartialMessageBuilder, ZERO_USAGE, mapUsage } from "./partial-builder.j
|
|
|
12
12
|
import { buildWorkflowMcpServers } from "../gsd/workflow-mcp.js";
|
|
13
13
|
import { showInterviewRound } from "../shared/tui.js";
|
|
14
14
|
const OTHER_OPTION_LABEL = "None of the above";
|
|
15
|
+
const SENSITIVE_FIELD_PATTERN = /(password|passphrase|secret|token|api[_\s-]*key|private[_\s-]*key|credential)/i;
|
|
15
16
|
// ---------------------------------------------------------------------------
|
|
16
17
|
// Stream factory
|
|
17
18
|
// ---------------------------------------------------------------------------
|
|
@@ -182,6 +183,56 @@ export function parseAskUserQuestionsElicitation(request) {
|
|
|
182
183
|
}
|
|
183
184
|
return questions.length > 0 ? questions : null;
|
|
184
185
|
}
|
|
186
|
+
function isSecureElicitationField(requestMessage, fieldId, field) {
|
|
187
|
+
if (field.format === "password")
|
|
188
|
+
return true;
|
|
189
|
+
if (field.writeOnly === true)
|
|
190
|
+
return true;
|
|
191
|
+
const rawField = field;
|
|
192
|
+
if (rawField.sensitive === true || rawField["x-sensitive"] === true)
|
|
193
|
+
return true;
|
|
194
|
+
const haystack = [
|
|
195
|
+
requestMessage,
|
|
196
|
+
fieldId.replace(/[_-]+/g, " "),
|
|
197
|
+
typeof field.title === "string" ? field.title : "",
|
|
198
|
+
typeof field.description === "string" ? field.description : "",
|
|
199
|
+
]
|
|
200
|
+
.join(" ")
|
|
201
|
+
.toLowerCase();
|
|
202
|
+
return SENSITIVE_FIELD_PATTERN.test(haystack);
|
|
203
|
+
}
|
|
204
|
+
export function parseTextInputElicitation(request) {
|
|
205
|
+
if (request.mode && request.mode !== "form")
|
|
206
|
+
return null;
|
|
207
|
+
const schema = request.requestedSchema;
|
|
208
|
+
const fieldsSource = schema?.properties && typeof schema.properties === "object"
|
|
209
|
+
? schema.properties
|
|
210
|
+
: schema?.keys && typeof schema.keys === "object"
|
|
211
|
+
? schema.keys
|
|
212
|
+
: undefined;
|
|
213
|
+
if (!fieldsSource)
|
|
214
|
+
return null;
|
|
215
|
+
const requiredSet = new Set(Array.isArray(request.requestedSchema?.required)
|
|
216
|
+
? request.requestedSchema.required.filter((value) => typeof value === "string")
|
|
217
|
+
: []);
|
|
218
|
+
const fields = [];
|
|
219
|
+
for (const [fieldId, field] of Object.entries(fieldsSource)) {
|
|
220
|
+
if (!field || typeof field !== "object")
|
|
221
|
+
continue;
|
|
222
|
+
if (field.type !== "string")
|
|
223
|
+
continue;
|
|
224
|
+
if (Array.isArray(field.oneOf) && field.oneOf.length > 0)
|
|
225
|
+
continue;
|
|
226
|
+
fields.push({
|
|
227
|
+
id: fieldId,
|
|
228
|
+
title: typeof field.title === "string" && field.title.length > 0 ? field.title : fieldId,
|
|
229
|
+
description: typeof field.description === "string" ? field.description : "",
|
|
230
|
+
required: requiredSet.has(fieldId),
|
|
231
|
+
secure: isSecureElicitationField(request.message, fieldId, field),
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
return fields.length > 0 ? fields : null;
|
|
235
|
+
}
|
|
185
236
|
export function roundResultToElicitationContent(questions, result) {
|
|
186
237
|
const content = {};
|
|
187
238
|
for (const question of questions) {
|
|
@@ -246,6 +297,38 @@ async function promptElicitationWithDialogs(request, questions, ui, signal) {
|
|
|
246
297
|
}
|
|
247
298
|
return { action: "accept", content };
|
|
248
299
|
}
|
|
300
|
+
function buildTextInputPromptTitle(request, field) {
|
|
301
|
+
const parts = [
|
|
302
|
+
request.serverName ? `[${request.serverName}]` : "",
|
|
303
|
+
field.title,
|
|
304
|
+
field.description,
|
|
305
|
+
].filter((part) => typeof part === "string" && part.trim().length > 0);
|
|
306
|
+
return parts.join("\n\n");
|
|
307
|
+
}
|
|
308
|
+
function buildTextInputPlaceholder(field) {
|
|
309
|
+
const desc = field.description.trim();
|
|
310
|
+
if (!desc)
|
|
311
|
+
return field.required ? "Required" : "Leave empty to skip";
|
|
312
|
+
const formatLine = desc
|
|
313
|
+
.split(/\r?\n/)
|
|
314
|
+
.map((line) => line.trim())
|
|
315
|
+
.find((line) => /^format:/i.test(line));
|
|
316
|
+
if (!formatLine)
|
|
317
|
+
return field.required ? "Required" : "Leave empty to skip";
|
|
318
|
+
const hint = formatLine.replace(/^format:\s*/i, "").trim();
|
|
319
|
+
return hint.length > 0 ? hint : field.required ? "Required" : "Leave empty to skip";
|
|
320
|
+
}
|
|
321
|
+
async function promptTextInputElicitation(request, fields, ui, signal) {
|
|
322
|
+
const content = {};
|
|
323
|
+
for (const field of fields) {
|
|
324
|
+
const value = await ui.input(buildTextInputPromptTitle(request, field), buildTextInputPlaceholder(field), { signal, ...(field.secure ? { secure: true } : {}) });
|
|
325
|
+
if (value === undefined) {
|
|
326
|
+
return { action: "cancel" };
|
|
327
|
+
}
|
|
328
|
+
content[field.id] = value;
|
|
329
|
+
}
|
|
330
|
+
return { action: "accept", content };
|
|
331
|
+
}
|
|
249
332
|
export function createClaudeCodeElicitationHandler(ui) {
|
|
250
333
|
if (!ui)
|
|
251
334
|
return undefined;
|
|
@@ -254,17 +337,21 @@ export function createClaudeCodeElicitationHandler(ui) {
|
|
|
254
337
|
return { action: "decline" };
|
|
255
338
|
}
|
|
256
339
|
const questions = parseAskUserQuestionsElicitation(request);
|
|
257
|
-
if (
|
|
258
|
-
|
|
340
|
+
if (questions) {
|
|
341
|
+
const interviewResult = await showInterviewRound(questions, { signal }, { ui }).catch(() => undefined);
|
|
342
|
+
if (interviewResult && Object.keys(interviewResult.answers).length > 0) {
|
|
343
|
+
return {
|
|
344
|
+
action: "accept",
|
|
345
|
+
content: roundResultToElicitationContent(questions, interviewResult),
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
return promptElicitationWithDialogs(request, questions, ui, signal);
|
|
259
349
|
}
|
|
260
|
-
const
|
|
261
|
-
if (
|
|
262
|
-
return
|
|
263
|
-
action: "accept",
|
|
264
|
-
content: roundResultToElicitationContent(questions, interviewResult),
|
|
265
|
-
};
|
|
350
|
+
const textFields = parseTextInputElicitation(request);
|
|
351
|
+
if (textFields) {
|
|
352
|
+
return promptTextInputElicitation(request, textFields, ui, signal);
|
|
266
353
|
}
|
|
267
|
-
return
|
|
354
|
+
return { action: "decline" };
|
|
268
355
|
};
|
|
269
356
|
}
|
|
270
357
|
// ---------------------------------------------------------------------------
|
|
@@ -278,6 +365,7 @@ export function createClaudeCodeElicitationHandler(ui) {
|
|
|
278
365
|
*/
|
|
279
366
|
export function buildSdkOptions(modelId, prompt, extraOptions = {}) {
|
|
280
367
|
const mcpServers = buildWorkflowMcpServers();
|
|
368
|
+
const disallowedTools = ["AskUserQuestion"];
|
|
281
369
|
return {
|
|
282
370
|
pathToClaudeCodeExecutable: getClaudePath(),
|
|
283
371
|
model: modelId,
|
|
@@ -288,6 +376,7 @@ export function buildSdkOptions(modelId, prompt, extraOptions = {}) {
|
|
|
288
376
|
allowDangerouslySkipPermissions: true,
|
|
289
377
|
settingSources: ["project"],
|
|
290
378
|
systemPrompt: { type: "preset", preset: "claude_code" },
|
|
379
|
+
disallowedTools,
|
|
291
380
|
...(mcpServers ? { mcpServers } : {}),
|
|
292
381
|
betas: modelId.includes("sonnet") ? ["context-1m-2025-08-07"] : [],
|
|
293
382
|
...extraOptions,
|
|
@@ -371,9 +460,9 @@ export function extractToolResultsFromSdkUserMessage(message) {
|
|
|
371
460
|
}
|
|
372
461
|
return extracted;
|
|
373
462
|
}
|
|
374
|
-
function
|
|
375
|
-
for (const block of
|
|
376
|
-
if (block.type !== "toolCall")
|
|
463
|
+
function attachExternalResultsToToolBlocks(toolBlocks, toolResultsById) {
|
|
464
|
+
for (const block of toolBlocks) {
|
|
465
|
+
if (block.type !== "toolCall" && block.type !== "serverToolUse")
|
|
377
466
|
continue;
|
|
378
467
|
const externalResult = toolResultsById.get(block.id);
|
|
379
468
|
if (!externalResult)
|
|
@@ -402,8 +491,8 @@ async function pumpSdkMessages(model, context, options, stream) {
|
|
|
402
491
|
/** Track the last text content seen across all assistant turns for the final message. */
|
|
403
492
|
let lastTextContent = "";
|
|
404
493
|
let lastThinkingContent = "";
|
|
405
|
-
/** Collect tool
|
|
406
|
-
const
|
|
494
|
+
/** Collect tool blocks from intermediate SDK turns for tool execution rendering. */
|
|
495
|
+
const intermediateToolBlocks = [];
|
|
407
496
|
/** Preserve real external tool results from Claude Code's synthetic user messages. */
|
|
408
497
|
const toolResultsById = new Map();
|
|
409
498
|
try {
|
|
@@ -491,9 +580,9 @@ async function pumpSdkMessages(model, context, options, stream) {
|
|
|
491
580
|
else if (block.type === "thinking" && block.thinking) {
|
|
492
581
|
lastThinkingContent = block.thinking;
|
|
493
582
|
}
|
|
494
|
-
else if (block.type === "toolCall") {
|
|
495
|
-
// Collect tool
|
|
496
|
-
|
|
583
|
+
else if (block.type === "toolCall" || block.type === "serverToolUse") {
|
|
584
|
+
// Collect tool blocks for externalToolExecution rendering
|
|
585
|
+
intermediateToolBlocks.push(block);
|
|
497
586
|
}
|
|
498
587
|
}
|
|
499
588
|
}
|
|
@@ -502,25 +591,35 @@ async function pumpSdkMessages(model, context, options, stream) {
|
|
|
502
591
|
for (const { toolUseId, result } of extractToolResultsFromSdkUserMessage(msg)) {
|
|
503
592
|
toolResultsById.set(toolUseId, result);
|
|
504
593
|
}
|
|
505
|
-
|
|
594
|
+
attachExternalResultsToToolBlocks(intermediateToolBlocks, toolResultsById);
|
|
506
595
|
// Push a synthetic toolcall_end for each tool call from this turn
|
|
507
596
|
// so the TUI can render tool results in real-time during the SDK
|
|
508
597
|
// session instead of waiting until the entire session completes.
|
|
509
598
|
if (builder) {
|
|
510
599
|
for (const block of builder.message.content) {
|
|
511
|
-
if (block.type !== "toolCall")
|
|
512
|
-
continue;
|
|
513
600
|
const extResult = block.externalResult;
|
|
514
601
|
if (!extResult)
|
|
515
602
|
continue;
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
603
|
+
const contentIndex = builder.message.content.indexOf(block);
|
|
604
|
+
if (contentIndex < 0)
|
|
605
|
+
continue;
|
|
606
|
+
// Push synthetic completion events with result attached so the
|
|
607
|
+
// chat-controller can update pending ToolExecutionComponents.
|
|
608
|
+
if (block.type === "toolCall") {
|
|
609
|
+
stream.push({
|
|
610
|
+
type: "toolcall_end",
|
|
611
|
+
contentIndex,
|
|
612
|
+
toolCall: block,
|
|
613
|
+
partial: builder.message,
|
|
614
|
+
});
|
|
615
|
+
}
|
|
616
|
+
else if (block.type === "serverToolUse") {
|
|
617
|
+
stream.push({
|
|
618
|
+
type: "server_tool_use",
|
|
619
|
+
contentIndex,
|
|
620
|
+
partial: builder.message,
|
|
621
|
+
});
|
|
622
|
+
}
|
|
524
623
|
}
|
|
525
624
|
}
|
|
526
625
|
builder = null;
|
|
@@ -534,8 +633,8 @@ async function pumpSdkMessages(model, context, options, stream) {
|
|
|
534
633
|
// events for proper TUI rendering, followed by the text response.
|
|
535
634
|
const finalContent = [];
|
|
536
635
|
// Add tool calls from intermediate turns first (renders above text)
|
|
537
|
-
|
|
538
|
-
finalContent.push(...
|
|
636
|
+
attachExternalResultsToToolBlocks(intermediateToolBlocks, toolResultsById);
|
|
637
|
+
finalContent.push(...intermediateToolBlocks);
|
|
539
638
|
// Add text/thinking from the last turn
|
|
540
639
|
if (builder && builder.message.content.length > 0) {
|
|
541
640
|
for (const block of builder.message.content) {
|
|
@@ -93,7 +93,7 @@ export function detectDestination(basePath) {
|
|
|
93
93
|
async function collectOneSecret(ctx, pageIndex, totalPages, keyName, hint, guidance) {
|
|
94
94
|
if (!ctx.hasUI)
|
|
95
95
|
return null;
|
|
96
|
-
|
|
96
|
+
const customResult = await ctx.ui.custom((tui, theme, _kb, done) => {
|
|
97
97
|
let value = "";
|
|
98
98
|
let cachedLines;
|
|
99
99
|
const editorTheme = {
|
|
@@ -178,6 +178,22 @@ async function collectOneSecret(ctx, pageIndex, totalPages, keyName, hint, guida
|
|
|
178
178
|
handleInput,
|
|
179
179
|
};
|
|
180
180
|
});
|
|
181
|
+
// RPC/web surfaces may not implement ctx.ui.custom(). Fall back to a
|
|
182
|
+
// standard input prompt so users can still provide the secret.
|
|
183
|
+
if (customResult !== undefined) {
|
|
184
|
+
return customResult;
|
|
185
|
+
}
|
|
186
|
+
if (typeof ctx.ui?.input !== "function") {
|
|
187
|
+
return null;
|
|
188
|
+
}
|
|
189
|
+
const inputTitle = `Secure value for ${keyName} (${pageIndex + 1}/${totalPages})`;
|
|
190
|
+
const inputPlaceholder = hint || "Enter secret value";
|
|
191
|
+
const inputResult = await ctx.ui.input(inputTitle, inputPlaceholder, { secure: true });
|
|
192
|
+
if (typeof inputResult !== "string") {
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
195
|
+
const trimmed = inputResult.trim();
|
|
196
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
181
197
|
}
|
|
182
198
|
/**
|
|
183
199
|
* Exported wrapper around collectOneSecret for testing.
|
|
@@ -248,17 +248,9 @@ export async function bootstrapAutoSession(s, ctx, pi, base, verboseMode, reques
|
|
|
248
248
|
logWarning("engine", `mkdir failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
249
249
|
}
|
|
250
250
|
}
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
const result = ensureProjectWorkflowMcpConfig(base);
|
|
255
|
-
if (result.status !== "unchanged") {
|
|
256
|
-
ctx.ui.notify(`Claude Code MCP prepared at ${result.configPath}`, "info");
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
catch (err) {
|
|
260
|
-
ctx.ui.notify(`Claude Code MCP prep failed: ${err instanceof Error ? err.message : String(err)}`, "warning");
|
|
261
|
-
}
|
|
251
|
+
{
|
|
252
|
+
const { prepareWorkflowMcpForProject } = await import("./workflow-mcp-auto-prep.js");
|
|
253
|
+
prepareWorkflowMcpForProject(ctx, base);
|
|
262
254
|
}
|
|
263
255
|
// Initialize GitServiceImpl
|
|
264
256
|
s.gitService = new GitServiceImpl(s.basePath, loadEffectiveGSDPreferences()?.preferences?.git ?? {});
|
|
@@ -39,6 +39,8 @@ export function registerHooks(pi) {
|
|
|
39
39
|
resetToolCallLoopGuard();
|
|
40
40
|
resetAskUserQuestionsCache();
|
|
41
41
|
await syncServiceTierStatus(ctx);
|
|
42
|
+
const { prepareWorkflowMcpForProject } = await import("../workflow-mcp-auto-prep.js");
|
|
43
|
+
prepareWorkflowMcpForProject(ctx, process.cwd());
|
|
42
44
|
// Apply show_token_cost preference (#1515)
|
|
43
45
|
try {
|
|
44
46
|
const { loadEffectiveGSDPreferences } = await import("../preferences.js");
|
|
@@ -78,6 +80,8 @@ export function registerHooks(pi) {
|
|
|
78
80
|
resetAskUserQuestionsCache();
|
|
79
81
|
clearDiscussionFlowState();
|
|
80
82
|
await syncServiceTierStatus(ctx);
|
|
83
|
+
const { prepareWorkflowMcpForProject } = await import("../workflow-mcp-auto-prep.js");
|
|
84
|
+
prepareWorkflowMcpForProject(ctx, process.cwd());
|
|
81
85
|
loadToolApiKeys();
|
|
82
86
|
});
|
|
83
87
|
pi.on("before_agent_start", async (event, ctx) => {
|
|
@@ -17,6 +17,7 @@ import { parse } from "yaml";
|
|
|
17
17
|
import { readGraph, writeGraph, getNextPendingStep, markStepComplete, expandIteration, } from "./graph.js";
|
|
18
18
|
import { injectContext } from "./context-injector.js";
|
|
19
19
|
import { parseUnitId } from "./unit-id.js";
|
|
20
|
+
import { withFileLock } from "./file-lock.js";
|
|
20
21
|
/** Read and parse the frozen DEFINITION.yaml from a run directory. */
|
|
21
22
|
export function readFrozenDefinition(runDir) {
|
|
22
23
|
const defPath = join(runDir, "DEFINITION.yaml");
|
|
@@ -135,18 +136,21 @@ export class CustomWorkflowEngine {
|
|
|
135
136
|
* Returns "milestone-complete" when all steps are now done, "continue" otherwise.
|
|
136
137
|
*/
|
|
137
138
|
async reconcile(state, completedStep) {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
139
|
+
const graphPath = join(this.runDir, "GRAPH.yaml");
|
|
140
|
+
return await withFileLock(graphPath, () => {
|
|
141
|
+
// Re-read the graph from disk so we do not overwrite concurrent
|
|
142
|
+
// workflow edits with a stale in-memory snapshot from deriveState().
|
|
143
|
+
const graph = readGraph(this.runDir);
|
|
144
|
+
// Extract stepId from "<workflowName>/<stepId>"
|
|
145
|
+
const { milestone, slice, task } = parseUnitId(completedStep.unitId);
|
|
146
|
+
const stepId = task ?? slice ?? milestone;
|
|
147
|
+
const updatedGraph = markStepComplete(graph, stepId);
|
|
148
|
+
writeGraph(this.runDir, updatedGraph);
|
|
149
|
+
const allDone = updatedGraph.steps.every((s) => s.status === "complete" || s.status === "expanded");
|
|
150
|
+
return {
|
|
151
|
+
outcome: allDone ? "milestone-complete" : "continue",
|
|
152
|
+
};
|
|
153
|
+
});
|
|
150
154
|
}
|
|
151
155
|
/**
|
|
152
156
|
* Return UI-facing metadata for progress display.
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
function _require(name) {
|
|
3
|
+
try {
|
|
4
|
+
return require(name);
|
|
5
|
+
}
|
|
6
|
+
catch {
|
|
7
|
+
try {
|
|
8
|
+
const gsdPiRequire = require("module").createRequire(require("path").join(process.cwd(), "node_modules", "gsd-pi", "index.js"));
|
|
9
|
+
return gsdPiRequire(name);
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
export function withFileLockSync(filePath, fn) {
|
|
17
|
+
const lockfile = _require("proper-lockfile");
|
|
18
|
+
if (!lockfile)
|
|
19
|
+
return fn();
|
|
20
|
+
if (!existsSync(filePath))
|
|
21
|
+
return fn();
|
|
22
|
+
try {
|
|
23
|
+
const release = lockfile.lockSync(filePath, { retries: 5, stale: 10000 });
|
|
24
|
+
try {
|
|
25
|
+
return fn();
|
|
26
|
+
}
|
|
27
|
+
finally {
|
|
28
|
+
release();
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
catch (err) {
|
|
32
|
+
if (err.code === "ELOCKED") {
|
|
33
|
+
// Could not get lock after retries, let's fallback to un-locked instead of crashing the whole state machine
|
|
34
|
+
return fn();
|
|
35
|
+
}
|
|
36
|
+
throw err;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
export async function withFileLock(filePath, fn) {
|
|
40
|
+
const lockfile = _require("proper-lockfile");
|
|
41
|
+
if (!lockfile)
|
|
42
|
+
return await fn();
|
|
43
|
+
if (!existsSync(filePath))
|
|
44
|
+
return await fn();
|
|
45
|
+
try {
|
|
46
|
+
const release = await lockfile.lock(filePath, { retries: 5, stale: 10000 });
|
|
47
|
+
try {
|
|
48
|
+
return await fn();
|
|
49
|
+
}
|
|
50
|
+
finally {
|
|
51
|
+
await release();
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
catch (err) {
|
|
55
|
+
if (err.code === "ELOCKED") {
|
|
56
|
+
return await fn();
|
|
57
|
+
}
|
|
58
|
+
throw err;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -334,8 +334,9 @@ function resolveAvailableModel(modelId, availableModels, currentProvider) {
|
|
|
334
334
|
* Build the discuss-and-plan prompt for a new milestone.
|
|
335
335
|
* Used by all three "new milestone" paths (first ever, no active, all complete).
|
|
336
336
|
*/
|
|
337
|
-
function buildDiscussPrompt(nextId, preamble, _basePath, preparationContext) {
|
|
337
|
+
function buildDiscussPrompt(nextId, preamble, _basePath, pi, ctx, preparationContext) {
|
|
338
338
|
const milestoneRel = `.gsd/milestones/${nextId}`;
|
|
339
|
+
const structuredQuestionsAvailable = getStructuredQuestionsAvailability(pi, ctx);
|
|
339
340
|
const inlinedTemplates = [
|
|
340
341
|
inlineTemplate("project", "Project"),
|
|
341
342
|
inlineTemplate("requirements", "Requirements"),
|
|
@@ -347,6 +348,7 @@ function buildDiscussPrompt(nextId, preamble, _basePath, preparationContext) {
|
|
|
347
348
|
milestoneId: nextId,
|
|
348
349
|
preamble,
|
|
349
350
|
preparationContext: preparationContext ?? "",
|
|
351
|
+
structuredQuestionsAvailable,
|
|
350
352
|
contextPath: `${milestoneRel}/${nextId}-CONTEXT.md`,
|
|
351
353
|
roadmapPath: `${milestoneRel}/${nextId}-ROADMAP.md`,
|
|
352
354
|
inlinedTemplates,
|
|
@@ -390,7 +392,7 @@ function buildHeadlessDiscussPrompt(nextId, seedContext, _basePath) {
|
|
|
390
392
|
* @param basePath - Root directory of the project
|
|
391
393
|
* @returns The discuss prompt string
|
|
392
394
|
*/
|
|
393
|
-
async function prepareAndBuildDiscussPrompt(ctx, nextId, preamble, basePath) {
|
|
395
|
+
async function prepareAndBuildDiscussPrompt(ctx, pi, nextId, preamble, basePath) {
|
|
394
396
|
const prefs = loadEffectiveGSDPreferences()?.preferences ?? {};
|
|
395
397
|
// Run preparation if enabled (default: true) — results are injected as
|
|
396
398
|
// supplementary context into the standard discuss prompt, NOT as a
|
|
@@ -421,7 +423,7 @@ async function prepareAndBuildDiscussPrompt(ctx, nextId, preamble, basePath) {
|
|
|
421
423
|
logWarning("guided", `preparation failed, proceeding without context: ${err.message}`);
|
|
422
424
|
}
|
|
423
425
|
}
|
|
424
|
-
return buildDiscussPrompt(nextId, preamble, basePath, preparationContext);
|
|
426
|
+
return buildDiscussPrompt(nextId, preamble, basePath, pi, ctx, preparationContext);
|
|
425
427
|
}
|
|
426
428
|
/**
|
|
427
429
|
* Bootstrap a .gsd/ project from scratch for headless use.
|
|
@@ -638,7 +640,7 @@ export async function showDiscuss(ctx, pi, basePath) {
|
|
|
638
640
|
const uniqueMilestoneIds = !!loadEffectiveGSDPreferences()?.preferences?.unique_milestone_ids;
|
|
639
641
|
const nextId = nextMilestoneIdReserved(milestoneIds, uniqueMilestoneIds);
|
|
640
642
|
pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId: nextId, step: false, createdAt: Date.now() });
|
|
641
|
-
await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, nextId, `New milestone ${nextId}.`, basePath), "gsd-run", ctx, "discuss-milestone");
|
|
643
|
+
await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, pi, nextId, `New milestone ${nextId}.`, basePath), "gsd-run", ctx, "discuss-milestone");
|
|
642
644
|
}
|
|
643
645
|
return;
|
|
644
646
|
}
|
|
@@ -994,7 +996,7 @@ async function handleMilestoneActions(ctx, pi, basePath, milestoneId, milestoneT
|
|
|
994
996
|
const uniqueMilestoneIds = !!loadEffectiveGSDPreferences()?.preferences?.unique_milestone_ids;
|
|
995
997
|
const nextId = nextMilestoneIdReserved(milestoneIds, uniqueMilestoneIds);
|
|
996
998
|
pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId: nextId, step: stepMode, createdAt: Date.now() });
|
|
997
|
-
await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, nextId, `New milestone ${nextId}.`, basePath), "gsd-run", ctx, "discuss-milestone");
|
|
999
|
+
await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, pi, nextId, `New milestone ${nextId}.`, basePath), "gsd-run", ctx, "discuss-milestone");
|
|
998
1000
|
return true;
|
|
999
1001
|
}
|
|
1000
1002
|
// "back" or null
|
|
@@ -1161,7 +1163,7 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
|
|
|
1161
1163
|
if (isFirst) {
|
|
1162
1164
|
// First ever — skip wizard, just ask directly
|
|
1163
1165
|
pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId: nextId, step: stepMode, createdAt: Date.now() });
|
|
1164
|
-
await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, nextId, `New project, milestone ${nextId}. Do NOT read or explore .gsd/ — it's empty scaffolding.`, basePath), "gsd-run", ctx, "discuss-milestone");
|
|
1166
|
+
await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, pi, nextId, `New project, milestone ${nextId}. Do NOT read or explore .gsd/ — it's empty scaffolding.`, basePath), "gsd-run", ctx, "discuss-milestone");
|
|
1165
1167
|
}
|
|
1166
1168
|
else {
|
|
1167
1169
|
const choice = await showNextAction(ctx, {
|
|
@@ -1179,7 +1181,7 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
|
|
|
1179
1181
|
});
|
|
1180
1182
|
if (choice === "new_milestone") {
|
|
1181
1183
|
pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId: nextId, step: stepMode, createdAt: Date.now() });
|
|
1182
|
-
await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, nextId, `New milestone ${nextId}.`, basePath), "gsd-run", ctx, "discuss-milestone");
|
|
1184
|
+
await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, pi, nextId, `New milestone ${nextId}.`, basePath), "gsd-run", ctx, "discuss-milestone");
|
|
1183
1185
|
}
|
|
1184
1186
|
}
|
|
1185
1187
|
return;
|
|
@@ -1211,7 +1213,7 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
|
|
|
1211
1213
|
const uniqueMilestoneIds = !!loadEffectiveGSDPreferences()?.preferences?.unique_milestone_ids;
|
|
1212
1214
|
const nextId = nextMilestoneIdReserved(milestoneIds, uniqueMilestoneIds);
|
|
1213
1215
|
pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId: nextId, step: stepMode, createdAt: Date.now() });
|
|
1214
|
-
await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, nextId, `New milestone ${nextId}.`, basePath), "gsd-run", ctx, "discuss-milestone");
|
|
1216
|
+
await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, pi, nextId, `New milestone ${nextId}.`, basePath), "gsd-run", ctx, "discuss-milestone");
|
|
1215
1217
|
}
|
|
1216
1218
|
else if (choice === "status") {
|
|
1217
1219
|
const { fireStatusViaCommand } = await import("./commands.js");
|
|
@@ -1275,7 +1277,7 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
|
|
|
1275
1277
|
const uniqueMilestoneIds = !!loadEffectiveGSDPreferences()?.preferences?.unique_milestone_ids;
|
|
1276
1278
|
const nextId = nextMilestoneIdReserved(milestoneIds, uniqueMilestoneIds);
|
|
1277
1279
|
pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId: nextId, step: stepMode, createdAt: Date.now() });
|
|
1278
|
-
await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, nextId, `New milestone ${nextId}.`, basePath), "gsd-run", ctx, "discuss-milestone");
|
|
1280
|
+
await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, pi, nextId, `New milestone ${nextId}.`, basePath), "gsd-run", ctx, "discuss-milestone");
|
|
1279
1281
|
}
|
|
1280
1282
|
return;
|
|
1281
1283
|
}
|
|
@@ -1365,7 +1367,7 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
|
|
|
1365
1367
|
const uniqueMilestoneIds = !!loadEffectiveGSDPreferences()?.preferences?.unique_milestone_ids;
|
|
1366
1368
|
const nextId = nextMilestoneIdReserved(milestoneIds, uniqueMilestoneIds);
|
|
1367
1369
|
pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId: nextId, step: stepMode, createdAt: Date.now() });
|
|
1368
|
-
await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, nextId, `New milestone ${nextId}.`, basePath), "gsd-run", ctx, "discuss-milestone");
|
|
1370
|
+
await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, pi, nextId, `New milestone ${nextId}.`, basePath), "gsd-run", ctx, "discuss-milestone");
|
|
1369
1371
|
}
|
|
1370
1372
|
else if (choice === "discard_milestone") {
|
|
1371
1373
|
const confirmed = await showConfirm(ctx, {
|
|
@@ -225,17 +225,9 @@ export async function showProjectInit(ctx, pi, basePath, detection) {
|
|
|
225
225
|
catch {
|
|
226
226
|
// Non-fatal — STATE.md will be regenerated on next /gsd invocation
|
|
227
227
|
}
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
const result = ensureProjectWorkflowMcpConfig(basePath);
|
|
232
|
-
if (result.status !== "unchanged") {
|
|
233
|
-
ctx.ui.notify(`Claude Code MCP prepared at ${result.configPath}`, "info");
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
catch (err) {
|
|
237
|
-
ctx.ui.notify(`Claude Code MCP prep failed: ${err instanceof Error ? err.message : String(err)}`, "warning");
|
|
238
|
-
}
|
|
228
|
+
{
|
|
229
|
+
const { prepareWorkflowMcpForProject } = await import("./workflow-mcp-auto-prep.js");
|
|
230
|
+
prepareWorkflowMcpForProject(ctx, basePath);
|
|
239
231
|
}
|
|
240
232
|
ctx.ui.notify("GSD initialized. Starting your first milestone...", "info");
|
|
241
233
|
return { completed: true, bootstrapped: true };
|