gsd-pi 2.70.0 → 2.70.1-dev.366a872
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/loader.js +4 -0
- package/dist/resources/extensions/claude-code-cli/stream-adapter.js +270 -23
- package/dist/resources/extensions/get-secrets-from-user.js +17 -1
- package/dist/resources/extensions/gsd/auto-model-selection.js +33 -19
- package/dist/resources/extensions/gsd/auto-prompts.js +7 -3
- package/dist/resources/extensions/gsd/auto-start.js +28 -12
- package/dist/resources/extensions/gsd/auto.js +12 -8
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +4 -0
- package/dist/resources/extensions/gsd/commands-handlers.js +22 -8
- package/dist/resources/extensions/gsd/custom-workflow-engine.js +16 -12
- package/dist/resources/extensions/gsd/doctor-engine-checks.js +12 -0
- package/dist/resources/extensions/gsd/doctor-format.js +2 -0
- package/dist/resources/extensions/gsd/file-lock.js +60 -0
- package/dist/resources/extensions/gsd/guided-flow.js +33 -20
- package/dist/resources/extensions/gsd/init-wizard.js +3 -11
- package/dist/resources/extensions/gsd/pre-execution-checks.js +5 -3
- package/dist/resources/extensions/gsd/prompts/discuss.md +31 -13
- package/dist/resources/extensions/gsd/state.js +234 -332
- package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +34 -0
- package/dist/resources/extensions/gsd/validate-directory.js +30 -12
- package/dist/resources/extensions/gsd/workflow-events.js +25 -13
- package/dist/resources/extensions/gsd/workflow-mcp-auto-prep.js +56 -0
- package/dist/resources/extensions/gsd/workflow-mcp.js +12 -1
- package/dist/resources/extensions/slash-commands/audit.js +2 -1
- package/dist/resources/extensions/subagent/isolation.js +4 -2
- package/dist/update-check.d.ts +1 -0
- package/dist/update-check.js +30 -27
- package/dist/update-cmd.js +3 -11
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +11 -11
- package/dist/web/standalone/.next/build-manifest.json +4 -4
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
- package/dist/web/standalone/.next/required-server-files.json +3 -3
- package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
- package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
- package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +3 -3
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +3 -3
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/experimental/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/experimental/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/notifications/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/notifications/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +4 -4
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -4
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +3 -3
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/page.js +2 -2
- package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +11 -11
- package/dist/web/standalone/.next/server/chunks/63.js +3 -3
- package/dist/web/standalone/.next/server/chunks/6897.js +1 -1
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware.js +2 -2
- package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
- package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +1 -1
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/dist/web/standalone/.next/static/chunks/2826.dd3dc8bbd3025fa5.js +9 -0
- package/dist/web/standalone/.next/static/chunks/app/_not-found/{page-2f24283c162b6ab3.js → page-f2a7482d42a5614b.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/app/{layout-9ecfd95f343793f0.js → layout-a16c7a7ecdf0c2cf.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/app/page-f1e30ab6bb269149.js +1 -0
- package/dist/web/standalone/.next/static/chunks/main-app-fdab67f7802d7832.js +1 -0
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +1 -0
- package/dist/web/standalone/.next/static/chunks/{webpack-6e4d7e9a4f57bed4.js → webpack-b868033a5834586d.js} +1 -1
- package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
- package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
- package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
- package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api.target.mk +14 -14
- package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_except.target.mk +14 -14
- package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_maybe.target.mk +14 -14
- package/dist/web/standalone/server.js +1 -1
- package/dist/web-mode.js +4 -0
- package/package.json +11 -11
- package/packages/mcp-server/dist/env-writer.d.ts +39 -0
- package/packages/mcp-server/dist/env-writer.d.ts.map +1 -0
- package/packages/mcp-server/dist/env-writer.js +158 -0
- package/packages/mcp-server/dist/env-writer.js.map +1 -0
- package/packages/mcp-server/dist/server.d.ts +11 -2
- package/packages/mcp-server/dist/server.d.ts.map +1 -1
- package/packages/mcp-server/dist/server.js +102 -2
- package/packages/mcp-server/dist/server.js.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.d.ts +2 -0
- package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.js +35 -3
- package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
- package/packages/mcp-server/src/env-writer.test.ts +280 -0
- package/packages/mcp-server/src/env-writer.ts +183 -0
- package/packages/mcp-server/src/import-candidates.test.ts +48 -0
- package/packages/mcp-server/src/secure-env-collect.test.ts +265 -0
- package/packages/mcp-server/src/server.ts +137 -3
- package/packages/mcp-server/src/workflow-tools.ts +34 -1
- package/packages/pi-agent-core/dist/agent.d.ts +8 -0
- package/packages/pi-agent-core/dist/agent.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/agent.js +3 -0
- package/packages/pi-agent-core/dist/agent.js.map +1 -1
- package/packages/pi-agent-core/src/agent.test.ts +82 -0
- package/packages/pi-agent-core/src/agent.ts +12 -0
- package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js +348 -0
- package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/config.d.ts +1 -0
- package/packages/pi-coding-agent/dist/core/lsp/config.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/config.js +38 -15
- package/packages/pi-coding-agent/dist/core/lsp/config.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/sdk.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/sdk.js +10 -0
- package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.d.ts +19 -2
- package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.js +50 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/extension-input.d.ts +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/extension-input.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/extension-input.js +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/extension-input.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +158 -23
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +6 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +58 -2
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js +3 -1
- package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-types.d.ts +1 -0
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-types.js.map +1 -1
- package/packages/pi-coding-agent/package.json +1 -1
- package/packages/pi-coding-agent/src/core/chat-controller-ordering.test.ts +418 -0
- package/packages/pi-coding-agent/src/core/extensions/types.ts +2 -0
- package/packages/pi-coding-agent/src/core/lsp/config.ts +43 -17
- package/packages/pi-coding-agent/src/core/sdk.ts +8 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/dynamic-border.ts +58 -2
- package/packages/pi-coding-agent/src/modes/interactive/components/extension-input.ts +2 -0
- package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +189 -29
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode-state.ts +1 -0
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +66 -2
- package/packages/pi-coding-agent/src/modes/interactive/slash-command-handlers.ts +7 -5
- package/packages/pi-coding-agent/src/modes/rpc/rpc-mode.ts +1 -1
- package/packages/pi-coding-agent/src/modes/rpc/rpc-types.ts +1 -0
- package/packages/pi-tui/dist/components/__tests__/input.test.js +9 -0
- package/packages/pi-tui/dist/components/__tests__/input.test.js.map +1 -1
- package/packages/pi-tui/dist/components/__tests__/markdown-maxlines.test.d.ts +2 -0
- package/packages/pi-tui/dist/components/__tests__/markdown-maxlines.test.d.ts.map +1 -0
- package/packages/pi-tui/dist/components/__tests__/markdown-maxlines.test.js +66 -0
- package/packages/pi-tui/dist/components/__tests__/markdown-maxlines.test.js.map +1 -0
- package/packages/pi-tui/dist/components/input.d.ts +2 -0
- package/packages/pi-tui/dist/components/input.d.ts.map +1 -1
- package/packages/pi-tui/dist/components/input.js +7 -4
- package/packages/pi-tui/dist/components/input.js.map +1 -1
- package/packages/pi-tui/dist/components/markdown.d.ts +3 -0
- package/packages/pi-tui/dist/components/markdown.d.ts.map +1 -1
- package/packages/pi-tui/dist/components/markdown.js +17 -1
- package/packages/pi-tui/dist/components/markdown.js.map +1 -1
- package/packages/pi-tui/src/components/__tests__/input.test.ts +11 -0
- package/packages/pi-tui/src/components/__tests__/markdown-maxlines.test.ts +75 -0
- package/packages/pi-tui/src/components/input.ts +7 -4
- package/packages/pi-tui/src/components/markdown.ts +22 -1
- package/pkg/package.json +1 -1
- package/src/resources/extensions/claude-code-cli/stream-adapter.ts +384 -24
- package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +317 -0
- package/src/resources/extensions/get-secrets-from-user.ts +24 -1
- package/src/resources/extensions/gsd/auto-model-selection.ts +39 -25
- package/src/resources/extensions/gsd/auto-prompts.ts +7 -3
- package/src/resources/extensions/gsd/auto-start.ts +37 -14
- package/src/resources/extensions/gsd/auto.ts +12 -8
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +4 -0
- package/src/resources/extensions/gsd/commands-handlers.ts +22 -7
- package/src/resources/extensions/gsd/custom-workflow-engine.ts +19 -14
- package/src/resources/extensions/gsd/doctor-engine-checks.ts +14 -0
- package/src/resources/extensions/gsd/doctor-format.ts +1 -0
- package/src/resources/extensions/gsd/doctor-types.ts +1 -0
- package/src/resources/extensions/gsd/file-lock.ts +59 -0
- package/src/resources/extensions/gsd/guided-flow.ts +36 -17
- package/src/resources/extensions/gsd/init-wizard.ts +3 -13
- package/src/resources/extensions/gsd/pre-execution-checks.ts +6 -3
- package/src/resources/extensions/gsd/prompts/discuss.md +31 -13
- package/src/resources/extensions/gsd/state.ts +274 -344
- package/src/resources/extensions/gsd/tests/derive-state-helpers.test.ts +436 -0
- package/src/resources/extensions/gsd/tests/discuss-incremental-persistence.test.ts +9 -0
- package/src/resources/extensions/gsd/tests/doctor-scope-db-unavailable.test.ts +43 -0
- package/src/resources/extensions/gsd/tests/file-lock.test.ts +103 -0
- package/src/resources/extensions/gsd/tests/interactive-routing-bypass.test.ts +207 -0
- package/src/resources/extensions/gsd/tests/pre-exec-backtick-strip.test.ts +48 -1
- package/src/resources/extensions/gsd/tests/resource-loader-import-path.test.ts +8 -7
- package/src/resources/extensions/gsd/tests/secure-env-collect.test.ts +45 -0
- package/src/resources/extensions/gsd/tests/validate-directory.test.ts +33 -1
- package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +87 -1
- package/src/resources/extensions/gsd/tests/workflow-mcp-auto-prep.test.ts +76 -0
- package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +180 -1
- package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +22 -0
- package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +60 -25
- package/src/resources/extensions/gsd/validate-directory.ts +33 -11
- package/src/resources/extensions/gsd/workflow-events.ts +34 -25
- package/src/resources/extensions/gsd/workflow-mcp-auto-prep.ts +76 -0
- package/src/resources/extensions/gsd/workflow-mcp.ts +16 -1
- package/src/resources/extensions/slash-commands/audit.ts +2 -1
- package/src/resources/extensions/subagent/isolation.ts +4 -3
- package/dist/web/standalone/.next/static/chunks/2826.821e01b07d92e948.js +0 -9
- package/dist/web/standalone/.next/static/chunks/app/page-7115e62689b5fd84.js +0 -1
- package/dist/web/standalone/.next/static/chunks/main-app-d3d4c336195465f9.js +0 -1
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ab5a8926e07ec673.js +0 -1
- /package/dist/web/standalone/.next/static/{Nl6lg7zP5dNgNBV1107v1 → eMly3KqfZihJJAHQGmxvO}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{Nl6lg7zP5dNgNBV1107v1 → eMly3KqfZihJJAHQGmxvO}/_ssgManifest.js +0 -0
|
@@ -15,6 +15,26 @@ import { isAutoActive, checkRemoteAutoSession } from "./auto.js";
|
|
|
15
15
|
import { getAutoWorktreePath } from "./auto-worktree.js";
|
|
16
16
|
import { projectRoot } from "./commands/context.js";
|
|
17
17
|
import { loadPrompt } from "./prompt-loader.js";
|
|
18
|
+
const UPDATE_REGISTRY_URL = "https://registry.npmjs.org/gsd-pi/latest";
|
|
19
|
+
const UPDATE_FETCH_TIMEOUT_MS = 5000;
|
|
20
|
+
async function fetchLatestVersionForCommand() {
|
|
21
|
+
const controller = new AbortController();
|
|
22
|
+
const timeout = setTimeout(() => controller.abort(), UPDATE_FETCH_TIMEOUT_MS);
|
|
23
|
+
try {
|
|
24
|
+
const res = await fetch(UPDATE_REGISTRY_URL, { signal: controller.signal });
|
|
25
|
+
if (!res.ok)
|
|
26
|
+
return null;
|
|
27
|
+
const data = (await res.json());
|
|
28
|
+
const latest = typeof data.version === "string" ? data.version.trim().replace(/^v/, "") : "";
|
|
29
|
+
return latest.length > 0 ? latest : null;
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
finally {
|
|
35
|
+
clearTimeout(timeout);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
18
38
|
export function dispatchDoctorHeal(pi, scope, reportText, structuredIssues) {
|
|
19
39
|
const workflowPath = process.env.GSD_WORKFLOW_PATH ?? join(process.env.HOME ?? "~", ".gsd", "agent", "GSD-WORKFLOW.md");
|
|
20
40
|
const workflow = readFileSync(workflowPath, "utf-8");
|
|
@@ -311,14 +331,8 @@ export async function handleUpdate(ctx) {
|
|
|
311
331
|
const NPM_PACKAGE = "gsd-pi";
|
|
312
332
|
const current = process.env.GSD_VERSION || "0.0.0";
|
|
313
333
|
ctx.ui.notify(`Current version: v${current}\nChecking npm registry...`, "info");
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
latest = execSync(`npm view ${NPM_PACKAGE} version`, {
|
|
317
|
-
encoding: "utf-8",
|
|
318
|
-
stdio: ["ignore", "pipe", "ignore"],
|
|
319
|
-
}).trim();
|
|
320
|
-
}
|
|
321
|
-
catch {
|
|
334
|
+
const latest = await fetchLatestVersionForCommand();
|
|
335
|
+
if (!latest) {
|
|
322
336
|
ctx.ui.notify("Failed to reach npm registry. Check your network connection.", "error");
|
|
323
337
|
return;
|
|
324
338
|
}
|
|
@@ -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.
|
|
@@ -6,6 +6,18 @@ import { deriveState } from "./state.js";
|
|
|
6
6
|
import { readEvents } from "./workflow-events.js";
|
|
7
7
|
import { renderAllProjections } from "./workflow-projections.js";
|
|
8
8
|
export async function checkEngineHealth(basePath, issues, fixesApplied) {
|
|
9
|
+
const dbPath = join(basePath, ".gsd", "gsd.db");
|
|
10
|
+
if (!isDbAvailable() && existsSync(dbPath)) {
|
|
11
|
+
issues.push({
|
|
12
|
+
severity: "warning",
|
|
13
|
+
code: "db_unavailable",
|
|
14
|
+
scope: "project",
|
|
15
|
+
unitId: "project",
|
|
16
|
+
message: "Database unavailable — using filesystem state derivation (degraded mode). State queries may be slower and less reliable.",
|
|
17
|
+
file: ".gsd/gsd.db",
|
|
18
|
+
fixable: false,
|
|
19
|
+
});
|
|
20
|
+
}
|
|
9
21
|
// ── DB constraint violation detection (full doctor only, not pre-dispatch per D-10) ──
|
|
10
22
|
try {
|
|
11
23
|
if (isDbAvailable()) {
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
function matchesScope(unitId, scope) {
|
|
2
2
|
if (!scope)
|
|
3
3
|
return true;
|
|
4
|
+
if (unitId === "project" || unitId === "environment")
|
|
5
|
+
return true;
|
|
4
6
|
return unitId === scope || unitId.startsWith(`${scope}/`) || unitId.startsWith(`${scope}`);
|
|
5
7
|
}
|
|
6
8
|
export function summarizeDoctorIssues(issues) {
|
|
@@ -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
|
+
}
|
|
@@ -35,7 +35,7 @@ import { findMilestoneIds, nextMilestoneId, reserveMilestoneId, getReservedMiles
|
|
|
35
35
|
import { parkMilestone, discardMilestone } from "./milestone-actions.js";
|
|
36
36
|
import { selectAndApplyModel } from "./auto-model-selection.js";
|
|
37
37
|
import { DISCUSS_TOOLS_ALLOWLIST } from "./constants.js";
|
|
38
|
-
import { getWorkflowTransportSupportError, getRequiredWorkflowToolsForGuidedUnit, } from "./workflow-mcp.js";
|
|
38
|
+
import { getWorkflowTransportSupportError, getRequiredWorkflowToolsForGuidedUnit, supportsStructuredQuestions, } from "./workflow-mcp.js";
|
|
39
39
|
import { runPreparation, formatCodebaseBrief, formatPriorContextBrief, } from "./preparation.js";
|
|
40
40
|
// ─── Re-exports (preserve public API for existing importers) ────────────────
|
|
41
41
|
export { MILESTONE_ID_RE, generateMilestoneSuffix, nextMilestoneId, extractMilestoneSeq, parseMilestoneId, milestoneIdSort, maxMilestoneNum, findMilestoneIds, reserveMilestoneId, claimReservedId, getReservedMilestoneIds, clearReservedMilestoneIds, } from "./milestone-ids.js";
|
|
@@ -236,7 +236,8 @@ async function dispatchWorkflow(pi, note, customType = "gsd-run", ctx, unitType)
|
|
|
236
236
|
// tier downgrade, fallback chains) — same path as auto-mode dispatches (#2958).
|
|
237
237
|
if (ctx && unitType) {
|
|
238
238
|
const prefs = loadEffectiveGSDPreferences()?.preferences;
|
|
239
|
-
const result = await selectAndApplyModel(ctx, pi, unitType, /* unitId */ "", /* basePath */ process.cwd(), prefs, /* verbose */ false, /* autoModeStartModel */ null
|
|
239
|
+
const result = await selectAndApplyModel(ctx, pi, unitType, /* unitId */ "", /* basePath */ process.cwd(), prefs, /* verbose */ false, /* autoModeStartModel */ null,
|
|
240
|
+
/* retryContext */ undefined, /* isAutoMode */ false);
|
|
240
241
|
if (result.appliedModel) {
|
|
241
242
|
debugLog("guided-flow-model-applied", {
|
|
242
243
|
unitType,
|
|
@@ -294,6 +295,16 @@ async function dispatchWorkflow(pi, note, customType = "gsd-run", ctx, unitType)
|
|
|
294
295
|
pi.setActiveTools(savedTools);
|
|
295
296
|
}
|
|
296
297
|
}
|
|
298
|
+
function getStructuredQuestionsAvailability(pi, ctx) {
|
|
299
|
+
if (!ctx)
|
|
300
|
+
return "false";
|
|
301
|
+
const provider = ctx.model?.provider;
|
|
302
|
+
const authMode = provider ? ctx.modelRegistry.getProviderAuthMode(provider) : undefined;
|
|
303
|
+
return supportsStructuredQuestions(pi.getActiveTools(), {
|
|
304
|
+
authMode,
|
|
305
|
+
baseUrl: ctx.model?.baseUrl,
|
|
306
|
+
}) ? "true" : "false";
|
|
307
|
+
}
|
|
297
308
|
/**
|
|
298
309
|
* Resolve a model ID string to a model object from available models.
|
|
299
310
|
* Handles "provider/model" and bare ID formats.
|
|
@@ -323,8 +334,9 @@ function resolveAvailableModel(modelId, availableModels, currentProvider) {
|
|
|
323
334
|
* Build the discuss-and-plan prompt for a new milestone.
|
|
324
335
|
* Used by all three "new milestone" paths (first ever, no active, all complete).
|
|
325
336
|
*/
|
|
326
|
-
function buildDiscussPrompt(nextId, preamble, _basePath, preparationContext) {
|
|
337
|
+
function buildDiscussPrompt(nextId, preamble, _basePath, pi, ctx, preparationContext) {
|
|
327
338
|
const milestoneRel = `.gsd/milestones/${nextId}`;
|
|
339
|
+
const structuredQuestionsAvailable = getStructuredQuestionsAvailability(pi, ctx);
|
|
328
340
|
const inlinedTemplates = [
|
|
329
341
|
inlineTemplate("project", "Project"),
|
|
330
342
|
inlineTemplate("requirements", "Requirements"),
|
|
@@ -336,6 +348,7 @@ function buildDiscussPrompt(nextId, preamble, _basePath, preparationContext) {
|
|
|
336
348
|
milestoneId: nextId,
|
|
337
349
|
preamble,
|
|
338
350
|
preparationContext: preparationContext ?? "",
|
|
351
|
+
structuredQuestionsAvailable,
|
|
339
352
|
contextPath: `${milestoneRel}/${nextId}-CONTEXT.md`,
|
|
340
353
|
roadmapPath: `${milestoneRel}/${nextId}-ROADMAP.md`,
|
|
341
354
|
inlinedTemplates,
|
|
@@ -379,7 +392,7 @@ function buildHeadlessDiscussPrompt(nextId, seedContext, _basePath) {
|
|
|
379
392
|
* @param basePath - Root directory of the project
|
|
380
393
|
* @returns The discuss prompt string
|
|
381
394
|
*/
|
|
382
|
-
async function prepareAndBuildDiscussPrompt(ctx, nextId, preamble, basePath) {
|
|
395
|
+
async function prepareAndBuildDiscussPrompt(ctx, pi, nextId, preamble, basePath) {
|
|
383
396
|
const prefs = loadEffectiveGSDPreferences()?.preferences ?? {};
|
|
384
397
|
// Run preparation if enabled (default: true) — results are injected as
|
|
385
398
|
// supplementary context into the standard discuss prompt, NOT as a
|
|
@@ -410,7 +423,7 @@ async function prepareAndBuildDiscussPrompt(ctx, nextId, preamble, basePath) {
|
|
|
410
423
|
logWarning("guided", `preparation failed, proceeding without context: ${err.message}`);
|
|
411
424
|
}
|
|
412
425
|
}
|
|
413
|
-
return buildDiscussPrompt(nextId, preamble, basePath, preparationContext);
|
|
426
|
+
return buildDiscussPrompt(nextId, preamble, basePath, pi, ctx, preparationContext);
|
|
414
427
|
}
|
|
415
428
|
/**
|
|
416
429
|
* Bootstrap a .gsd/ project from scratch for headless use.
|
|
@@ -600,7 +613,7 @@ export async function showDiscuss(ctx, pi, basePath) {
|
|
|
600
613
|
});
|
|
601
614
|
if (choice === "discuss_draft") {
|
|
602
615
|
const discussMilestoneTemplates = inlineTemplate("context", "Context");
|
|
603
|
-
const structuredQuestionsAvailable = pi
|
|
616
|
+
const structuredQuestionsAvailable = getStructuredQuestionsAvailability(pi, ctx);
|
|
604
617
|
const basePrompt = loadPrompt("guided-discuss-milestone", {
|
|
605
618
|
milestoneId: mid, milestoneTitle, inlinedTemplates: discussMilestoneTemplates, structuredQuestionsAvailable,
|
|
606
619
|
commitInstruction: buildDocsCommitInstruction(`docs(${mid}): milestone context from discuss`),
|
|
@@ -614,7 +627,7 @@ export async function showDiscuss(ctx, pi, basePath) {
|
|
|
614
627
|
}
|
|
615
628
|
else if (choice === "discuss_fresh") {
|
|
616
629
|
const discussMilestoneTemplates = inlineTemplate("context", "Context");
|
|
617
|
-
const structuredQuestionsAvailable = pi
|
|
630
|
+
const structuredQuestionsAvailable = getStructuredQuestionsAvailability(pi, ctx);
|
|
618
631
|
pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId: mid, step: false, createdAt: Date.now() });
|
|
619
632
|
await dispatchWorkflow(pi, loadPrompt("guided-discuss-milestone", {
|
|
620
633
|
milestoneId: mid, milestoneTitle, inlinedTemplates: discussMilestoneTemplates, structuredQuestionsAvailable,
|
|
@@ -627,7 +640,7 @@ export async function showDiscuss(ctx, pi, basePath) {
|
|
|
627
640
|
const uniqueMilestoneIds = !!loadEffectiveGSDPreferences()?.preferences?.unique_milestone_ids;
|
|
628
641
|
const nextId = nextMilestoneIdReserved(milestoneIds, uniqueMilestoneIds);
|
|
629
642
|
pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId: nextId, step: false, createdAt: Date.now() });
|
|
630
|
-
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");
|
|
631
644
|
}
|
|
632
645
|
return;
|
|
633
646
|
}
|
|
@@ -757,7 +770,7 @@ export async function showDiscuss(ctx, pi, basePath) {
|
|
|
757
770
|
if (confirm !== "rediscuss")
|
|
758
771
|
continue;
|
|
759
772
|
}
|
|
760
|
-
const sqAvail = pi
|
|
773
|
+
const sqAvail = getStructuredQuestionsAvailability(pi, ctx);
|
|
761
774
|
const prompt = await buildDiscussSlicePrompt(mid, chosen.id, chosen.title, basePath, { rediscuss: isRediscuss, structuredQuestionsAvailable: sqAvail });
|
|
762
775
|
await dispatchWorkflow(pi, prompt, "gsd-discuss", ctx, "discuss-slice");
|
|
763
776
|
// Wait for the discuss session to finish, then loop back to the picker
|
|
@@ -848,7 +861,7 @@ async function dispatchDiscussForMilestone(ctx, pi, basePath, mid, milestoneTitl
|
|
|
848
861
|
].join("\n")
|
|
849
862
|
: "";
|
|
850
863
|
const discussMilestoneTemplates = inlineTemplate("context", "Context");
|
|
851
|
-
const structuredQuestionsAvailable = pi
|
|
864
|
+
const structuredQuestionsAvailable = getStructuredQuestionsAvailability(pi, ctx);
|
|
852
865
|
const basePrompt = loadPrompt("guided-discuss-milestone", {
|
|
853
866
|
milestoneId: mid,
|
|
854
867
|
milestoneTitle,
|
|
@@ -983,7 +996,7 @@ async function handleMilestoneActions(ctx, pi, basePath, milestoneId, milestoneT
|
|
|
983
996
|
const uniqueMilestoneIds = !!loadEffectiveGSDPreferences()?.preferences?.unique_milestone_ids;
|
|
984
997
|
const nextId = nextMilestoneIdReserved(milestoneIds, uniqueMilestoneIds);
|
|
985
998
|
pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId: nextId, step: stepMode, createdAt: Date.now() });
|
|
986
|
-
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");
|
|
987
1000
|
return true;
|
|
988
1001
|
}
|
|
989
1002
|
// "back" or null
|
|
@@ -1150,7 +1163,7 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
|
|
|
1150
1163
|
if (isFirst) {
|
|
1151
1164
|
// First ever — skip wizard, just ask directly
|
|
1152
1165
|
pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId: nextId, step: stepMode, createdAt: Date.now() });
|
|
1153
|
-
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");
|
|
1154
1167
|
}
|
|
1155
1168
|
else {
|
|
1156
1169
|
const choice = await showNextAction(ctx, {
|
|
@@ -1168,7 +1181,7 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
|
|
|
1168
1181
|
});
|
|
1169
1182
|
if (choice === "new_milestone") {
|
|
1170
1183
|
pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId: nextId, step: stepMode, createdAt: Date.now() });
|
|
1171
|
-
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");
|
|
1172
1185
|
}
|
|
1173
1186
|
}
|
|
1174
1187
|
return;
|
|
@@ -1200,7 +1213,7 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
|
|
|
1200
1213
|
const uniqueMilestoneIds = !!loadEffectiveGSDPreferences()?.preferences?.unique_milestone_ids;
|
|
1201
1214
|
const nextId = nextMilestoneIdReserved(milestoneIds, uniqueMilestoneIds);
|
|
1202
1215
|
pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId: nextId, step: stepMode, createdAt: Date.now() });
|
|
1203
|
-
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");
|
|
1204
1217
|
}
|
|
1205
1218
|
else if (choice === "status") {
|
|
1206
1219
|
const { fireStatusViaCommand } = await import("./commands.js");
|
|
@@ -1237,7 +1250,7 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
|
|
|
1237
1250
|
});
|
|
1238
1251
|
if (choice === "discuss_draft") {
|
|
1239
1252
|
const discussMilestoneTemplates = inlineTemplate("context", "Context");
|
|
1240
|
-
const structuredQuestionsAvailable = pi
|
|
1253
|
+
const structuredQuestionsAvailable = getStructuredQuestionsAvailability(pi, ctx);
|
|
1241
1254
|
const basePrompt = loadPrompt("guided-discuss-milestone", {
|
|
1242
1255
|
milestoneId, milestoneTitle, inlinedTemplates: discussMilestoneTemplates, structuredQuestionsAvailable,
|
|
1243
1256
|
commitInstruction: buildDocsCommitInstruction(`docs(${milestoneId}): milestone context from discuss`),
|
|
@@ -1251,7 +1264,7 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
|
|
|
1251
1264
|
}
|
|
1252
1265
|
else if (choice === "discuss_fresh") {
|
|
1253
1266
|
const discussMilestoneTemplates = inlineTemplate("context", "Context");
|
|
1254
|
-
const structuredQuestionsAvailable = pi
|
|
1267
|
+
const structuredQuestionsAvailable = getStructuredQuestionsAvailability(pi, ctx);
|
|
1255
1268
|
pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId, step: stepMode, createdAt: Date.now() });
|
|
1256
1269
|
await dispatchWorkflow(pi, loadPrompt("guided-discuss-milestone", {
|
|
1257
1270
|
milestoneId, milestoneTitle, inlinedTemplates: discussMilestoneTemplates, structuredQuestionsAvailable,
|
|
@@ -1264,7 +1277,7 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
|
|
|
1264
1277
|
const uniqueMilestoneIds = !!loadEffectiveGSDPreferences()?.preferences?.unique_milestone_ids;
|
|
1265
1278
|
const nextId = nextMilestoneIdReserved(milestoneIds, uniqueMilestoneIds);
|
|
1266
1279
|
pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId: nextId, step: stepMode, createdAt: Date.now() });
|
|
1267
|
-
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");
|
|
1268
1281
|
}
|
|
1269
1282
|
return;
|
|
1270
1283
|
}
|
|
@@ -1342,7 +1355,7 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
|
|
|
1342
1355
|
}
|
|
1343
1356
|
else if (choice === "discuss") {
|
|
1344
1357
|
const discussMilestoneTemplates = inlineTemplate("context", "Context");
|
|
1345
|
-
const structuredQuestionsAvailable = pi
|
|
1358
|
+
const structuredQuestionsAvailable = getStructuredQuestionsAvailability(pi, ctx);
|
|
1346
1359
|
await dispatchWorkflow(pi, loadPrompt("guided-discuss-milestone", {
|
|
1347
1360
|
milestoneId, milestoneTitle, inlinedTemplates: discussMilestoneTemplates, structuredQuestionsAvailable,
|
|
1348
1361
|
commitInstruction: buildDocsCommitInstruction(`docs(${milestoneId}): milestone context from discuss`),
|
|
@@ -1354,7 +1367,7 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
|
|
|
1354
1367
|
const uniqueMilestoneIds = !!loadEffectiveGSDPreferences()?.preferences?.unique_milestone_ids;
|
|
1355
1368
|
const nextId = nextMilestoneIdReserved(milestoneIds, uniqueMilestoneIds);
|
|
1356
1369
|
pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId: nextId, step: stepMode, createdAt: Date.now() });
|
|
1357
|
-
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");
|
|
1358
1371
|
}
|
|
1359
1372
|
else if (choice === "discard_milestone") {
|
|
1360
1373
|
const confirmed = await showConfirm(ctx, {
|
|
@@ -1480,7 +1493,7 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
|
|
|
1480
1493
|
}), "gsd-run", ctx, "plan-slice");
|
|
1481
1494
|
}
|
|
1482
1495
|
else if (choice === "discuss") {
|
|
1483
|
-
const sqAvail = pi
|
|
1496
|
+
const sqAvail = getStructuredQuestionsAvailability(pi, ctx);
|
|
1484
1497
|
await dispatchWorkflow(pi, await buildDiscussSlicePrompt(milestoneId, sliceId, sliceTitle, basePath, { rediscuss: hasContext, structuredQuestionsAvailable: sqAvail }), "gsd-run", ctx, "discuss-slice");
|
|
1485
1498
|
}
|
|
1486
1499
|
else if (choice === "research") {
|
|
@@ -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 };
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
import { existsSync } from "node:fs";
|
|
17
17
|
import { spawn } from "node:child_process";
|
|
18
18
|
import { resolve } from "node:path";
|
|
19
|
+
const NPM_COMMAND = process.platform === "win32" ? "npm.cmd" : "npm";
|
|
19
20
|
// ─── Package Existence Check ─────────────────────────────────────────────────
|
|
20
21
|
/**
|
|
21
22
|
* Extract npm package names from task descriptions.
|
|
@@ -98,9 +99,10 @@ function normalizePackageName(raw) {
|
|
|
98
99
|
*/
|
|
99
100
|
async function checkPackageOnNpm(packageName, timeoutMs = 5000) {
|
|
100
101
|
return new Promise((resolve) => {
|
|
101
|
-
const child = spawn(
|
|
102
|
+
const child = spawn(NPM_COMMAND, ["view", packageName, "name"], {
|
|
102
103
|
stdio: ["ignore", "pipe", "pipe"],
|
|
103
104
|
timeout: timeoutMs,
|
|
105
|
+
shell: process.platform === "win32",
|
|
104
106
|
});
|
|
105
107
|
let stdout = "";
|
|
106
108
|
let stderr = "";
|
|
@@ -216,9 +218,9 @@ function extractPathFromAnnotation(raw) {
|
|
|
216
218
|
const trimmed = raw.trim();
|
|
217
219
|
if (!trimmed)
|
|
218
220
|
return trimmed;
|
|
219
|
-
const backtickMatch = trimmed.match(
|
|
221
|
+
const backtickMatch = trimmed.match(/^(`+)([^`]+)\1(?:(?:\s+[—–-]\s+.+)|(?:\s+\([^()]+\)))?$/);
|
|
220
222
|
if (backtickMatch) {
|
|
221
|
-
return backtickMatch[
|
|
223
|
+
return backtickMatch[2].trim();
|
|
222
224
|
}
|
|
223
225
|
const annotatedMatch = trimmed.match(/^(.+?)\s+[—–-]\s+.+$/);
|
|
224
226
|
if (annotatedMatch) {
|
|
@@ -49,6 +49,26 @@ This happens ONCE, before the first round. The goal: your first questions should
|
|
|
49
49
|
|
|
50
50
|
For subsequent rounds, continue investigating between rounds — check docs, search, or scout as needed to make each round's questions smarter. But the first-round investigation is mandatory and explicit. Distribute searches across turns rather than clustering them in one turn.
|
|
51
51
|
|
|
52
|
+
## Question Rounds
|
|
53
|
+
|
|
54
|
+
Ask **1–3 questions per round**. Keep each round tightly focused on one or two of the depth checklist dimensions — do not try to cover all six in one round.
|
|
55
|
+
|
|
56
|
+
**If `{{structuredQuestionsAvailable}}` is `true`:** use `ask_user_questions` for each round. 1–3 questions per call, each as a separate question object. Keep option labels short (3–5 words). Always include a freeform "Other / let me explain" option. When the user picks that option or writes a long freeform answer, switch to plain text follow-up for that thread before resuming structured questions. **IMPORTANT: Call `ask_user_questions` exactly once per turn. Never make multiple calls with the same or overlapping questions — wait for the user's response before asking the next round.**
|
|
57
|
+
|
|
58
|
+
**If `{{structuredQuestionsAvailable}}` is `false`:** ask questions in plain text. Keep each round to 1–3 focused questions. Wait for answers before asking the next round.
|
|
59
|
+
|
|
60
|
+
After each answer set, investigate further if any answer opens a new unknown, then ask the next round.
|
|
61
|
+
|
|
62
|
+
### Round cadence
|
|
63
|
+
|
|
64
|
+
After each round of answers, decide whether you already have enough depth to write strong output.
|
|
65
|
+
|
|
66
|
+
- **Incremental persistence:** After every 2 question rounds, silently save a `{{milestoneId}}-CONTEXT-DRAFT.md` using `gsd_summary_save` with `artifact_type: "CONTEXT-DRAFT"` and `milestone_id: "{{milestoneId}}"`. This protects confirmed work against session crashes. Do NOT mention this save to the user.
|
|
67
|
+
- If not ready, continue to the next round immediately. Do **not** ask a meta "ready to wrap up?" question after every round.
|
|
68
|
+
- **Depth-matching rule:** Simple, well-defined work needs fewer rounds — maybe 1–2. Large, ambiguous visions need more — maybe 4+. Do not pad rounds to hit a number. Stop when the Depth Enforcement checklist below is fully satisfied.
|
|
69
|
+
- Do not count the reflection step as a question round. Rounds start after reflection is confirmed.
|
|
70
|
+
- When you genuinely believe the depth checklist is satisfied, move to the Depth Verification step below. Do not ask a separate "ready to wrap up?" gate — the depth verification IS the gate.
|
|
71
|
+
|
|
52
72
|
## Questioning Philosophy
|
|
53
73
|
|
|
54
74
|
You are a thinking partner, not an interviewer.
|
|
@@ -94,29 +114,27 @@ Do NOT offer to proceed until ALL of the following are satisfied. Track these in
|
|
|
94
114
|
|
|
95
115
|
Before offering to proceed, demonstrate absorption: reference specific things the user emphasized, specific terminology they used, specific nuance they sharpened — and show how those shaped your understanding. Synthesize, don't recite. "Your emphasis on X led me to prioritize Y over Z" is good. "You said X, you said Y, you said Z" is not. The user should feel heard in the specifics, not just acknowledged in the abstract.
|
|
96
116
|
|
|
97
|
-
**Questioning depth should match scope.** Simple, well-defined work needs fewer rounds — maybe 1-2. Large, ambiguous visions need more — maybe 4+. Don't pad rounds to hit a number. Stop when the depth checklist is satisfied and you genuinely understand the work.
|
|
98
|
-
|
|
99
|
-
Do not count the reflection step as a question round. Rounds start after reflection is confirmed.
|
|
100
|
-
|
|
101
117
|
## Depth Verification
|
|
102
118
|
|
|
103
119
|
Before moving to the wrap-up gate, present a structured depth summary as a checkpoint.
|
|
104
120
|
|
|
105
121
|
**Print the summary as normal chat text first** — this is where the formatting renders properly. Structure the summary across the depth checklist dimensions using the user's own terminology and framing. Cover: what you understood them to be building, what shaped your understanding most (their emphasis, constraints, concerns), and any areas where you're least confident in your understanding.
|
|
106
122
|
|
|
107
|
-
**Then
|
|
123
|
+
**Then confirm:**
|
|
108
124
|
|
|
109
|
-
**
|
|
125
|
+
**If `{{structuredQuestionsAvailable}}` is `true`:** use `ask_user_questions` with:
|
|
126
|
+
- header: "Depth Check"
|
|
127
|
+
- question: "Did I capture the depth right?"
|
|
128
|
+
- options: "Yes, you got it (Recommended)", "Not quite — let me clarify"
|
|
129
|
+
- **The question ID must contain `depth_verification`** (e.g., `depth_verification_confirm`) — this naming convention enables downstream mechanical detection and the write-gate.
|
|
110
130
|
|
|
111
|
-
|
|
112
|
-
1. Print in chat: the full depth summary with markdown formatting (headers, bold, bullets)
|
|
113
|
-
2. Call `ask_user_questions` with: header "Depth Check", question "Did I capture the depth right?", options "Yes, you got it (Recommended)" and "Not quite — let me clarify"
|
|
131
|
+
**If `{{structuredQuestionsAvailable}}` is `false`:** ask in plain text: "Did I capture that correctly? If not, tell me what I missed." Wait for explicit confirmation before proceeding. **The same non-bypassable gate applies to the plain-text path** — if the user does not respond, gives an ambiguous answer, or does not explicitly confirm, you MUST re-ask. Never rationalize past a missing confirmation.
|
|
114
132
|
|
|
115
133
|
If they clarify, absorb the correction and re-verify.
|
|
116
134
|
|
|
117
135
|
The depth verification is the required write-gate. Do **not** add another meta "ready to proceed?" checkpoint immediately after it unless there is still material ambiguity.
|
|
118
136
|
|
|
119
|
-
**CRITICAL — Non-bypassable gate:** The system mechanically blocks CONTEXT.md writes until the user selects the "(Recommended)" option. If the user declines, cancels, or the tool fails, you MUST re-ask — never rationalize past the block ("tool not responding, I'll proceed" is forbidden). The gate exists to protect the user's work; treat a block as an instruction, not an obstacle to work around.
|
|
137
|
+
**CRITICAL — Non-bypassable gate:** The system mechanically blocks CONTEXT.md writes until the user selects the "(Recommended)" option (structured path) or explicitly confirms (plain-text path). If the user declines, cancels, does not respond, or the tool fails, you MUST re-ask — never rationalize past the block ("tool not responding, I'll proceed" is forbidden). The gate exists to protect the user's work; treat a block as an instruction, not an obstacle to work around.
|
|
120
138
|
|
|
121
139
|
## Wrap-up Gate
|
|
122
140
|
|
|
@@ -244,7 +262,7 @@ If a milestone has no dependencies, omit the frontmatter. The dependency chain f
|
|
|
244
262
|
|
|
245
263
|
#### Phase 3: Sequential readiness gate for remaining milestones
|
|
246
264
|
|
|
247
|
-
For each remaining milestone **one at a time, in sequence**, decide the most likely readiness mode from the evidence you already have, then use `ask_user_questions`
|
|
265
|
+
For each remaining milestone **one at a time, in sequence**, decide the most likely readiness mode from the evidence you already have, then present the three options below to the user. **If `{{structuredQuestionsAvailable}}` is `true`:** use `ask_user_questions`. **If `{{structuredQuestionsAvailable}}` is `false`:** present the options as a plain-text numbered list and ask the user to type their choice. **Non-bypassable:** If the user does not respond, gives an ambiguous answer, or the tool fails, you MUST re-ask — never rationalize past the block or auto-select a readiness mode. Present three options:
|
|
248
266
|
|
|
249
267
|
- **"Discuss now"** — The user wants to conduct a focused discussion for this milestone in the current session, while the context from the broader discussion is still fresh. Proceed with a focused discussion for this milestone (reflection → investigation → questioning → depth verification). When the discussion concludes, write a full `CONTEXT.md`. Then move to the gate for the next milestone.
|
|
250
268
|
- **"Write draft for later"** — This milestone has seed material from the current conversation but needs its own dedicated discussion in a future session. Write a `CONTEXT-DRAFT.md` capturing the seed material (what was discussed, key ideas, provisional scope, open questions). Mark it clearly as a draft, not a finalized context. **What happens downstream:** When auto-mode reaches this milestone, it pauses and notifies the user: "M00x has draft context — needs discussion. Run /gsd." The `/gsd` wizard shows a "Discuss from draft" option that seeds the new discussion with this draft, so nothing from the current conversation is lost. After the dedicated discussion produces a full CONTEXT.md, the draft file is automatically deleted.
|
|
@@ -256,9 +274,9 @@ Before writing each milestone's CONTEXT.md (whether primary or secondary), you M
|
|
|
256
274
|
|
|
257
275
|
1. **Read the actual code** for every file or module you reference. Confirm APIs exist, check what functions actually do, identify phantom capabilities (code that exists but isn't wired up).
|
|
258
276
|
2. **Check for stale assumptions** — the codebase changes. Verify referenced modules still work as described.
|
|
259
|
-
3. **Present findings** — use `ask_user_questions` with a question ID containing BOTH `depth_verification` AND the milestone ID (e.g., `depth_verification_M002`). Present: what you're about to write, key technical findings from investigation, risks the code review surfaced.
|
|
277
|
+
3. **Present findings** — **If `{{structuredQuestionsAvailable}}` is `true`:** use `ask_user_questions` with a question ID containing BOTH `depth_verification` AND the milestone ID (e.g., `depth_verification_M002`). Present: what you're about to write, key technical findings from investigation, risks the code review surfaced. **If `{{structuredQuestionsAvailable}}` is `false`:** present the same findings in plain text and ask for explicit confirmation before proceeding.
|
|
260
278
|
|
|
261
|
-
**The system mechanically blocks CONTEXT.md writes until the per-milestone depth verification passes
|
|
279
|
+
**The system mechanically blocks CONTEXT.md writes until the per-milestone depth verification passes** (structured path: user selects "(Recommended)" option; plain-text path: user explicitly confirms). Each milestone needs its own verification — one global verification does not unlock all milestones.
|
|
262
280
|
|
|
263
281
|
**Why sequential, not batch:** After writing the primary milestone's context and roadmap, the agent still has context window capacity. Asking one milestone at a time lets the user decide per-milestone whether to invest that remaining capacity in a focused discussion now, or defer to a future session. A batch question ("Ready/Draft/Queue for M002, M003, M004?") forces the user to decide everything upfront without knowing how much session capacity remains.
|
|
264
282
|
|