gsd-pi 2.72.0-dev.de4c4b3 → 2.73.0
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 +12 -2
- package/dist/cli.js +59 -3
- package/dist/onboarding.js +10 -0
- package/dist/resources/extensions/async-jobs/await-tool.js +7 -4
- package/dist/resources/extensions/async-jobs/job-manager.js +28 -3
- package/dist/resources/extensions/claude-code-cli/partial-builder.js +40 -12
- package/dist/resources/extensions/claude-code-cli/stream-adapter.js +48 -23
- package/dist/resources/extensions/gsd/auto/loop.js +84 -1
- package/dist/resources/extensions/gsd/auto-post-unit.js +6 -0
- package/dist/resources/extensions/gsd/auto-recovery.js +11 -0
- package/dist/resources/extensions/gsd/auto.js +25 -19
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +9 -11
- package/dist/resources/extensions/gsd/commands-handlers.js +4 -1
- package/dist/resources/extensions/gsd/context-injector.js +1 -1
- package/dist/resources/extensions/gsd/custom-workflow-engine.js +3 -7
- package/dist/resources/extensions/gsd/definition-io.js +15 -0
- package/dist/resources/extensions/gsd/dispatch-guard.js +4 -0
- package/dist/resources/extensions/gsd/doctor-runtime-checks.js +6 -3
- package/dist/resources/extensions/gsd/git-service.js +11 -8
- package/dist/resources/extensions/gsd/gitignore.js +12 -6
- package/dist/resources/extensions/gsd/gsd-db.js +49 -6
- package/dist/resources/extensions/gsd/key-manager.js +2 -0
- package/dist/resources/extensions/gsd/preferences-skills.js +2 -34
- package/dist/resources/extensions/gsd/preferences-types.js +15 -0
- package/dist/resources/extensions/gsd/preferences.js +16 -3
- package/dist/resources/extensions/gsd/prompt-loader.js +4 -1
- package/dist/resources/extensions/gsd/prompts/discuss.md +122 -13
- package/dist/resources/extensions/gsd/prompts/system.md +1 -1
- package/dist/resources/extensions/gsd/state.js +21 -1
- package/dist/resources/extensions/gsd/workflow-projections.js +7 -0
- package/dist/resources/extensions/gsd/worktree-manager.js +30 -3
- package/dist/resources/extensions/gsd/write-intercept.js +10 -1
- package/dist/resources/extensions/ollama/index.js +4 -5
- package/dist/resources/extensions/ollama/ollama-client.js +35 -6
- package/dist/resources/extensions/ollama/ollama-discovery.js +32 -6
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +12 -12
- package/dist/web/standalone/.next/build-manifest.json +3 -3
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/required-server-files.json +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 +2 -2
- 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 +2 -2
- 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 +4 -4
- package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +4 -4
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -4
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +3 -3
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/page.js +2 -2
- package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +12 -12
- package/dist/web/standalone/.next/server/chunks/2331.js +16 -16
- package/dist/web/standalone/.next/server/chunks/4741.js +12 -12
- package/dist/web/standalone/.next/server/chunks/5822.js +2 -2
- package/dist/web/standalone/.next/server/chunks/63.js +8 -8
- package/dist/web/standalone/.next/server/chunks/6897.js +3 -3
- package/dist/web/standalone/.next/server/edge-runtime-webpack.js +2 -0
- package/dist/web/standalone/.next/server/functions-config-manifest.json +0 -9
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware-manifest.json +29 -2
- package/dist/web/standalone/.next/server/middleware.js +4 -12
- 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/server/webpack-runtime.js +1 -1
- package/dist/web/standalone/.next/static/chunks/app/_not-found/{page-f2a7482d42a5614b.js → page-2f24283c162b6ab3.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/app/{layout-a16c7a7ecdf0c2cf.js → layout-9ecfd95f343793f0.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/app/page-7115e62689b5fd84.js +1 -0
- package/dist/web/standalone/.next/static/chunks/main-app-d3d4c336195465f9.js +1 -0
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ab5a8926e07ec673.js +1 -0
- package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
- package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
- package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
- package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api.target.mk +14 -14
- package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_except.target.mk +14 -14
- package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_maybe.target.mk +14 -14
- package/dist/web/standalone/server.js +1 -1
- package/package.json +1 -1
- package/packages/pi-ai/dist/env-api-keys.js +1 -0
- package/packages/pi-ai/dist/env-api-keys.js.map +1 -1
- package/packages/pi-ai/dist/models.custom.d.ts +105 -0
- package/packages/pi-ai/dist/models.custom.d.ts.map +1 -1
- package/packages/pi-ai/dist/models.custom.js +97 -0
- package/packages/pi-ai/dist/models.custom.js.map +1 -1
- package/packages/pi-ai/dist/models.generated.d.ts +648 -140
- package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
- package/packages/pi-ai/dist/models.generated.js +867 -370
- package/packages/pi-ai/dist/models.generated.js.map +1 -1
- package/packages/pi-ai/dist/models.generated.test.d.ts +2 -0
- package/packages/pi-ai/dist/models.generated.test.d.ts.map +1 -0
- package/packages/pi-ai/dist/models.generated.test.js +334 -0
- package/packages/pi-ai/dist/models.generated.test.js.map +1 -0
- package/packages/pi-ai/dist/models.test.js +105 -0
- package/packages/pi-ai/dist/models.test.js.map +1 -1
- package/packages/pi-ai/dist/types.d.ts +1 -1
- package/packages/pi-ai/dist/types.d.ts.map +1 -1
- package/packages/pi-ai/dist/types.js.map +1 -1
- package/packages/pi-ai/dist/utils/oauth/github-copilot.d.ts.map +1 -1
- package/packages/pi-ai/dist/utils/oauth/github-copilot.js +5 -1
- package/packages/pi-ai/dist/utils/oauth/github-copilot.js.map +1 -1
- package/packages/pi-ai/dist/utils/oauth/github-copilot.test.d.ts +2 -0
- package/packages/pi-ai/dist/utils/oauth/github-copilot.test.d.ts.map +1 -0
- package/packages/pi-ai/dist/utils/oauth/github-copilot.test.js +57 -0
- package/packages/pi-ai/dist/utils/oauth/github-copilot.test.js.map +1 -0
- package/packages/pi-ai/src/env-api-keys.ts +1 -0
- package/packages/pi-ai/src/models.custom.ts +98 -0
- package/packages/pi-ai/src/models.generated.test.ts +373 -0
- package/packages/pi-ai/src/models.generated.ts +867 -370
- package/packages/pi-ai/src/models.test.ts +135 -0
- package/packages/pi-ai/src/types.ts +1 -0
- package/packages/pi-ai/src/utils/oauth/github-copilot.test.ts +71 -0
- package/packages/pi-ai/src/utils/oauth/github-copilot.ts +4 -1
- package/packages/pi-coding-agent/dist/core/model-resolver.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-resolver.js +1 -0
- package/packages/pi-coding-agent/dist/core/model-resolver.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 +9 -0
- package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js +36 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +87 -12
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/packages/pi-coding-agent/package.json +1 -1
- package/packages/pi-coding-agent/src/core/model-resolver.ts +1 -0
- package/packages/pi-coding-agent/src/core/sdk.ts +10 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/tool-execution.test.ts +72 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +84 -12
- package/packages/pi-tui/dist/components/__tests__/editor.test.js +12 -0
- package/packages/pi-tui/dist/components/__tests__/editor.test.js.map +1 -1
- package/packages/pi-tui/dist/components/__tests__/input.test.js +12 -0
- package/packages/pi-tui/dist/components/__tests__/input.test.js.map +1 -1
- package/packages/pi-tui/dist/keys.d.ts.map +1 -1
- package/packages/pi-tui/dist/keys.js +27 -0
- package/packages/pi-tui/dist/keys.js.map +1 -1
- package/packages/pi-tui/src/components/__tests__/editor.test.ts +18 -0
- package/packages/pi-tui/src/components/__tests__/input.test.ts +18 -0
- package/packages/pi-tui/src/keys.ts +32 -0
- package/pkg/package.json +1 -1
- package/src/resources/extensions/async-jobs/await-tool.test.ts +40 -7
- package/src/resources/extensions/async-jobs/await-tool.ts +7 -4
- package/src/resources/extensions/async-jobs/job-manager.ts +33 -3
- package/src/resources/extensions/claude-code-cli/partial-builder.ts +45 -12
- package/src/resources/extensions/claude-code-cli/stream-adapter.ts +49 -24
- package/src/resources/extensions/claude-code-cli/tests/partial-builder.test.ts +91 -2
- package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +112 -0
- package/src/resources/extensions/gsd/auto/loop.ts +89 -1
- package/src/resources/extensions/gsd/auto-post-unit.ts +7 -0
- package/src/resources/extensions/gsd/auto-recovery.ts +10 -0
- package/src/resources/extensions/gsd/auto.ts +25 -20
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +8 -10
- package/src/resources/extensions/gsd/commands-handlers.ts +5 -1
- package/src/resources/extensions/gsd/context-injector.ts +1 -1
- package/src/resources/extensions/gsd/custom-workflow-engine.ts +4 -8
- package/src/resources/extensions/gsd/definition-io.ts +18 -0
- package/src/resources/extensions/gsd/dispatch-guard.ts +5 -0
- package/src/resources/extensions/gsd/doctor-runtime-checks.ts +6 -3
- package/src/resources/extensions/gsd/git-service.ts +11 -8
- package/src/resources/extensions/gsd/gitignore.ts +12 -6
- package/src/resources/extensions/gsd/gsd-db.ts +54 -6
- package/src/resources/extensions/gsd/key-manager.ts +2 -0
- package/src/resources/extensions/gsd/preferences-skills.ts +2 -36
- package/src/resources/extensions/gsd/preferences-types.ts +16 -0
- package/src/resources/extensions/gsd/preferences.ts +19 -6
- package/src/resources/extensions/gsd/prompt-loader.ts +6 -1
- package/src/resources/extensions/gsd/prompts/discuss.md +122 -13
- package/src/resources/extensions/gsd/prompts/system.md +1 -1
- package/src/resources/extensions/gsd/state.ts +20 -0
- package/src/resources/extensions/gsd/tests/auto-paused-ui-cleanup.test.ts +27 -0
- package/src/resources/extensions/gsd/tests/block-db-writes.test.ts +63 -0
- package/src/resources/extensions/gsd/tests/definition-io.test.ts +57 -0
- package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +26 -0
- package/src/resources/extensions/gsd/tests/doctor-heal-fixable-warnings.test.ts +14 -0
- package/src/resources/extensions/gsd/tests/false-degraded-mode-warning.test.ts +104 -0
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +107 -5
- package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +8 -6
- package/src/resources/extensions/gsd/tests/key-manager.test.ts +63 -0
- package/src/resources/extensions/gsd/tests/memory-pressure-stuck-state.test.ts +54 -0
- package/src/resources/extensions/gsd/tests/plan-milestone-artifact-verification.test.ts +62 -0
- package/src/resources/extensions/gsd/tests/post-unit-state-rebuild.test.ts +34 -0
- package/src/resources/extensions/gsd/tests/preferences-formatting.test.ts +87 -0
- package/src/resources/extensions/gsd/tests/preferences.test.ts +53 -0
- package/src/resources/extensions/gsd/tests/projection-regression.test.ts +96 -1
- package/src/resources/extensions/gsd/tests/prompt-loader-working-directory.test.ts +19 -0
- package/src/resources/extensions/gsd/tests/register-hooks-depth-verification.test.ts +97 -0
- package/src/resources/extensions/gsd/tests/stale-slice-rows.test.ts +41 -0
- package/src/resources/extensions/gsd/workflow-projections.ts +8 -0
- package/src/resources/extensions/gsd/worktree-manager.ts +29 -3
- package/src/resources/extensions/gsd/write-intercept.ts +10 -1
- package/src/resources/extensions/ollama/index.ts +4 -5
- package/src/resources/extensions/ollama/ollama-client.ts +35 -6
- package/src/resources/extensions/ollama/ollama-discovery.ts +37 -6
- package/src/resources/extensions/ollama/tests/ollama-discovery.test.ts +54 -0
- package/dist/resources/extensions/gsd/auto-observability.js +0 -54
- package/dist/resources/extensions/gsd/file-watcher.js +0 -80
- package/dist/resources/extensions/gsd/rtk-status.js +0 -43
- package/dist/web/standalone/.next/static/chunks/app/page-f1e30ab6bb269149.js +0 -1
- package/dist/web/standalone/.next/static/chunks/main-app-fdab67f7802d7832.js +0 -1
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +0 -1
- package/src/resources/extensions/gsd/auto-observability.ts +0 -72
- package/src/resources/extensions/gsd/file-watcher.ts +0 -100
- package/src/resources/extensions/gsd/rtk-status.ts +0 -53
- /package/dist/web/standalone/.next/static/{f-Gremw0nLxxFUySaHRPw → KSZ2dcC3p4z6lOmUpPpzr}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{f-Gremw0nLxxFUySaHRPw → KSZ2dcC3p4z6lOmUpPpzr}/_ssgManifest.js +0 -0
|
@@ -172,14 +172,10 @@ export function registerHooks(pi) {
|
|
|
172
172
|
// Only gate-shaped ask_user_questions calls should block execution.
|
|
173
173
|
// The gate stays pending until the user selects the approval option.
|
|
174
174
|
if (event.toolName === "ask_user_questions") {
|
|
175
|
-
const
|
|
176
|
-
const
|
|
177
|
-
if (
|
|
178
|
-
|
|
179
|
-
const questionId = questions.find((question) => typeof question?.id === "string" && isGateQuestionId(question.id))?.id;
|
|
180
|
-
if (typeof questionId === "string") {
|
|
181
|
-
setPendingGate(questionId);
|
|
182
|
-
}
|
|
175
|
+
const questions = event.input?.questions ?? [];
|
|
176
|
+
const questionId = questions.find((question) => typeof question?.id === "string" && isGateQuestionId(question.id))?.id;
|
|
177
|
+
if (typeof questionId === "string") {
|
|
178
|
+
setPendingGate(questionId);
|
|
183
179
|
}
|
|
184
180
|
}
|
|
185
181
|
// ── Discussion gate enforcement: block tool calls while gate is pending ──
|
|
@@ -261,8 +257,6 @@ export function registerHooks(pi) {
|
|
|
261
257
|
return;
|
|
262
258
|
const milestoneId = getDiscussionMilestoneId(process.cwd());
|
|
263
259
|
const queueActive = isQueuePhaseActive();
|
|
264
|
-
if (!milestoneId && !queueActive)
|
|
265
|
-
return;
|
|
266
260
|
const details = event.details;
|
|
267
261
|
// ── Discussion gate enforcement: handle gate question responses ──
|
|
268
262
|
// If the result is cancelled or has no response, the pending gate stays active
|
|
@@ -293,12 +287,16 @@ export function registerHooks(pi) {
|
|
|
293
287
|
// Only unlock the gate if the user selected the first option (confirmation).
|
|
294
288
|
// Cross-references against the question's defined options to reject free-form "Other" text.
|
|
295
289
|
const answer = details.response?.answers?.[question.id];
|
|
290
|
+
const inferredMilestoneId = extractDepthVerificationMilestoneId(question.id) ?? milestoneId;
|
|
296
291
|
if (isDepthConfirmationAnswer(answer?.selected, question.options)) {
|
|
297
|
-
markDepthVerified(
|
|
292
|
+
markDepthVerified(inferredMilestoneId);
|
|
293
|
+
clearPendingGate();
|
|
298
294
|
}
|
|
299
295
|
break;
|
|
300
296
|
}
|
|
301
297
|
}
|
|
298
|
+
if (!milestoneId && !queueActive)
|
|
299
|
+
return;
|
|
302
300
|
if (!milestoneId)
|
|
303
301
|
return;
|
|
304
302
|
const basePath = process.cwd();
|
|
@@ -61,6 +61,9 @@ export function parseDoctorArgs(args) {
|
|
|
61
61
|
const requestedScope = mode === "doctor" ? parts[0] : parts[1];
|
|
62
62
|
return { jsonMode, dryRun, fixFlag, includeBuild, includeTests, mode, requestedScope };
|
|
63
63
|
}
|
|
64
|
+
export function isDoctorHealActionable(issue) {
|
|
65
|
+
return issue.fixable && issue.severity !== "info";
|
|
66
|
+
}
|
|
64
67
|
export async function handleDoctor(args, ctx, pi) {
|
|
65
68
|
const { jsonMode, dryRun, fixFlag, includeBuild, includeTests, mode, requestedScope } = parseDoctorArgs(args);
|
|
66
69
|
const scope = await selectDoctorScope(projectRoot(), requestedScope);
|
|
@@ -88,7 +91,7 @@ export async function handleDoctor(args, ctx, pi) {
|
|
|
88
91
|
scope: effectiveScope,
|
|
89
92
|
includeWarnings: true,
|
|
90
93
|
});
|
|
91
|
-
const actionable = unresolved.filter(
|
|
94
|
+
const actionable = unresolved.filter(isDoctorHealActionable);
|
|
92
95
|
if (actionable.length === 0) {
|
|
93
96
|
ctx.ui.notify("Doctor heal found nothing actionable to hand off to the LLM.", "info");
|
|
94
97
|
return;
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
*/
|
|
15
15
|
import { readFileSync, existsSync } from "node:fs";
|
|
16
16
|
import { resolve, sep } from "node:path";
|
|
17
|
-
import { readFrozenDefinition } from "./
|
|
17
|
+
import { readFrozenDefinition } from "./definition-io.js";
|
|
18
18
|
/** Maximum characters per artifact to prevent context window blowout. */
|
|
19
19
|
const MAX_CONTEXT_CHARS = 10_000;
|
|
20
20
|
/**
|
|
@@ -13,17 +13,13 @@
|
|
|
13
13
|
*/
|
|
14
14
|
import { readFileSync } from "node:fs";
|
|
15
15
|
import { join } from "node:path";
|
|
16
|
-
import { parse } from "yaml";
|
|
17
16
|
import { readGraph, writeGraph, getNextPendingStep, markStepComplete, expandIteration, } from "./graph.js";
|
|
18
17
|
import { injectContext } from "./context-injector.js";
|
|
18
|
+
import { readFrozenDefinition } from "./definition-io.js";
|
|
19
19
|
import { parseUnitId } from "./unit-id.js";
|
|
20
20
|
import { withFileLock } from "./file-lock.js";
|
|
21
|
-
|
|
22
|
-
export
|
|
23
|
-
const defPath = join(runDir, "DEFINITION.yaml");
|
|
24
|
-
const raw = readFileSync(defPath, "utf-8");
|
|
25
|
-
return parse(raw, { schema: "core" });
|
|
26
|
-
}
|
|
21
|
+
// Re-export for downstream consumers
|
|
22
|
+
export { readFrozenDefinition } from "./definition-io.js";
|
|
27
23
|
export class CustomWorkflowEngine {
|
|
28
24
|
engineId = "custom";
|
|
29
25
|
runDir;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* definition-io.ts — Read frozen DEFINITION.yaml from a run directory.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from custom-workflow-engine.ts to break the circular dependency
|
|
5
|
+
* between context-injector.ts and custom-workflow-engine.ts.
|
|
6
|
+
*/
|
|
7
|
+
import { readFileSync } from "node:fs";
|
|
8
|
+
import { join } from "node:path";
|
|
9
|
+
import { parse } from "yaml";
|
|
10
|
+
/** Read and parse the frozen DEFINITION.yaml from a run directory. */
|
|
11
|
+
export function readFrozenDefinition(runDir) {
|
|
12
|
+
const defPath = join(runDir, "DEFINITION.yaml");
|
|
13
|
+
const raw = readFileSync(defPath, "utf-8");
|
|
14
|
+
return parse(raw, { schema: "core" });
|
|
15
|
+
}
|
|
@@ -102,6 +102,10 @@ export function getPriorSliceCompletionBlocker(base, _mainBranch, unitType, unit
|
|
|
102
102
|
}
|
|
103
103
|
}
|
|
104
104
|
else {
|
|
105
|
+
const milestoneUsesExplicitDeps = slices.some((slice) => slice.depends.length > 0);
|
|
106
|
+
if (milestoneUsesExplicitDeps) {
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
105
109
|
// Positional fallback is only a heuristic for legacy slices with no
|
|
106
110
|
// declared dependencies. Skip any earlier slice that depends on the
|
|
107
111
|
// target, directly or transitively, or we can deadlock a valid zero-dep
|
|
@@ -277,13 +277,16 @@ export async function checkRuntimeHealth(basePath, issues, fixesApplied, shouldF
|
|
|
277
277
|
if (existsSync(gitignorePath) && nativeIsRepo(basePath)) {
|
|
278
278
|
const content = readFileSync(gitignorePath, "utf-8");
|
|
279
279
|
const existingLines = new Set(content.split("\n").map(l => l.trim()).filter(l => l && !l.startsWith("#")));
|
|
280
|
-
// Check for critical runtime patterns that must be present
|
|
280
|
+
// Check for critical runtime patterns that must be present.
|
|
281
|
+
// NOTE: GSD_RUNTIME_PATTERNS in gitignore.ts is the canonical source of truth.
|
|
282
|
+
// This is a minimal subset for the doctor check.
|
|
281
283
|
const criticalPatterns = [
|
|
282
284
|
".gsd/activity/",
|
|
283
285
|
".gsd/runtime/",
|
|
284
286
|
".gsd/auto.lock",
|
|
285
|
-
".gsd/gsd.db",
|
|
286
|
-
".gsd/completed-units
|
|
287
|
+
".gsd/gsd.db*",
|
|
288
|
+
".gsd/completed-units*.json",
|
|
289
|
+
".gsd/event-log.jsonl",
|
|
287
290
|
];
|
|
288
291
|
// If blanket .gsd/ or .gsd is present, all patterns are covered
|
|
289
292
|
const hasBlanketIgnore = existingLines.has(".gsd/") || existingLines.has(".gsd");
|
|
@@ -78,22 +78,25 @@ export class MergeConflictError extends GSDError {
|
|
|
78
78
|
/**
|
|
79
79
|
* GSD runtime paths that should be excluded from smart staging.
|
|
80
80
|
* These are transient/generated artifacts that should never be committed.
|
|
81
|
-
*
|
|
82
|
-
*
|
|
81
|
+
*
|
|
82
|
+
* NOTE: GSD_RUNTIME_PATTERNS in gitignore.ts is the canonical source of truth.
|
|
83
|
+
* This array must stay synchronized with it.
|
|
83
84
|
*/
|
|
84
85
|
export const RUNTIME_EXCLUSION_PATHS = [
|
|
85
86
|
".gsd/activity/",
|
|
87
|
+
".gsd/forensics/",
|
|
86
88
|
".gsd/runtime/",
|
|
87
89
|
".gsd/worktrees/",
|
|
90
|
+
".gsd/parallel/",
|
|
88
91
|
".gsd/auto.lock",
|
|
89
92
|
".gsd/metrics.json",
|
|
90
|
-
".gsd/completed-units
|
|
93
|
+
".gsd/completed-units*.json", // covers completed-units.json and archived completed-units-{MID}.json
|
|
94
|
+
".gsd/state-manifest.json",
|
|
91
95
|
".gsd/STATE.md",
|
|
92
|
-
".gsd/gsd.db",
|
|
93
|
-
".gsd/
|
|
94
|
-
".gsd/
|
|
95
|
-
".gsd/
|
|
96
|
-
".gsd/doctor-history.jsonl", // doctor run history (#2296)
|
|
96
|
+
".gsd/gsd.db*",
|
|
97
|
+
".gsd/journal/",
|
|
98
|
+
".gsd/doctor-history.jsonl",
|
|
99
|
+
".gsd/event-log.jsonl",
|
|
97
100
|
".gsd/DISCUSSION-MANIFEST.json",
|
|
98
101
|
];
|
|
99
102
|
// ─── Integration Branch Metadata ───────────────────────────────────────────
|
|
@@ -13,6 +13,12 @@ import { gsdRoot } from "./paths.js";
|
|
|
13
13
|
import { GIT_NO_PROMPT_ENV } from "./git-constants.js";
|
|
14
14
|
/**
|
|
15
15
|
* GSD runtime patterns for git index cleanup.
|
|
16
|
+
*
|
|
17
|
+
* CANONICAL SOURCE OF TRUTH: This array is the authoritative list of runtime
|
|
18
|
+
* ignore patterns. Other modules (RUNTIME_EXCLUSION_PATHS in git-service.ts,
|
|
19
|
+
* SKIP_* arrays in worktree-manager.ts, criticalPatterns in doctor-runtime-checks.ts)
|
|
20
|
+
* must stay synchronized with this list.
|
|
21
|
+
*
|
|
16
22
|
* With external state (symlink), these are a no-op in most cases,
|
|
17
23
|
* but retained for backwards compatibility during migration.
|
|
18
24
|
*/
|
|
@@ -24,13 +30,13 @@ const GSD_RUNTIME_PATTERNS = [
|
|
|
24
30
|
".gsd/parallel/",
|
|
25
31
|
".gsd/auto.lock",
|
|
26
32
|
".gsd/metrics.json",
|
|
27
|
-
".gsd/completed-units
|
|
33
|
+
".gsd/completed-units*.json", // covers completed-units.json and archived completed-units-{MID}.json
|
|
34
|
+
".gsd/state-manifest.json",
|
|
28
35
|
".gsd/STATE.md",
|
|
29
|
-
".gsd/gsd.db",
|
|
30
|
-
".gsd/
|
|
31
|
-
".gsd/
|
|
32
|
-
".gsd/
|
|
33
|
-
".gsd/doctor-history.jsonl", // doctor run history (#2296)
|
|
36
|
+
".gsd/gsd.db*",
|
|
37
|
+
".gsd/journal/",
|
|
38
|
+
".gsd/doctor-history.jsonl",
|
|
39
|
+
".gsd/event-log.jsonl",
|
|
34
40
|
".gsd/DISCUSSION-MANIFEST.json",
|
|
35
41
|
".gsd/milestones/**/*-CONTINUE.md",
|
|
36
42
|
".gsd/milestones/**/continue.md",
|
|
@@ -121,6 +121,25 @@ function openRawDb(path) {
|
|
|
121
121
|
return new Database(path);
|
|
122
122
|
}
|
|
123
123
|
const SCHEMA_VERSION = 14;
|
|
124
|
+
function indexExists(db, name) {
|
|
125
|
+
return !!db.prepare("SELECT 1 as present FROM sqlite_master WHERE type = 'index' AND name = ?").get(name);
|
|
126
|
+
}
|
|
127
|
+
function dedupeVerificationEvidenceRows(db) {
|
|
128
|
+
db.exec(`
|
|
129
|
+
DELETE FROM verification_evidence
|
|
130
|
+
WHERE rowid NOT IN (
|
|
131
|
+
SELECT MIN(rowid)
|
|
132
|
+
FROM verification_evidence
|
|
133
|
+
GROUP BY task_id, slice_id, milestone_id, command, verdict
|
|
134
|
+
)
|
|
135
|
+
`);
|
|
136
|
+
}
|
|
137
|
+
function ensureVerificationEvidenceDedupIndex(db) {
|
|
138
|
+
if (indexExists(db, "idx_verification_evidence_dedup"))
|
|
139
|
+
return;
|
|
140
|
+
dedupeVerificationEvidenceRows(db);
|
|
141
|
+
db.exec("CREATE UNIQUE INDEX IF NOT EXISTS idx_verification_evidence_dedup ON verification_evidence(task_id, slice_id, milestone_id, command, verdict)");
|
|
142
|
+
}
|
|
124
143
|
function initSchema(db, fileBacked) {
|
|
125
144
|
if (fileBacked)
|
|
126
145
|
db.exec("PRAGMA journal_mode=WAL");
|
|
@@ -132,7 +151,7 @@ function initSchema(db, fileBacked) {
|
|
|
132
151
|
db.exec("PRAGMA auto_vacuum = INCREMENTAL");
|
|
133
152
|
if (fileBacked)
|
|
134
153
|
db.exec("PRAGMA cache_size = -8000"); // 8 MB page cache
|
|
135
|
-
if (fileBacked)
|
|
154
|
+
if (fileBacked && process.platform !== "darwin")
|
|
136
155
|
db.exec("PRAGMA mmap_size = 67108864"); // 64 MB mmap
|
|
137
156
|
db.exec("PRAGMA temp_store = MEMORY");
|
|
138
157
|
db.exec("PRAGMA foreign_keys = ON");
|
|
@@ -358,7 +377,7 @@ function initSchema(db, fileBacked) {
|
|
|
358
377
|
db.exec("CREATE INDEX IF NOT EXISTS idx_milestones_status ON milestones(status)");
|
|
359
378
|
db.exec("CREATE INDEX IF NOT EXISTS idx_quality_gates_pending ON quality_gates(milestone_id, slice_id, status)");
|
|
360
379
|
db.exec("CREATE INDEX IF NOT EXISTS idx_verification_evidence_task ON verification_evidence(milestone_id, slice_id, task_id)");
|
|
361
|
-
db
|
|
380
|
+
ensureVerificationEvidenceDedupIndex(db);
|
|
362
381
|
// v14 index — slice dependency lookups
|
|
363
382
|
db.exec("CREATE INDEX IF NOT EXISTS idx_slice_deps_target ON slice_dependencies(milestone_id, depends_on_slice_id)");
|
|
364
383
|
db.exec(`CREATE VIEW IF NOT EXISTS active_decisions AS SELECT * FROM decisions WHERE superseded_by IS NULL`);
|
|
@@ -668,7 +687,7 @@ function migrateSchema(db) {
|
|
|
668
687
|
db.exec("CREATE INDEX IF NOT EXISTS idx_milestones_status ON milestones(status)");
|
|
669
688
|
db.exec("CREATE INDEX IF NOT EXISTS idx_quality_gates_pending ON quality_gates(milestone_id, slice_id, status)");
|
|
670
689
|
db.exec("CREATE INDEX IF NOT EXISTS idx_verification_evidence_task ON verification_evidence(milestone_id, slice_id, task_id)");
|
|
671
|
-
db
|
|
690
|
+
ensureVerificationEvidenceDedupIndex(db);
|
|
672
691
|
db.prepare("INSERT INTO schema_version (version, applied_at) VALUES (:version, :applied_at)").run({
|
|
673
692
|
":version": 13,
|
|
674
693
|
":applied_at": new Date().toISOString(),
|
|
@@ -800,6 +819,7 @@ export function closeDatabase() {
|
|
|
800
819
|
currentDb = null;
|
|
801
820
|
currentPath = null;
|
|
802
821
|
currentPid = 0;
|
|
822
|
+
_dbOpenAttempted = false;
|
|
803
823
|
}
|
|
804
824
|
}
|
|
805
825
|
/** Run a full VACUUM — call sparingly (e.g. after milestone completion). */
|
|
@@ -1333,6 +1353,29 @@ export function setSliceSummaryMd(milestoneId, sliceId, summaryMd, uatMd) {
|
|
|
1333
1353
|
currentDb.prepare(`UPDATE slices SET full_summary_md = :summary_md, full_uat_md = :uat_md WHERE milestone_id = :mid AND id = :sid`).run({ ":mid": milestoneId, ":sid": sliceId, ":summary_md": summaryMd, ":uat_md": uatMd });
|
|
1334
1354
|
}
|
|
1335
1355
|
function rowToTask(row) {
|
|
1356
|
+
const parseTaskArray = (value) => {
|
|
1357
|
+
if (Array.isArray(value)) {
|
|
1358
|
+
return value.filter((entry) => typeof entry === "string");
|
|
1359
|
+
}
|
|
1360
|
+
if (typeof value !== "string")
|
|
1361
|
+
return [];
|
|
1362
|
+
const trimmed = value.trim();
|
|
1363
|
+
if (!trimmed)
|
|
1364
|
+
return [];
|
|
1365
|
+
try {
|
|
1366
|
+
const parsed = JSON.parse(trimmed);
|
|
1367
|
+
if (Array.isArray(parsed)) {
|
|
1368
|
+
return parsed.filter((entry) => typeof entry === "string");
|
|
1369
|
+
}
|
|
1370
|
+
if (typeof parsed === "string" && parsed.trim()) {
|
|
1371
|
+
return [parsed.trim()];
|
|
1372
|
+
}
|
|
1373
|
+
}
|
|
1374
|
+
catch {
|
|
1375
|
+
// Older/corrupt DB rows may contain raw comma-separated paths instead of JSON arrays.
|
|
1376
|
+
}
|
|
1377
|
+
return trimmed.split(",").map((entry) => entry.trim()).filter(Boolean);
|
|
1378
|
+
};
|
|
1336
1379
|
return {
|
|
1337
1380
|
milestone_id: row["milestone_id"],
|
|
1338
1381
|
slice_id: row["slice_id"],
|
|
@@ -1352,10 +1395,10 @@ function rowToTask(row) {
|
|
|
1352
1395
|
full_summary_md: row["full_summary_md"],
|
|
1353
1396
|
description: row["description"] ?? "",
|
|
1354
1397
|
estimate: row["estimate"] ?? "",
|
|
1355
|
-
files:
|
|
1398
|
+
files: parseTaskArray(row["files"]),
|
|
1356
1399
|
verify: row["verify"] ?? "",
|
|
1357
|
-
inputs:
|
|
1358
|
-
expected_output:
|
|
1400
|
+
inputs: parseTaskArray(row["inputs"]),
|
|
1401
|
+
expected_output: parseTaskArray(row["expected_output"]),
|
|
1359
1402
|
observability_impact: row["observability_impact"] ?? "",
|
|
1360
1403
|
full_plan_md: row["full_plan_md"] ?? "",
|
|
1361
1404
|
sequence: row["sequence"] ?? 0,
|
|
@@ -26,6 +26,8 @@ export const PROVIDER_REGISTRY = [
|
|
|
26
26
|
{ id: "custom-openai", label: "Custom (OpenAI-compat)", category: "llm", envVar: "CUSTOM_OPENAI_API_KEY" },
|
|
27
27
|
{ id: "cerebras", label: "Cerebras", category: "llm", envVar: "CEREBRAS_API_KEY" },
|
|
28
28
|
{ id: "azure-openai-responses", label: "Azure OpenAI", category: "llm", envVar: "AZURE_OPENAI_API_KEY" },
|
|
29
|
+
{ id: "alibaba-coding-plan", label: "Alibaba Coding Plan", category: "llm", envVar: "ALIBABA_API_KEY", dashboardUrl: "bailian.console.aliyun.com" },
|
|
30
|
+
{ id: "alibaba-dashscope", label: "Alibaba DashScope", category: "llm", envVar: "DASHSCOPE_API_KEY", dashboardUrl: "dashscope.console.aliyun.com" },
|
|
29
31
|
// Tool Keys
|
|
30
32
|
{ id: "context7", label: "Context7 Docs", category: "tool", envVar: "CONTEXT7_API_KEY", dashboardUrl: "context7.com/dashboard" },
|
|
31
33
|
{ id: "jina", label: "Jina Page Extract", category: "tool", envVar: "JINA_API_KEY", dashboardUrl: "jina.ai/api" },
|
|
@@ -9,7 +9,6 @@ import { homedir } from "node:os";
|
|
|
9
9
|
import { isAbsolute, join } from "node:path";
|
|
10
10
|
import { statSync } from "node:fs";
|
|
11
11
|
import { validatePreferences } from "./preferences-validation.js";
|
|
12
|
-
import { loadEffectiveGSDPreferences } from "./preferences.js";
|
|
13
12
|
/**
|
|
14
13
|
* Known skill directories, in priority order.
|
|
15
14
|
* Searches both the skills.sh ecosystem directory (~/.agents/skills/) and
|
|
@@ -130,36 +129,5 @@ export function resolveAllSkillReferences(preferences, cwd) {
|
|
|
130
129
|
}
|
|
131
130
|
return { resolutions, warnings };
|
|
132
131
|
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
* If resolved, shows the path so the agent knows exactly where to read.
|
|
136
|
-
* If unresolved, marks it clearly.
|
|
137
|
-
*/
|
|
138
|
-
export function formatSkillRef(ref, resolutions) {
|
|
139
|
-
const resolution = resolutions.get(ref);
|
|
140
|
-
if (!resolution || resolution.method === "unresolved") {
|
|
141
|
-
return `${ref} (⚠ not found — check skill name or path)`;
|
|
142
|
-
}
|
|
143
|
-
// For absolute paths where SKILL.md is just appended, don't clutter the output
|
|
144
|
-
if (resolution.method === "absolute-path" || resolution.method === "absolute-dir") {
|
|
145
|
-
return ref;
|
|
146
|
-
}
|
|
147
|
-
// For bare names resolved from skill directories, show the resolved path
|
|
148
|
-
return `${ref} → \`${resolution.resolvedPath}\``;
|
|
149
|
-
}
|
|
150
|
-
/**
|
|
151
|
-
* Resolve the skill discovery mode from effective preferences.
|
|
152
|
-
* Defaults to "suggest" -- skills are identified during research but not installed automatically.
|
|
153
|
-
*/
|
|
154
|
-
export function resolveSkillDiscoveryMode() {
|
|
155
|
-
const prefs = loadEffectiveGSDPreferences();
|
|
156
|
-
return prefs?.preferences.skill_discovery ?? "suggest";
|
|
157
|
-
}
|
|
158
|
-
/**
|
|
159
|
-
* Resolve the skill staleness threshold in days.
|
|
160
|
-
* Returns 0 if disabled, default 60 if not configured.
|
|
161
|
-
*/
|
|
162
|
-
export function resolveSkillStalenessDays() {
|
|
163
|
-
const prefs = loadEffectiveGSDPreferences();
|
|
164
|
-
return prefs?.preferences.skill_staleness_days ?? 60;
|
|
165
|
-
}
|
|
132
|
+
// resolveSkillDiscoveryMode and resolveSkillStalenessDays moved to
|
|
133
|
+
// preferences.ts to break circular dependency (they need loadEffectiveGSDPreferences).
|
|
@@ -92,3 +92,18 @@ export const KNOWN_UNIT_TYPES = [
|
|
|
92
92
|
"discuss-milestone", "discuss-slice", "worktree-merge",
|
|
93
93
|
];
|
|
94
94
|
export const SKILL_ACTIONS = new Set(["use", "prefer", "avoid"]);
|
|
95
|
+
/**
|
|
96
|
+
* Format a skill reference for the system prompt.
|
|
97
|
+
* If resolved, shows the path so the agent knows exactly where to read.
|
|
98
|
+
* If unresolved, marks it clearly.
|
|
99
|
+
*/
|
|
100
|
+
export function formatSkillRef(ref, resolutions) {
|
|
101
|
+
const resolution = resolutions.get(ref);
|
|
102
|
+
if (!resolution || resolution.method === "unresolved") {
|
|
103
|
+
return `${ref} (⚠ not found — check skill name or path)`;
|
|
104
|
+
}
|
|
105
|
+
if (resolution.method === "absolute-path" || resolution.method === "absolute-dir") {
|
|
106
|
+
return ref;
|
|
107
|
+
}
|
|
108
|
+
return `${ref} → \`${resolution.resolvedPath}\``;
|
|
109
|
+
}
|
|
@@ -17,13 +17,23 @@ import { parse as parseYaml } from "yaml";
|
|
|
17
17
|
import { normalizeStringArray } from "../shared/format-utils.js";
|
|
18
18
|
import { logWarning } from "./workflow-logger.js";
|
|
19
19
|
import { resolveProfileDefaults as _resolveProfileDefaults } from "./preferences-models.js";
|
|
20
|
-
import { KNOWN_PREFERENCE_KEYS, MODE_DEFAULTS, } from "./preferences-types.js";
|
|
20
|
+
import { KNOWN_PREFERENCE_KEYS, MODE_DEFAULTS, formatSkillRef, } from "./preferences-types.js";
|
|
21
21
|
import { validatePreferences } from "./preferences-validation.js";
|
|
22
|
-
import { formatSkillRef } from "./preferences-skills.js";
|
|
23
22
|
// ─── Re-exports: validation ─────────────────────────────────────────────────
|
|
24
23
|
export { validatePreferences } from "./preferences-validation.js";
|
|
25
24
|
// ─── Re-exports: skills ─────────────────────────────────────────────────────
|
|
26
|
-
export { resolveAllSkillReferences
|
|
25
|
+
export { resolveAllSkillReferences } from "./preferences-skills.js";
|
|
26
|
+
// These lived in preferences-skills.ts but imported loadEffectiveGSDPreferences
|
|
27
|
+
// back from this file, creating a circular dependency. Moved here since they
|
|
28
|
+
// are trivial wrappers over loadEffectiveGSDPreferences.
|
|
29
|
+
export function resolveSkillDiscoveryMode() {
|
|
30
|
+
const prefs = loadEffectiveGSDPreferences();
|
|
31
|
+
return prefs?.preferences.skill_discovery ?? "suggest";
|
|
32
|
+
}
|
|
33
|
+
export function resolveSkillStalenessDays() {
|
|
34
|
+
const prefs = loadEffectiveGSDPreferences();
|
|
35
|
+
return prefs?.preferences.skill_staleness_days ?? 60;
|
|
36
|
+
}
|
|
27
37
|
// ─── Re-exports: models ─────────────────────────────────────────────────────
|
|
28
38
|
export { resolveModelForUnit, resolveModelWithFallbacksForUnit, getNextFallbackModel, isTransientNetworkError, validateModelId, updatePreferencesModels, resolveDynamicRoutingConfig, resolveAutoSupervisorConfig, resolveProfileDefaults, resolveEffectiveProfile, resolveInlineLevel, resolveContextSelection, resolveSearchProviderFromPreferences, } from "./preferences-models.js";
|
|
29
39
|
// ─── Path Constants & Getters ───────────────────────────────────────────────
|
|
@@ -304,6 +314,9 @@ function mergePreferences(base, override) {
|
|
|
304
314
|
github: (base.github || override.github)
|
|
305
315
|
? { ...(base.github ?? {}), ...(override.github ?? {}) }
|
|
306
316
|
: undefined,
|
|
317
|
+
experimental: (base.experimental || override.experimental)
|
|
318
|
+
? { ...(base.experimental ?? {}), ...(override.experimental ?? {}) }
|
|
319
|
+
: undefined,
|
|
307
320
|
service_tier: override.service_tier ?? base.service_tier,
|
|
308
321
|
forensics_dedup: override.forensics_dedup ?? base.forensics_dedup,
|
|
309
322
|
show_token_cost: override.show_token_cost ?? base.show_token_cost,
|
|
@@ -132,10 +132,13 @@ export function loadPrompt(name, vars = {}) {
|
|
|
132
132
|
}
|
|
133
133
|
}
|
|
134
134
|
for (const [key, value] of Object.entries(effectiveVars)) {
|
|
135
|
+
const safeValue = key === "workingDirectory" && typeof value === "string"
|
|
136
|
+
? value.replaceAll("\\", "/")
|
|
137
|
+
: value;
|
|
135
138
|
// Use split/join instead of replaceAll to avoid JavaScript's special
|
|
136
139
|
// replacement patterns ($', $`, $&) being interpreted in the value.
|
|
137
140
|
// See: https://github.com/gsd-build/gsd-2/issues/2968
|
|
138
|
-
content = content.split(`{{${key}}}`).join(
|
|
141
|
+
content = content.split(`{{${key}}}`).join(safeValue);
|
|
139
142
|
}
|
|
140
143
|
return content.trim();
|
|
141
144
|
}
|
|
@@ -49,31 +49,132 @@ 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
|
|
52
|
+
## Layered Question Rounds
|
|
53
53
|
|
|
54
|
-
|
|
54
|
+
Questions are organized into four layers. Each layer targets a specific depth dimension. At each layer: ask 1-3 open questions per round, investigate between rounds as needed, and gate before advancing.
|
|
55
55
|
|
|
56
|
-
**
|
|
56
|
+
**Default to open questions.** Use `ask_user_questions` only when there are 2-3 genuinely distinct paths with clear tradeoffs (e.g., "REST vs GraphQL" or "Postgres vs SQLite"). For nuanced design questions, ask in plain text and let the user explain.
|
|
57
57
|
|
|
58
|
-
**If `{{structuredQuestionsAvailable}}` is `
|
|
58
|
+
**If `{{structuredQuestionsAvailable}}` is `true`:** use `ask_user_questions` for binary/ternary choices. 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.**
|
|
59
59
|
|
|
60
|
-
|
|
60
|
+
**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.
|
|
61
61
|
|
|
62
|
-
|
|
62
|
+
**Incremental persistence:** After every 2 question rounds (across any layer), 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.
|
|
63
63
|
|
|
64
|
-
|
|
64
|
+
### Identify Work Type
|
|
65
65
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
66
|
+
Before starting Layer 1, identify the primary work type and state it:
|
|
67
|
+
|
|
68
|
+
"Based on your description and the codebase, this is primarily **[work type]** work."
|
|
69
|
+
|
|
70
|
+
Work types include: API/backend, UI/frontend, CLI/developer tool, data pipeline, ML/AI, infrastructure/platform, refactoring/migration, or a combination. The user can correct this. The classification shapes which questions deserve deep exploration at each layer.
|
|
71
|
+
|
|
72
|
+
### Layer 1 — Scope
|
|
73
|
+
|
|
74
|
+
Resolve what's in and what's out. Ask about:
|
|
75
|
+
- Feature boundaries — what exactly ships in this milestone vs later
|
|
76
|
+
- Ambiguities in the user's description — anything you're unsure about, ask
|
|
77
|
+
- Dependencies — what does this work depend on, what depends on it
|
|
78
|
+
- Priority — if scope needs trimming, what matters most
|
|
79
|
+
|
|
80
|
+
Adapt depth to work type:
|
|
81
|
+
- **CLI work:** Focus on user mental model, command grammar, what existing commands do
|
|
82
|
+
- **Refactoring:** Focus on what changes vs what must stay identical
|
|
83
|
+
|
|
84
|
+
**Depth-matching:** Simple, well-defined scope may need 1 round. Ambiguous or large scope may need 3-4 rounds. Don't pad rounds to hit a number.
|
|
85
|
+
|
|
86
|
+
#### Layer 1 Gate
|
|
87
|
+
|
|
88
|
+
Summarize scope decisions in the user's own terminology:
|
|
89
|
+
- What's included, what's excluded, what's deferred
|
|
90
|
+
- Key boundaries and constraints
|
|
91
|
+
|
|
92
|
+
Then ask: **"Does this capture the scope? Adjust anything before we move on."**
|
|
93
|
+
|
|
94
|
+
If the user adjusts, reflect the updated understanding and ask again. Do not advance until the user explicitly confirms. If the user says "looks good, let's move faster" at any gate, respect that and advance.
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
### Layer 2 — Architecture
|
|
99
|
+
|
|
100
|
+
Resolve how it's built. Ask about:
|
|
101
|
+
- Per-slice technical decisions — what approach for each major piece
|
|
102
|
+
- Inter-slice contracts — how do the pieces connect
|
|
103
|
+
- Library/framework choices — with evidence from investigation, not assumptions
|
|
104
|
+
- Integration with existing code — what patterns to follow, what to change
|
|
105
|
+
|
|
106
|
+
Adapt depth to work type:
|
|
107
|
+
- **API work:** Contracts, versioning, backwards compatibility, auth boundaries
|
|
108
|
+
- **UI work:** Component boundaries, state management, data flow
|
|
109
|
+
- **Infrastructure:** Deployment topology, failure domains, rollback
|
|
110
|
+
|
|
111
|
+
Between rounds, use your available web search tools to research technologies from the Codebase Brief. Search for "[technology] [version] best practices [current year]" and "[technology] [version] known issues". Present findings alongside your questions.
|
|
112
|
+
|
|
113
|
+
#### Layer 2 Gate
|
|
114
|
+
|
|
115
|
+
Summarize architecture decisions, each with:
|
|
116
|
+
- The decision and rationale
|
|
117
|
+
- Evidence source (codebase patterns, library docs, web research)
|
|
118
|
+
- Alternatives considered
|
|
119
|
+
|
|
120
|
+
Then ask: **"Does this capture the architecture? Adjust anything before we move on."**
|
|
121
|
+
|
|
122
|
+
Same gate rules: reflect adjustments, wait for confirmation.
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
### Layer 3 — Error States
|
|
127
|
+
|
|
128
|
+
Resolve what happens when things fail. Present this layer with an option:
|
|
129
|
+
|
|
130
|
+
"We can go deep on error handling and failure modes, or I can apply sensible defaults based on the architecture decisions above. Which do you prefer?"
|
|
131
|
+
|
|
132
|
+
If the user chooses defaults, summarize what the defaults are and gate. If the user chooses to go deep, ask about:
|
|
133
|
+
- Failure modes for each major component
|
|
134
|
+
- Error propagation between layers (API → frontend, service → database)
|
|
135
|
+
- Timeout, retry, and circuit-breaker strategies
|
|
136
|
+
- What the user sees when something fails
|
|
137
|
+
|
|
138
|
+
Adapt depth to work type:
|
|
139
|
+
- **API work:** Rate limiting, timeout cascades, partial failure, status codes
|
|
140
|
+
- **UI work:** Loading states, optimistic updates, offline behavior, error boundaries
|
|
141
|
+
- **Data pipelines:** Data corruption, checkpoint recovery, idempotency
|
|
142
|
+
|
|
143
|
+
#### Layer 3 Gate
|
|
144
|
+
|
|
145
|
+
Summarize error handling strategy. Then ask: **"Does this capture how errors should be handled? Adjust anything before we move on."**
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
### Layer 4 — Quality Bar
|
|
150
|
+
|
|
151
|
+
Resolve what "done" means concretely. Ask about:
|
|
152
|
+
- Per-slice acceptance criteria — specific enough for automated verification
|
|
153
|
+
- Test strategy — what types of tests, what coverage expectations
|
|
154
|
+
- Definition of done — what must be true before the milestone ships
|
|
155
|
+
- Non-functional requirements — performance, accessibility, security if relevant
|
|
156
|
+
|
|
157
|
+
Adapt depth to work type:
|
|
158
|
+
- **CLI work:** Shell compatibility, error message clarity, exit code semantics
|
|
159
|
+
- **Refactoring:** Behavioral equivalence tests, not just code coverage
|
|
160
|
+
- **UI work:** Visual regression criteria, responsive breakpoints
|
|
161
|
+
|
|
162
|
+
#### Layer 4 Gate
|
|
163
|
+
|
|
164
|
+
Summarize quality bar: acceptance criteria, test strategy, definition of done. Then ask: **"Does this capture the quality bar? Adjust anything before we move on to requirements and roadmap?"**
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
### Layer cadence
|
|
169
|
+
|
|
170
|
+
- Do not count the reflection step as a question round. Rounds start at Layer 1 after reflection is confirmed.
|
|
171
|
+
- When all four layer gates have been confirmed (or skipped by the user), move to the Depth Verification step below. Do not ask a separate "ready to wrap up?" gate — the depth verification confirms the full picture.
|
|
71
172
|
|
|
72
173
|
## Questioning Philosophy
|
|
73
174
|
|
|
74
175
|
You are a thinking partner, not an interviewer.
|
|
75
176
|
|
|
76
|
-
**Turn-taking contract (non-bypassable).** Never fabricate, simulate, or role-play user responses. Never generate fake transcript markers like `[User]`, `[Human]`, or `User:` to invent input. Ask one question round (1-3 questions) per turn, then stop and wait for the user's actual response before continuing. If you use `ask_user_questions`, call it at most once per turn and treat its returned response as the only valid structured user input for that round.
|
|
177
|
+
**Turn-taking contract (non-bypassable).** Never fabricate, simulate, or role-play user responses. Never generate fake transcript markers like `[User]`, `[Human]`, or `User:` to invent input. Prior conversation context may be provided to you inside `<conversation_history>` with `<user_message>` / `<assistant_message>` XML tags — treat those as read-only context and never emit those tags in your response. Ask one question round (1-3 questions) per turn, then stop and wait for the user's actual response before continuing. If you use `ask_user_questions`, call it at most once per turn and treat its returned response as the only valid structured user input for that round.
|
|
77
178
|
|
|
78
179
|
**Start open, follow energy.** Let the user's enthusiasm guide where you dig deeper. If they light up about a particular aspect, explore it. If they're vague about something, that's where you probe.
|
|
79
180
|
|
|
@@ -225,6 +326,14 @@ Once the user is satisfied, in a single pass:
|
|
|
225
326
|
**Depth-Preservation Guidance for context.md:**
|
|
226
327
|
When writing context.md, preserve the user's exact terminology, emphasis, and specific framing from the discussion. Do not paraphrase user nuance into generic summaries. If the user said "craft feel," write "craft feel" — not "high-quality user experience." If they emphasized a specific constraint or negative requirement, carry that emphasis through verbatim. The context file is downstream agents' only window into this conversation — flattening specifics into generics loses the signal that shaped every decision.
|
|
227
328
|
|
|
329
|
+
**Structured sections from discussion layers:**
|
|
330
|
+
When writing CONTEXT.md, include structured sections that map to the discussion layers:
|
|
331
|
+
- **Scope** — what's in, what's out, what's deferred (from Layer 1 gate summary)
|
|
332
|
+
- **Architectural Decisions** — each with rationale, evidence source, alternatives considered (from Layer 2 gate summary)
|
|
333
|
+
- **Error Handling Strategy** — failure modes, propagation, user-facing error behavior (from Layer 3 gate summary)
|
|
334
|
+
- **Acceptance Criteria** — per-slice criteria specific enough for the planner to use directly (from Layer 4 gate summary)
|
|
335
|
+
These sections are in addition to whatever other context the discussion surfaced.
|
|
336
|
+
|
|
228
337
|
4. Write `{{contextPath}}` — use the **Context** output template below. Preserve key risks, unknowns, existing codebase constraints, integration points, and relevant requirements surfaced during discussion.
|
|
229
338
|
5. Call `gsd_plan_milestone` to create the roadmap. Decompose into demoable vertical slices with risk, depends, demo sentences, proof strategy, verification classes, milestone definition of done, requirement coverage, and a boundary map. If the milestone crosses multiple runtime boundaries, include an explicit final integration slice that proves the assembled system works end-to-end in a real environment. Use the **Roadmap** output template below to structure the tool call parameters.
|
|
230
339
|
6. For each architectural or pattern decision made during discussion, call `gsd_decision_save` — the tool auto-assigns IDs and regenerates `.gsd/DECISIONS.md` automatically.
|
|
@@ -35,7 +35,7 @@ GSD ships with bundled skills. Load the relevant skill file with the `read` tool
|
|
|
35
35
|
- Read before edit.
|
|
36
36
|
- Reproduce before fix when possible.
|
|
37
37
|
- Work is not done until the relevant verification has passed.
|
|
38
|
-
- **Never fabricate, simulate, or role-play user responses.** Never generate markers like `[User]`, `[Human]`, `User:`, or similar to represent user input inside your own output. Ask one question round (1-3 questions), then stop and wait for the user's actual response before continuing. If `ask_user_questions` is available, treat its returned response as the only valid structured user input for that round.
|
|
38
|
+
- **Never fabricate, simulate, or role-play user responses.** Never generate markers like `[User]`, `[Human]`, `User:`, or similar to represent user input inside your own output. Prior conversation context may be provided to you inside `<conversation_history>` with `<user_message>` / `<assistant_message>` XML tags — treat those as read-only context and never emit those tags in your response. Ask one question round (1-3 questions), then stop and wait for the user's actual response before continuing. If `ask_user_questions` is available, treat its returned response as the only valid structured user input for that round.
|
|
39
39
|
- Never print, echo, log, or restate secrets or credentials. Report only key names and applied/skipped status.
|
|
40
40
|
- Never ask the user to edit `.env` files or set secrets manually. Use `secure_env_collect`.
|
|
41
41
|
- In enduring files, write current state only unless the file is explicitly historical.
|