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
|
@@ -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.
|
|
@@ -57,6 +57,7 @@ import {
|
|
|
57
57
|
insertMilestone,
|
|
58
58
|
insertSlice,
|
|
59
59
|
insertTask,
|
|
60
|
+
updateSliceStatus,
|
|
60
61
|
updateTaskStatus,
|
|
61
62
|
getPendingGateCountForTurn,
|
|
62
63
|
type MilestoneRow,
|
|
@@ -358,6 +359,25 @@ function reconcileDiskToDb(basePath: string): MilestoneRow[] {
|
|
|
358
359
|
depends: s.depends, demo: s.demo,
|
|
359
360
|
});
|
|
360
361
|
}
|
|
362
|
+
|
|
363
|
+
// Reconcile stale *existing* slice rows (#3599): a slice row may exist in
|
|
364
|
+
// the DB with status "pending" even though disk artifacts (SUMMARY) prove
|
|
365
|
+
// completion — the same class of desync that task-level reconciliation
|
|
366
|
+
// (further below) already handles. Without this, the dependency resolver
|
|
367
|
+
// builds doneSliceIds from stale DB rows and downstream slices stay blocked
|
|
368
|
+
// forever with "No slice eligible".
|
|
369
|
+
for (const dbSlice of dbSlices) {
|
|
370
|
+
if (isStatusDone(dbSlice.status)) continue;
|
|
371
|
+
const summaryPath = resolveSliceFile(basePath, mid, dbSlice.id, "SUMMARY");
|
|
372
|
+
if (summaryPath) {
|
|
373
|
+
try {
|
|
374
|
+
updateSliceStatus(mid, dbSlice.id, "complete");
|
|
375
|
+
logWarning("reconcile", `slice ${mid}/${dbSlice.id} status reconciled from "${dbSlice.status}" to "complete" (#3599)`, { mid, sid: dbSlice.id });
|
|
376
|
+
} catch (e) {
|
|
377
|
+
logError("reconcile", `failed to update slice ${dbSlice.id}`, { sid: dbSlice.id, error: (e as Error).message });
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
}
|
|
361
381
|
}
|
|
362
382
|
return allMilestones;
|
|
363
383
|
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { test } from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { readFileSync } from "node:fs";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
|
|
7
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
const autoSource = readFileSync(join(__dirname, "..", "auto.ts"), "utf-8");
|
|
9
|
+
|
|
10
|
+
test("#3370: cleanupAfterLoopExit preserves paused auto badge after provider pause", () => {
|
|
11
|
+
const cleanupIdx = autoSource.indexOf("function cleanupAfterLoopExit");
|
|
12
|
+
assert.ok(cleanupIdx > -1, "auto.ts should define cleanupAfterLoopExit");
|
|
13
|
+
|
|
14
|
+
const dispatchIdx = autoSource.indexOf("export async function dispatchHookUnit", cleanupIdx);
|
|
15
|
+
assert.ok(dispatchIdx > cleanupIdx, "cleanupAfterLoopExit body should be bounded by the next export");
|
|
16
|
+
|
|
17
|
+
const cleanupBody = autoSource.slice(cleanupIdx, dispatchIdx);
|
|
18
|
+
const pausedGuardIdx = cleanupBody.indexOf("if (!s.paused) {");
|
|
19
|
+
const clearStatusIdx = cleanupBody.indexOf('ctx.ui.setStatus("gsd-auto", undefined);');
|
|
20
|
+
|
|
21
|
+
assert.ok(pausedGuardIdx > -1, "loop-exit cleanup must guard UI clearing when auto is paused");
|
|
22
|
+
assert.ok(clearStatusIdx > pausedGuardIdx, "status clearing must live behind the paused guard");
|
|
23
|
+
assert.ok(
|
|
24
|
+
autoSource.includes('ctx?.ui.setStatus("gsd-auto", "paused");'),
|
|
25
|
+
"pauseAuto must still set the paused badge for transient provider pauses",
|
|
26
|
+
);
|
|
27
|
+
});
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Regression test for #3674 — block direct writes to gsd.db
|
|
3
|
+
*
|
|
4
|
+
* When gsd_complete_task was unavailable, agents fell back to shell-based
|
|
5
|
+
* sqlite3 writes, corrupting the WAL-backed database. The fix extends
|
|
6
|
+
* write-intercept to block file writes and bash commands targeting gsd.db.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { describe, test } from 'node:test';
|
|
10
|
+
import assert from 'node:assert/strict';
|
|
11
|
+
import { isBlockedStateFile, isBashWriteToStateFile } from '../write-intercept.ts';
|
|
12
|
+
|
|
13
|
+
describe('isBlockedStateFile blocks gsd.db paths (#3674)', () => {
|
|
14
|
+
test('blocks .gsd/gsd.db', () => {
|
|
15
|
+
assert.ok(isBlockedStateFile('/project/.gsd/gsd.db'));
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test('blocks .gsd/gsd.db-wal', () => {
|
|
19
|
+
assert.ok(isBlockedStateFile('/project/.gsd/gsd.db-wal'));
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
test('blocks .gsd/gsd.db-shm', () => {
|
|
23
|
+
assert.ok(isBlockedStateFile('/project/.gsd/gsd.db-shm'));
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test('blocks resolved symlink path under .gsd/projects/', () => {
|
|
27
|
+
assert.ok(isBlockedStateFile('/home/user/.gsd/projects/myproj/gsd.db'));
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test('still blocks STATE.md', () => {
|
|
31
|
+
assert.ok(isBlockedStateFile('/project/.gsd/STATE.md'));
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test('does not block other .gsd files', () => {
|
|
35
|
+
assert.ok(!isBlockedStateFile('/project/.gsd/DECISIONS.md'));
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
describe('isBashWriteToStateFile blocks DB shell commands (#3674)', () => {
|
|
40
|
+
test('blocks sqlite3 targeting gsd.db', () => {
|
|
41
|
+
assert.ok(isBashWriteToStateFile('sqlite3 .gsd/gsd.db "INSERT INTO ..."'));
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test('blocks better-sqlite3 targeting gsd.db', () => {
|
|
45
|
+
assert.ok(isBashWriteToStateFile('node -e "require(\'better-sqlite3\')(\'.gsd/gsd.db\')"'));
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test('blocks shell redirect to gsd.db', () => {
|
|
49
|
+
assert.ok(isBashWriteToStateFile('echo data > .gsd/gsd.db'));
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test('blocks cp to gsd.db', () => {
|
|
53
|
+
assert.ok(isBashWriteToStateFile('cp backup.db .gsd/gsd.db'));
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test('blocks mv to gsd.db', () => {
|
|
57
|
+
assert.ok(isBashWriteToStateFile('mv temp.db .gsd/gsd.db'));
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test('does not block reading gsd.db with cat', () => {
|
|
61
|
+
assert.ok(!isBashWriteToStateFile('cat .gsd/gsd.db'));
|
|
62
|
+
});
|
|
63
|
+
});
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* definition-io.ts — unit tests for readFrozenDefinition.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, test, beforeEach, afterEach } from "node:test";
|
|
6
|
+
import assert from "node:assert/strict";
|
|
7
|
+
import { mkdtempSync, mkdirSync, writeFileSync, rmSync, realpathSync } from "node:fs";
|
|
8
|
+
import { join } from "node:path";
|
|
9
|
+
import { tmpdir } from "node:os";
|
|
10
|
+
|
|
11
|
+
import { readFrozenDefinition } from "../definition-io.ts";
|
|
12
|
+
|
|
13
|
+
function createTmpDir(): string {
|
|
14
|
+
return realpathSync(mkdtempSync(join(tmpdir(), "gsd-defio-test-")));
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
describe("readFrozenDefinition", () => {
|
|
18
|
+
let runDir: string;
|
|
19
|
+
|
|
20
|
+
beforeEach(() => {
|
|
21
|
+
runDir = createTmpDir();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
afterEach(() => {
|
|
25
|
+
rmSync(runDir, { recursive: true, force: true });
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test("parses a valid DEFINITION.yaml", () => {
|
|
29
|
+
const yaml = [
|
|
30
|
+
"version: 1",
|
|
31
|
+
"name: test-workflow",
|
|
32
|
+
"description: A test workflow",
|
|
33
|
+
"steps:",
|
|
34
|
+
" - id: step-1",
|
|
35
|
+
" prompt: do the thing",
|
|
36
|
+
].join("\n");
|
|
37
|
+
writeFileSync(join(runDir, "DEFINITION.yaml"), yaml, "utf-8");
|
|
38
|
+
|
|
39
|
+
const def = readFrozenDefinition(runDir);
|
|
40
|
+
assert.equal(def.version, 1);
|
|
41
|
+
assert.equal(def.name, "test-workflow");
|
|
42
|
+
assert.equal(def.description, "A test workflow");
|
|
43
|
+
assert.equal(def.steps.length, 1);
|
|
44
|
+
assert.equal(def.steps[0].id, "step-1");
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test("throws when DEFINITION.yaml is missing", () => {
|
|
48
|
+
assert.throws(() => readFrozenDefinition(runDir), {
|
|
49
|
+
code: "ENOENT",
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test("throws on malformed YAML", () => {
|
|
54
|
+
writeFileSync(join(runDir, "DEFINITION.yaml"), ": : : not valid yaml [", "utf-8");
|
|
55
|
+
assert.throws(() => readFrozenDefinition(runDir));
|
|
56
|
+
});
|
|
57
|
+
});
|
|
@@ -172,6 +172,32 @@ test("dispatch guard ignores positionally-earlier reverse dependents for zero-de
|
|
|
172
172
|
);
|
|
173
173
|
});
|
|
174
174
|
|
|
175
|
+
test("dispatch guard treats zero-dependency slices as independent when a milestone uses explicit deps (#3998)", (t) => {
|
|
176
|
+
const repo = setupRepo();
|
|
177
|
+
t.after(() => teardownRepo(repo));
|
|
178
|
+
|
|
179
|
+
mkdirSync(join(repo, ".gsd", "milestones", "M022"), { recursive: true });
|
|
180
|
+
|
|
181
|
+
insertMilestone({ id: "M022", title: "Mixed dependency milestone" });
|
|
182
|
+
insertSlice({ id: "S02", milestoneId: "M022", title: "Core A", status: "complete", depends: [], sequence: 2 });
|
|
183
|
+
insertSlice({ id: "S03", milestoneId: "M022", title: "Core B", status: "complete", depends: [], sequence: 3 });
|
|
184
|
+
insertSlice({ id: "S05", milestoneId: "M022", title: "Blocked integration", status: "pending", depends: ["S02", "S03", "S07"], sequence: 5 });
|
|
185
|
+
insertSlice({ id: "S06", milestoneId: "M022", title: "Independent zero-dep slice", status: "pending", depends: [], sequence: 6 });
|
|
186
|
+
insertSlice({ id: "S07", milestoneId: "M022", title: "Late prerequisite", status: "pending", depends: ["S02"], sequence: 7 });
|
|
187
|
+
|
|
188
|
+
writeFileSync(join(repo, ".gsd", "milestones", "M022", "M022-ROADMAP.md"), "# M022\n");
|
|
189
|
+
|
|
190
|
+
assert.equal(
|
|
191
|
+
getPriorSliceCompletionBlocker(repo, "main", "execute-task", "M022/S06/T02"),
|
|
192
|
+
null,
|
|
193
|
+
);
|
|
194
|
+
|
|
195
|
+
assert.equal(
|
|
196
|
+
getPriorSliceCompletionBlocker(repo, "main", "execute-task", "M022/S05/T01"),
|
|
197
|
+
"Cannot dispatch execute-task M022/S05/T01: dependency slice M022/S07 is not complete.",
|
|
198
|
+
);
|
|
199
|
+
});
|
|
200
|
+
|
|
175
201
|
test("dispatch guard allows slice with all declared dependencies complete", (t) => {
|
|
176
202
|
const repo = setupRepo();
|
|
177
203
|
t.after(() => teardownRepo(repo));
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { isDoctorHealActionable } from "../commands-handlers.js";
|
|
4
|
+
|
|
5
|
+
test("doctor heal actionable filter keeps fixable warnings and errors", () => {
|
|
6
|
+
assert.equal(isDoctorHealActionable({ fixable: true, severity: "warning" }), true);
|
|
7
|
+
assert.equal(isDoctorHealActionable({ fixable: true, severity: "error" }), true);
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
test("doctor heal actionable filter excludes info and non-fixable issues", () => {
|
|
11
|
+
assert.equal(isDoctorHealActionable({ fixable: true, severity: "info" }), false);
|
|
12
|
+
assert.equal(isDoctorHealActionable({ fixable: false, severity: "warning" }), false);
|
|
13
|
+
assert.equal(isDoctorHealActionable({ fixable: false, severity: "error" }), false);
|
|
14
|
+
});
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* false-degraded-mode-warning.test.ts — Regression tests for #3922.
|
|
3
|
+
*
|
|
4
|
+
* Before this fix, deriveState() logged a "DB unavailable — degraded mode"
|
|
5
|
+
* warning even when the DB simply hadn't been opened yet (e.g. during
|
|
6
|
+
* before_agent_start context injection). The fix introduces wasDbOpenAttempted()
|
|
7
|
+
* to distinguish "not yet initialized" from "genuinely unavailable."
|
|
8
|
+
*
|
|
9
|
+
* Two aspects:
|
|
10
|
+
* 1. gsd-db: wasDbOpenAttempted() tracks whether openDatabase() was ever called.
|
|
11
|
+
* 2. state: the degraded-mode warning is gated behind wasDbOpenAttempted().
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { describe, test } from "node:test";
|
|
15
|
+
import assert from "node:assert/strict";
|
|
16
|
+
import { readFileSync } from "node:fs";
|
|
17
|
+
import { dirname, join } from "node:path";
|
|
18
|
+
import { fileURLToPath } from "node:url";
|
|
19
|
+
import {
|
|
20
|
+
openDatabase,
|
|
21
|
+
closeDatabase,
|
|
22
|
+
isDbAvailable,
|
|
23
|
+
wasDbOpenAttempted,
|
|
24
|
+
} from "../gsd-db.ts";
|
|
25
|
+
|
|
26
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
27
|
+
const stateSource = readFileSync(join(__dirname, "..", "state.ts"), "utf-8");
|
|
28
|
+
|
|
29
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
30
|
+
// 1. gsd-db: wasDbOpenAttempted flag
|
|
31
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
32
|
+
|
|
33
|
+
describe("wasDbOpenAttempted (#3922)", () => {
|
|
34
|
+
|
|
35
|
+
test("wasDbOpenAttempted returns true after openDatabase is called", () => {
|
|
36
|
+
// By this point in the test suite, openDatabase may or may not have been
|
|
37
|
+
// called by other tests. So we call it explicitly and verify it returns true.
|
|
38
|
+
openDatabase(":memory:");
|
|
39
|
+
assert.strictEqual(wasDbOpenAttempted(), true,
|
|
40
|
+
"wasDbOpenAttempted should be true after openDatabase call");
|
|
41
|
+
closeDatabase();
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test("openDatabase sets the flag even if it fails on invalid path", () => {
|
|
45
|
+
// openDatabase with an unreachable path may fail, but the flag should
|
|
46
|
+
// still be set because the attempt was made.
|
|
47
|
+
try { openDatabase("/nonexistent/path/that/will/fail.db"); } catch { /* expected */ }
|
|
48
|
+
assert.strictEqual(wasDbOpenAttempted(), true,
|
|
49
|
+
"wasDbOpenAttempted should be true even after a failed open attempt");
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
54
|
+
// 2. state.ts: degraded-mode warning is gated behind wasDbOpenAttempted
|
|
55
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
56
|
+
|
|
57
|
+
describe("degraded-mode warning guard (#3922)", () => {
|
|
58
|
+
|
|
59
|
+
test("state.ts imports wasDbOpenAttempted from gsd-db", () => {
|
|
60
|
+
assert.ok(
|
|
61
|
+
stateSource.includes("wasDbOpenAttempted"),
|
|
62
|
+
"state.ts must import wasDbOpenAttempted to gate the degraded-mode warning",
|
|
63
|
+
);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test("degraded-mode warning is inside a wasDbOpenAttempted() guard", () => {
|
|
67
|
+
// Find the degraded-mode warning string
|
|
68
|
+
const warningStr = 'DB unavailable — using filesystem state derivation (degraded mode)';
|
|
69
|
+
const warningIdx = stateSource.indexOf(warningStr);
|
|
70
|
+
assert.ok(warningIdx > 0, "degraded-mode warning string must exist in state.ts");
|
|
71
|
+
|
|
72
|
+
// The wasDbOpenAttempted() check must appear BEFORE the warning,
|
|
73
|
+
// within the same else-branch (i.e. within a reasonable distance).
|
|
74
|
+
// Look backwards from the warning for the guard.
|
|
75
|
+
const searchWindow = stateSource.slice(Math.max(0, warningIdx - 300), warningIdx);
|
|
76
|
+
assert.ok(
|
|
77
|
+
searchWindow.includes("wasDbOpenAttempted()"),
|
|
78
|
+
"wasDbOpenAttempted() guard must appear shortly before the degraded-mode warning " +
|
|
79
|
+
"to prevent false warnings when DB has not been initialized yet",
|
|
80
|
+
);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test("warning is NOT emitted unconditionally in the else branch", () => {
|
|
84
|
+
// The old code had `logWarning(...)` directly in the else branch.
|
|
85
|
+
// The fix wraps it in `if (wasDbOpenAttempted())`.
|
|
86
|
+
// Verify the logWarning call is inside a conditional, not bare.
|
|
87
|
+
const lines = stateSource.split("\n");
|
|
88
|
+
for (let i = 0; i < lines.length; i++) {
|
|
89
|
+
if (lines[i]!.includes("DB unavailable") && lines[i]!.includes("degraded mode")) {
|
|
90
|
+
// This line has the warning. Check that the preceding non-empty line
|
|
91
|
+
// contains an if-condition (wasDbOpenAttempted), not a bare else.
|
|
92
|
+
let prev = i - 1;
|
|
93
|
+
while (prev >= 0 && lines[prev]!.trim() === "") prev--;
|
|
94
|
+
const prevLine = lines[prev]!.trim();
|
|
95
|
+
assert.ok(
|
|
96
|
+
prevLine.includes("wasDbOpenAttempted"),
|
|
97
|
+
`Line ${i + 1} emits degraded-mode warning — preceding line ${prev + 1} must ` +
|
|
98
|
+
`contain wasDbOpenAttempted guard, but found: "${prevLine}"`,
|
|
99
|
+
);
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
});
|