gsd-pi 2.72.0 → 2.73.0-dev.1cfd50c
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 +15 -15
- 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 +3 -3
- 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 +15 -15
- 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-2f24283c162b6ab3.js → page-f2a7482d42a5614b.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/app/{layout-9ecfd95f343793f0.js → layout-a16c7a7ecdf0c2cf.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/app/page-f1e30ab6bb269149.js +1 -0
- package/dist/web/standalone/.next/static/chunks/main-app-fdab67f7802d7832.js +1 -0
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +1 -0
- package/dist/web/standalone/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/dist/modes/interactive/controllers/chat-controller.d.ts +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +22 -9
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.test.js +63 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.test.js.map +1 -0
- 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-coding-agent/src/modes/interactive/controllers/chat-controller.test.ts +71 -0
- package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +23 -9
- 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-7115e62689b5fd84.js +0 -1
- package/dist/web/standalone/.next/static/chunks/main-app-d3d4c336195465f9.js +0 -1
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ab5a8926e07ec673.js +0 -1
- package/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/{Y0I7CjXJl-tWoV__KidV4 → uNGVqSkAnszMl0okA4nnp}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{Y0I7CjXJl-tWoV__KidV4 → uNGVqSkAnszMl0okA4nnp}/_ssgManifest.js +0 -0
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
getRequirementById,
|
|
16
16
|
getActiveDecisions,
|
|
17
17
|
getActiveRequirements,
|
|
18
|
+
getTask,
|
|
18
19
|
transaction,
|
|
19
20
|
_getAdapter,
|
|
20
21
|
_resetProvider,
|
|
@@ -43,6 +44,16 @@ function cleanup(dbPath: string): void {
|
|
|
43
44
|
}
|
|
44
45
|
}
|
|
45
46
|
|
|
47
|
+
function withPlatform<T>(platform: NodeJS.Platform, fn: () => T): T {
|
|
48
|
+
const original = process.platform;
|
|
49
|
+
Object.defineProperty(process, 'platform', { value: platform });
|
|
50
|
+
try {
|
|
51
|
+
return fn();
|
|
52
|
+
} finally {
|
|
53
|
+
Object.defineProperty(process, 'platform', { value: original });
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
46
57
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
47
58
|
// gsd-db tests
|
|
48
59
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
@@ -279,6 +290,26 @@ describe('gsd-db', () => {
|
|
|
279
290
|
cleanup(dbPath);
|
|
280
291
|
});
|
|
281
292
|
|
|
293
|
+
test('gsd-db: mmap stays disabled on darwin file-backed DBs', () => {
|
|
294
|
+
const darwinDbPath = tempDbPath();
|
|
295
|
+
withPlatform('darwin', () => {
|
|
296
|
+
openDatabase(darwinDbPath);
|
|
297
|
+
const adapter = _getAdapter()!;
|
|
298
|
+
const mmap = adapter.prepare('PRAGMA mmap_size').get();
|
|
299
|
+
assert.deepStrictEqual(mmap?.['mmap_size'], 0, 'darwin should leave mmap_size disabled');
|
|
300
|
+
cleanup(darwinDbPath);
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
const linuxDbPath = tempDbPath();
|
|
304
|
+
withPlatform('linux', () => {
|
|
305
|
+
openDatabase(linuxDbPath);
|
|
306
|
+
const adapter = _getAdapter()!;
|
|
307
|
+
const mmap = adapter.prepare('PRAGMA mmap_size').get();
|
|
308
|
+
assert.deepStrictEqual(mmap?.['mmap_size'], 67108864, 'non-darwin should still enable mmap_size');
|
|
309
|
+
cleanup(linuxDbPath);
|
|
310
|
+
});
|
|
311
|
+
});
|
|
312
|
+
|
|
282
313
|
test('gsd-db: transaction rollback on error', () => {
|
|
283
314
|
openDatabase(':memory:');
|
|
284
315
|
|
|
@@ -329,6 +360,79 @@ describe('gsd-db', () => {
|
|
|
329
360
|
closeDatabase();
|
|
330
361
|
});
|
|
331
362
|
|
|
363
|
+
test('gsd-db: recreates missing verification evidence dedup index after removing duplicate rows', () => {
|
|
364
|
+
const dbPath = tempDbPath();
|
|
365
|
+
openDatabase(dbPath);
|
|
366
|
+
|
|
367
|
+
let adapter = _getAdapter()!;
|
|
368
|
+
adapter.prepare("INSERT INTO milestones (id, created_at) VALUES (?, '')").run('M001');
|
|
369
|
+
adapter.prepare("INSERT INTO slices (milestone_id, id, created_at) VALUES (?, ?, '')").run('M001', 'S01');
|
|
370
|
+
adapter.prepare("INSERT INTO tasks (milestone_id, slice_id, id) VALUES (?, ?, ?)").run('M001', 'S01', 'T01');
|
|
371
|
+
adapter.exec('DROP INDEX IF EXISTS idx_verification_evidence_dedup');
|
|
372
|
+
|
|
373
|
+
const insertEvidence = adapter.prepare(
|
|
374
|
+
`INSERT INTO verification_evidence (
|
|
375
|
+
task_id, slice_id, milestone_id, command, exit_code, verdict, duration_ms, created_at
|
|
376
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
377
|
+
);
|
|
378
|
+
insertEvidence.run('T01', 'S01', 'M001', 'npm test', 1, 'fail', 125, '2026-04-12T00:00:00.000Z');
|
|
379
|
+
insertEvidence.run('T01', 'S01', 'M001', 'npm test', 1, 'fail', 125, '2026-04-12T00:00:01.000Z');
|
|
380
|
+
insertEvidence.run('T01', 'S01', 'M001', 'npm run lint', 0, 'pass', 90, '2026-04-12T00:00:02.000Z');
|
|
381
|
+
|
|
382
|
+
closeDatabase();
|
|
383
|
+
|
|
384
|
+
assert.equal(openDatabase(dbPath), true, 'openDatabase should repair legacy duplicate evidence rows');
|
|
385
|
+
|
|
386
|
+
adapter = _getAdapter()!;
|
|
387
|
+
const countRow = adapter.prepare(
|
|
388
|
+
`SELECT count(*) as cnt
|
|
389
|
+
FROM verification_evidence
|
|
390
|
+
WHERE task_id = ? AND slice_id = ? AND milestone_id = ? AND command = ? AND verdict = ?`,
|
|
391
|
+
).get('T01', 'S01', 'M001', 'npm test', 'fail');
|
|
392
|
+
assert.equal(countRow?.['cnt'], 1, 'duplicate verification evidence rows should be deduplicated before index creation');
|
|
393
|
+
|
|
394
|
+
const indexRow = adapter.prepare(
|
|
395
|
+
"SELECT name FROM sqlite_master WHERE type = 'index' AND name = 'idx_verification_evidence_dedup'",
|
|
396
|
+
).get();
|
|
397
|
+
assert.equal(indexRow?.['name'], 'idx_verification_evidence_dedup', 'dedup index should be recreated on reopen');
|
|
398
|
+
|
|
399
|
+
cleanup(dbPath);
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
test('gsd-db: rowToTask tolerates legacy comma-separated task arrays', () => {
|
|
403
|
+
openDatabase(':memory:');
|
|
404
|
+
|
|
405
|
+
const adapter = _getAdapter()!;
|
|
406
|
+
adapter.prepare("INSERT INTO milestones (id, created_at) VALUES (?, '')").run('M001');
|
|
407
|
+
adapter.prepare("INSERT INTO slices (milestone_id, id, created_at) VALUES (?, ?, '')").run('M001', 'S01');
|
|
408
|
+
adapter.prepare(
|
|
409
|
+
`INSERT INTO tasks (
|
|
410
|
+
milestone_id, slice_id, id, key_files, key_decisions, files, inputs, expected_output
|
|
411
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
412
|
+
).run(
|
|
413
|
+
'M001',
|
|
414
|
+
'S01',
|
|
415
|
+
'T01',
|
|
416
|
+
'[]',
|
|
417
|
+
'[]',
|
|
418
|
+
'tests/test_verify.py, config.yaml, configs/roster_2026-05-11.yaml',
|
|
419
|
+
'tests/test_verify.py',
|
|
420
|
+
'reports/summary.md, artifacts/output.json',
|
|
421
|
+
);
|
|
422
|
+
|
|
423
|
+
const task = getTask('M001', 'S01', 'T01');
|
|
424
|
+
assert.ok(task, 'task should load successfully from DB');
|
|
425
|
+
assert.deepEqual(task?.files, [
|
|
426
|
+
'tests/test_verify.py',
|
|
427
|
+
'config.yaml',
|
|
428
|
+
'configs/roster_2026-05-11.yaml',
|
|
429
|
+
]);
|
|
430
|
+
assert.deepEqual(task?.inputs, ['tests/test_verify.py']);
|
|
431
|
+
assert.deepEqual(task?.expected_output, ['reports/summary.md', 'artifacts/output.json']);
|
|
432
|
+
|
|
433
|
+
closeDatabase();
|
|
434
|
+
});
|
|
435
|
+
|
|
332
436
|
test('gsd-db: query wrappers return null/empty when DB unavailable', () => {
|
|
333
437
|
// Ensure DB is closed
|
|
334
438
|
closeDatabase();
|
|
@@ -347,15 +451,13 @@ describe('gsd-db', () => {
|
|
|
347
451
|
assert.deepStrictEqual(ar, [], 'getActiveRequirements returns [] when DB closed');
|
|
348
452
|
});
|
|
349
453
|
|
|
350
|
-
test('gsd-db: wasDbOpenAttempted
|
|
351
|
-
|
|
352
|
-
// (previous tests in this suite already called openDatabase, so the flag is set)
|
|
454
|
+
test('gsd-db: closeDatabase resets wasDbOpenAttempted after an intentional close', () => {
|
|
455
|
+
openDatabase(':memory:');
|
|
353
456
|
assert.ok(wasDbOpenAttempted(), 'wasDbOpenAttempted should be true after openDatabase was called');
|
|
354
457
|
|
|
355
|
-
// Verify the flag persists even after closeDatabase
|
|
356
458
|
closeDatabase();
|
|
357
459
|
assert.ok(!isDbAvailable(), 'DB should not be available after close');
|
|
358
|
-
assert.ok(wasDbOpenAttempted(), 'wasDbOpenAttempted should
|
|
460
|
+
assert.ok(!wasDbOpenAttempted(), 'wasDbOpenAttempted should reset after closeDatabase');
|
|
359
461
|
});
|
|
360
462
|
|
|
361
463
|
// ─── Final Report ──────────────────────────────────────────────────────────
|
|
@@ -248,23 +248,25 @@ describe('git-service', async () => {
|
|
|
248
248
|
|
|
249
249
|
assert.deepStrictEqual(
|
|
250
250
|
RUNTIME_EXCLUSION_PATHS.length,
|
|
251
|
-
|
|
252
|
-
"exactly
|
|
251
|
+
15,
|
|
252
|
+
"exactly 15 runtime exclusion paths"
|
|
253
253
|
);
|
|
254
254
|
|
|
255
255
|
const expectedPaths = [
|
|
256
256
|
".gsd/activity/",
|
|
257
|
+
".gsd/forensics/",
|
|
257
258
|
".gsd/runtime/",
|
|
258
259
|
".gsd/worktrees/",
|
|
260
|
+
".gsd/parallel/",
|
|
259
261
|
".gsd/auto.lock",
|
|
260
262
|
".gsd/metrics.json",
|
|
261
|
-
".gsd/completed-units
|
|
263
|
+
".gsd/completed-units*.json",
|
|
264
|
+
".gsd/state-manifest.json",
|
|
262
265
|
".gsd/STATE.md",
|
|
263
|
-
".gsd/gsd.db",
|
|
264
|
-
".gsd/gsd.db-shm",
|
|
265
|
-
".gsd/gsd.db-wal",
|
|
266
|
+
".gsd/gsd.db*",
|
|
266
267
|
".gsd/journal/",
|
|
267
268
|
".gsd/doctor-history.jsonl",
|
|
269
|
+
".gsd/event-log.jsonl",
|
|
268
270
|
".gsd/DISCUSSION-MANIFEST.json",
|
|
269
271
|
];
|
|
270
272
|
|
|
@@ -427,3 +427,66 @@ test("formatDoctorFindings shows findings with appropriate icons", () => {
|
|
|
427
427
|
assert.ok(output.includes("1 warning"));
|
|
428
428
|
assert.ok(output.includes("1 fixed"));
|
|
429
429
|
});
|
|
430
|
+
|
|
431
|
+
// ─── Regression #3891 — alibaba-coding-plan missing from PROVIDER_REGISTRY ───────
|
|
432
|
+
//
|
|
433
|
+
// Before this fix, `alibaba-coding-plan` was not in PROVIDER_REGISTRY, causing
|
|
434
|
+
// `/gsd keys add alibaba-coding-plan` to silently fail (provider not found).
|
|
435
|
+
// alibaba-dashscope is the new standalone provider added in the same PR.
|
|
436
|
+
|
|
437
|
+
test("regression #3891 — alibaba-coding-plan is in PROVIDER_REGISTRY", () => {
|
|
438
|
+
const provider = findProvider("alibaba-coding-plan");
|
|
439
|
+
assert.ok(provider, "alibaba-coding-plan must be in PROVIDER_REGISTRY for /gsd keys add to work");
|
|
440
|
+
assert.equal(provider.id, "alibaba-coding-plan");
|
|
441
|
+
assert.equal(provider.category, "llm");
|
|
442
|
+
assert.equal(provider.envVar, "ALIBABA_API_KEY");
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
test("alibaba-dashscope is in PROVIDER_REGISTRY", () => {
|
|
446
|
+
const provider = findProvider("alibaba-dashscope");
|
|
447
|
+
assert.ok(provider, "alibaba-dashscope must be in PROVIDER_REGISTRY for /gsd keys add to work");
|
|
448
|
+
assert.equal(provider.id, "alibaba-dashscope");
|
|
449
|
+
assert.equal(provider.category, "llm");
|
|
450
|
+
assert.equal(provider.envVar, "DASHSCOPE_API_KEY");
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
test("alibaba-coding-plan and alibaba-dashscope are separate providers (different env vars)", () => {
|
|
454
|
+
const codingPlan = findProvider("alibaba-coding-plan");
|
|
455
|
+
const dashscope = findProvider("alibaba-dashscope");
|
|
456
|
+
assert.ok(codingPlan, "alibaba-coding-plan must exist");
|
|
457
|
+
assert.ok(dashscope, "alibaba-dashscope must exist");
|
|
458
|
+
assert.notEqual(
|
|
459
|
+
codingPlan.envVar,
|
|
460
|
+
dashscope.envVar,
|
|
461
|
+
"alibaba-coding-plan and alibaba-dashscope must use different env vars",
|
|
462
|
+
);
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
test("getAllKeyStatuses includes alibaba-coding-plan", () => {
|
|
466
|
+
const auth = makeAuth();
|
|
467
|
+
const statuses = getAllKeyStatuses(auth);
|
|
468
|
+
const found = statuses.find((s) => s.provider.id === "alibaba-coding-plan");
|
|
469
|
+
assert.ok(found, "getAllKeyStatuses must include alibaba-coding-plan");
|
|
470
|
+
});
|
|
471
|
+
|
|
472
|
+
test("getAllKeyStatuses includes alibaba-dashscope", () => {
|
|
473
|
+
const auth = makeAuth();
|
|
474
|
+
const statuses = getAllKeyStatuses(auth);
|
|
475
|
+
const found = statuses.find((s) => s.provider.id === "alibaba-dashscope");
|
|
476
|
+
assert.ok(found, "getAllKeyStatuses must include alibaba-dashscope");
|
|
477
|
+
});
|
|
478
|
+
|
|
479
|
+
test("getAllKeyStatuses detects DASHSCOPE_API_KEY for alibaba-dashscope (failure path: missing key shows not configured)", () => {
|
|
480
|
+
const saved = process.env.DASHSCOPE_API_KEY;
|
|
481
|
+
delete process.env.DASHSCOPE_API_KEY;
|
|
482
|
+
try {
|
|
483
|
+
const auth = makeAuth();
|
|
484
|
+
const statuses = getAllKeyStatuses(auth);
|
|
485
|
+
const found = statuses.find((s) => s.provider.id === "alibaba-dashscope");
|
|
486
|
+
assert.ok(found);
|
|
487
|
+
assert.equal(found.configured, false);
|
|
488
|
+
assert.equal(found.source, "none");
|
|
489
|
+
} finally {
|
|
490
|
+
if (saved !== undefined) process.env.DASHSCOPE_API_KEY = saved;
|
|
491
|
+
}
|
|
492
|
+
});
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Regression tests for memory pressure monitoring (#3331) and
|
|
3
|
+
* stuck detection persistence (#3704) in auto/loop.ts.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { describe, test } from "node:test";
|
|
7
|
+
import assert from "node:assert/strict";
|
|
8
|
+
import { readFileSync } from "node:fs";
|
|
9
|
+
import { join, dirname } from "node:path";
|
|
10
|
+
import { fileURLToPath } from "node:url";
|
|
11
|
+
|
|
12
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
13
|
+
const loopSource = readFileSync(join(__dirname, "..", "auto", "loop.ts"), "utf-8");
|
|
14
|
+
|
|
15
|
+
describe("memory pressure monitoring (#3331)", () => {
|
|
16
|
+
test("checkMemoryPressure function exists", () => {
|
|
17
|
+
assert.match(loopSource, /function checkMemoryPressure/);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
test("MEMORY_PRESSURE_THRESHOLD constant is defined", () => {
|
|
21
|
+
assert.match(loopSource, /MEMORY_PRESSURE_THRESHOLD\s*=\s*0\.\d+/);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test("memory check runs every MEMORY_CHECK_INTERVAL iterations", () => {
|
|
25
|
+
assert.match(loopSource, /iteration\s*%\s*MEMORY_CHECK_INTERVAL\s*===\s*0/);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test("memory pressure triggers graceful stopAuto", () => {
|
|
29
|
+
assert.match(loopSource, /mem\.pressured/);
|
|
30
|
+
assert.match(loopSource, /Stopping gracefully to prevent OOM/);
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
describe("stuck detection persistence (#3704)", () => {
|
|
35
|
+
test("loadStuckState function exists", () => {
|
|
36
|
+
assert.match(loopSource, /function loadStuckState/);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test("saveStuckState function exists", () => {
|
|
40
|
+
assert.match(loopSource, /function saveStuckState/);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test("loopState initialized from persisted state", () => {
|
|
44
|
+
assert.match(loopSource, /loadStuckState\(s\.basePath\)/);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test("stuck state saved after each iteration", () => {
|
|
48
|
+
assert.match(loopSource, /saveStuckState\(s\.basePath,\s*loopState\)/);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test("stuck state file path uses runtime directory", () => {
|
|
52
|
+
assert.match(loopSource, /stuck-state\.json/);
|
|
53
|
+
});
|
|
54
|
+
});
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { test } from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { tmpdir } from "node:os";
|
|
6
|
+
|
|
7
|
+
import { verifyExpectedArtifact } from "../auto-recovery.ts";
|
|
8
|
+
|
|
9
|
+
function createFixtureBase(): string {
|
|
10
|
+
const base = mkdtempSync(join(tmpdir(), "gsd-plan-milestone-artifact-"));
|
|
11
|
+
mkdirSync(join(base, ".gsd", "milestones"), { recursive: true });
|
|
12
|
+
return base;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function writeRoadmap(base: string, milestoneId: string, content: string): void {
|
|
16
|
+
const milestoneDir = join(base, ".gsd", "milestones", milestoneId);
|
|
17
|
+
mkdirSync(milestoneDir, { recursive: true });
|
|
18
|
+
writeFileSync(join(milestoneDir, `${milestoneId}-ROADMAP.md`), content, "utf-8");
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
test("#3405: plan-milestone roadmap stub does not count as a verified artifact", () => {
|
|
22
|
+
const base = createFixtureBase();
|
|
23
|
+
try {
|
|
24
|
+
writeRoadmap(base, "M001", [
|
|
25
|
+
"# M001: Placeholder",
|
|
26
|
+
"",
|
|
27
|
+
"**Vision:** Stub only.",
|
|
28
|
+
"",
|
|
29
|
+
"## Slices",
|
|
30
|
+
"",
|
|
31
|
+
"_TBD_",
|
|
32
|
+
"",
|
|
33
|
+
].join("\n"));
|
|
34
|
+
|
|
35
|
+
const result = verifyExpectedArtifact("plan-milestone", "M001", base);
|
|
36
|
+
assert.equal(result, false, "zero-slice roadmap stubs must fail verification");
|
|
37
|
+
} finally {
|
|
38
|
+
rmSync(base, { recursive: true, force: true });
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test("#3405: plan-milestone roadmap with real slices still passes artifact verification", () => {
|
|
43
|
+
const base = createFixtureBase();
|
|
44
|
+
try {
|
|
45
|
+
writeRoadmap(base, "M001", [
|
|
46
|
+
"# M001: Real roadmap",
|
|
47
|
+
"",
|
|
48
|
+
"**Vision:** Real work.",
|
|
49
|
+
"",
|
|
50
|
+
"## Slices",
|
|
51
|
+
"",
|
|
52
|
+
"- [ ] **S01: First slice** `risk:low` `depends:[]`",
|
|
53
|
+
" > After this: a real slice exists.",
|
|
54
|
+
"",
|
|
55
|
+
].join("\n"));
|
|
56
|
+
|
|
57
|
+
const result = verifyExpectedArtifact("plan-milestone", "M001", base);
|
|
58
|
+
assert.equal(result, true, "real roadmap slices should keep passing verification");
|
|
59
|
+
} finally {
|
|
60
|
+
rmSync(base, { recursive: true, force: true });
|
|
61
|
+
}
|
|
62
|
+
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Regression test for #3869: normal post-unit flow should rebuild STATE.md
|
|
3
|
+
* before syncing worktree state back to the project root.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import test from "node:test";
|
|
7
|
+
import assert from "node:assert/strict";
|
|
8
|
+
import { readFileSync } from "node:fs";
|
|
9
|
+
import { join } from "node:path";
|
|
10
|
+
|
|
11
|
+
const source = readFileSync(join(import.meta.dirname, "..", "auto-post-unit.ts"), "utf-8");
|
|
12
|
+
|
|
13
|
+
test("auto-post-unit imports rebuildState", () => {
|
|
14
|
+
assert.ok(
|
|
15
|
+
source.includes('import { rebuildState } from "./doctor.js";'),
|
|
16
|
+
"auto-post-unit.ts should import rebuildState from doctor.ts",
|
|
17
|
+
);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
test("postUnitPreVerification rebuilds STATE.md before worktree sync", () => {
|
|
21
|
+
const fnStart = source.indexOf("export async function postUnitPreVerification");
|
|
22
|
+
assert.ok(fnStart > 0, "postUnitPreVerification should exist");
|
|
23
|
+
|
|
24
|
+
const section = source.slice(fnStart, fnStart + 8000);
|
|
25
|
+
const rebuildIdx = section.indexOf('await runSafely("postUnit", "state-rebuild"');
|
|
26
|
+
const syncIdx = section.indexOf('await runSafely("postUnit", "worktree-sync"');
|
|
27
|
+
|
|
28
|
+
assert.ok(rebuildIdx > 0, "postUnitPreVerification should rebuild STATE.md after unit completion");
|
|
29
|
+
assert.ok(syncIdx > 0, "postUnitPreVerification should sync worktree state back to the project root");
|
|
30
|
+
assert.ok(
|
|
31
|
+
rebuildIdx < syncIdx,
|
|
32
|
+
"STATE.md rebuild should happen before worktree sync so synced state is fresh",
|
|
33
|
+
);
|
|
34
|
+
});
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for formatSkillRef — pure formatting logic for skill references
|
|
3
|
+
* in the system prompt. Moved from preferences-skills.ts to preferences-types.ts
|
|
4
|
+
* to break the preferences ↔ preferences-skills circular dependency.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { describe, test } from "node:test";
|
|
8
|
+
import assert from "node:assert/strict";
|
|
9
|
+
|
|
10
|
+
import { formatSkillRef } from "../preferences-types.ts";
|
|
11
|
+
import type { SkillResolution } from "../preferences-types.ts";
|
|
12
|
+
|
|
13
|
+
function makeResolutions(entries: [string, Partial<SkillResolution>][]): Map<string, SkillResolution> {
|
|
14
|
+
const map = new Map<string, SkillResolution>();
|
|
15
|
+
for (const [key, partial] of entries) {
|
|
16
|
+
map.set(key, {
|
|
17
|
+
original: partial.original ?? key,
|
|
18
|
+
resolvedPath: partial.resolvedPath ?? null,
|
|
19
|
+
method: partial.method ?? "unresolved",
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
return map;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
describe("formatSkillRef", () => {
|
|
26
|
+
test("marks unresolved references with a warning", () => {
|
|
27
|
+
const resolutions = makeResolutions([
|
|
28
|
+
["my-skill", { method: "unresolved" }],
|
|
29
|
+
]);
|
|
30
|
+
const result = formatSkillRef("my-skill", resolutions);
|
|
31
|
+
assert.match(result, /my-skill/);
|
|
32
|
+
assert.match(result, /not found/);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test("marks unknown references (not in map) with a warning", () => {
|
|
36
|
+
const resolutions = new Map<string, SkillResolution>();
|
|
37
|
+
const result = formatSkillRef("unknown-skill", resolutions);
|
|
38
|
+
assert.match(result, /unknown-skill/);
|
|
39
|
+
assert.match(result, /not found/);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test("returns bare ref for absolute-path resolution", () => {
|
|
43
|
+
const resolutions = makeResolutions([
|
|
44
|
+
["/home/user/skills/SKILL.md", {
|
|
45
|
+
method: "absolute-path",
|
|
46
|
+
resolvedPath: "/home/user/skills/SKILL.md",
|
|
47
|
+
}],
|
|
48
|
+
]);
|
|
49
|
+
const result = formatSkillRef("/home/user/skills/SKILL.md", resolutions);
|
|
50
|
+
assert.equal(result, "/home/user/skills/SKILL.md");
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test("returns bare ref for absolute-dir resolution", () => {
|
|
54
|
+
const resolutions = makeResolutions([
|
|
55
|
+
["/home/user/skills/my-skill", {
|
|
56
|
+
method: "absolute-dir",
|
|
57
|
+
resolvedPath: "/home/user/skills/my-skill/SKILL.md",
|
|
58
|
+
}],
|
|
59
|
+
]);
|
|
60
|
+
const result = formatSkillRef("/home/user/skills/my-skill", resolutions);
|
|
61
|
+
assert.equal(result, "/home/user/skills/my-skill");
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test("shows resolved path for user-skill resolution", () => {
|
|
65
|
+
const resolutions = makeResolutions([
|
|
66
|
+
["code-review", {
|
|
67
|
+
method: "user-skill",
|
|
68
|
+
resolvedPath: "/home/user/.claude/skills/code-review/SKILL.md",
|
|
69
|
+
}],
|
|
70
|
+
]);
|
|
71
|
+
const result = formatSkillRef("code-review", resolutions);
|
|
72
|
+
assert.match(result, /code-review/);
|
|
73
|
+
assert.match(result, /\.claude\/skills\/code-review\/SKILL\.md/);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test("shows resolved path for project-skill resolution", () => {
|
|
77
|
+
const resolutions = makeResolutions([
|
|
78
|
+
["lint-fix", {
|
|
79
|
+
method: "project-skill",
|
|
80
|
+
resolvedPath: "/repo/.gsd/skills/lint-fix/SKILL.md",
|
|
81
|
+
}],
|
|
82
|
+
]);
|
|
83
|
+
const result = formatSkillRef("lint-fix", resolutions);
|
|
84
|
+
assert.match(result, /lint-fix/);
|
|
85
|
+
assert.match(result, /\.gsd\/skills\/lint-fix\/SKILL\.md/);
|
|
86
|
+
});
|
|
87
|
+
});
|
|
@@ -10,10 +10,14 @@
|
|
|
10
10
|
|
|
11
11
|
import test from "node:test";
|
|
12
12
|
import assert from "node:assert/strict";
|
|
13
|
+
import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
14
|
+
import { tmpdir } from "node:os";
|
|
15
|
+
import { join } from "node:path";
|
|
13
16
|
import {
|
|
14
17
|
validatePreferences,
|
|
15
18
|
applyModeDefaults,
|
|
16
19
|
getIsolationMode,
|
|
20
|
+
loadEffectiveGSDPreferences,
|
|
17
21
|
parsePreferencesMarkdown,
|
|
18
22
|
_resetParseWarningFlag,
|
|
19
23
|
} from "../preferences.ts";
|
|
@@ -501,6 +505,55 @@ test("experimental.rtk parses correctly from preferences markdown", () => {
|
|
|
501
505
|
assert.equal(prefs!.experimental?.rtk, true);
|
|
502
506
|
});
|
|
503
507
|
|
|
508
|
+
test("loadEffectiveGSDPreferences preserves experimental prefs across global+project merge", () => {
|
|
509
|
+
const originalCwd = process.cwd();
|
|
510
|
+
const originalGsdHome = process.env.GSD_HOME;
|
|
511
|
+
const tempProject = mkdtempSync(join(tmpdir(), "gsd-prefs-project-"));
|
|
512
|
+
const tempGsdHome = mkdtempSync(join(tmpdir(), "gsd-prefs-home-"));
|
|
513
|
+
|
|
514
|
+
try {
|
|
515
|
+
mkdirSync(join(tempProject, ".gsd"), { recursive: true });
|
|
516
|
+
|
|
517
|
+
writeFileSync(
|
|
518
|
+
join(tempGsdHome, "preferences.md"),
|
|
519
|
+
[
|
|
520
|
+
"---",
|
|
521
|
+
"version: 1",
|
|
522
|
+
"experimental:",
|
|
523
|
+
" rtk: true",
|
|
524
|
+
"---",
|
|
525
|
+
].join("\n"),
|
|
526
|
+
"utf-8",
|
|
527
|
+
);
|
|
528
|
+
|
|
529
|
+
writeFileSync(
|
|
530
|
+
join(tempProject, ".gsd", "PREFERENCES.md"),
|
|
531
|
+
[
|
|
532
|
+
"---",
|
|
533
|
+
"version: 1",
|
|
534
|
+
"git:",
|
|
535
|
+
" isolation: none",
|
|
536
|
+
"---",
|
|
537
|
+
].join("\n"),
|
|
538
|
+
"utf-8",
|
|
539
|
+
);
|
|
540
|
+
|
|
541
|
+
process.env.GSD_HOME = tempGsdHome;
|
|
542
|
+
process.chdir(tempProject);
|
|
543
|
+
|
|
544
|
+
const loaded = loadEffectiveGSDPreferences();
|
|
545
|
+
assert.notEqual(loaded, null);
|
|
546
|
+
assert.equal(loaded!.preferences.experimental?.rtk, true);
|
|
547
|
+
assert.equal(loaded!.preferences.git?.isolation, "none");
|
|
548
|
+
} finally {
|
|
549
|
+
process.chdir(originalCwd);
|
|
550
|
+
if (originalGsdHome === undefined) delete process.env.GSD_HOME;
|
|
551
|
+
else process.env.GSD_HOME = originalGsdHome;
|
|
552
|
+
rmSync(tempProject, { recursive: true, force: true });
|
|
553
|
+
rmSync(tempGsdHome, { recursive: true, force: true });
|
|
554
|
+
}
|
|
555
|
+
});
|
|
556
|
+
|
|
504
557
|
test("experimental.rtk defaults to off in new project preferences", () => {
|
|
505
558
|
// No experimental key → feature is disabled
|
|
506
559
|
const content = "---\nversion: 1\n---\n";
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import test from 'node:test';
|
|
6
6
|
import assert from 'node:assert/strict';
|
|
7
7
|
|
|
8
|
-
import { renderPlanContent, renderRoadmapContent } from '../workflow-projections.ts';
|
|
8
|
+
import { renderPlanContent, renderRoadmapContent, renderSummaryContent } from '../workflow-projections.ts';
|
|
9
9
|
import type { SliceRow, TaskRow } from '../gsd-db.ts';
|
|
10
10
|
|
|
11
11
|
// ─── Helpers ─────────────────────────────────────────────────────────────
|
|
@@ -172,3 +172,98 @@ test('renderRoadmapContent: slice with status "pending" shows ⬜', () => {
|
|
|
172
172
|
|
|
173
173
|
assert.ok(content.includes('⬜'), 'pending slice should show ⬜');
|
|
174
174
|
});
|
|
175
|
+
|
|
176
|
+
// ─── renderSummaryContent: double-frontmatter regression ─────────────────
|
|
177
|
+
|
|
178
|
+
test('renderSummaryContent: uses full_summary_md as-is when it contains frontmatter', () => {
|
|
179
|
+
const existingSummary = [
|
|
180
|
+
'---',
|
|
181
|
+
'id: T01',
|
|
182
|
+
'parent: S01',
|
|
183
|
+
'milestone: M001',
|
|
184
|
+
'key_files:',
|
|
185
|
+
' - src/thing.ts',
|
|
186
|
+
'verification_result: passed',
|
|
187
|
+
'completed_at: 2026-01-01T00:00:00Z',
|
|
188
|
+
'blocker_discovered: false',
|
|
189
|
+
'---',
|
|
190
|
+
'',
|
|
191
|
+
'# T01: Did the thing',
|
|
192
|
+
'',
|
|
193
|
+
'**One-liner summary**',
|
|
194
|
+
'',
|
|
195
|
+
'## What Happened',
|
|
196
|
+
'',
|
|
197
|
+
'Narrative content here.',
|
|
198
|
+
'',
|
|
199
|
+
'## Deviations',
|
|
200
|
+
'',
|
|
201
|
+
'None.',
|
|
202
|
+
'',
|
|
203
|
+
].join('\n');
|
|
204
|
+
|
|
205
|
+
const task = makeTaskRow({
|
|
206
|
+
id: 'T01',
|
|
207
|
+
status: 'complete',
|
|
208
|
+
title: 'Did the thing',
|
|
209
|
+
one_liner: 'One-liner summary',
|
|
210
|
+
narrative: 'Narrative content here.',
|
|
211
|
+
full_summary_md: existingSummary,
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
const result = renderSummaryContent(task, 'S01', 'M001');
|
|
215
|
+
|
|
216
|
+
// Must NOT produce double frontmatter
|
|
217
|
+
const frontmatterCount = (result.match(/^---$/gm) || []).length;
|
|
218
|
+
assert.equal(frontmatterCount, 2, `Expected exactly 2 frontmatter delimiters (one block), got ${frontmatterCount}`);
|
|
219
|
+
|
|
220
|
+
// Must NOT produce double H1 heading
|
|
221
|
+
const h1Count = (result.match(/^# T01:/gm) || []).length;
|
|
222
|
+
assert.equal(h1Count, 1, `Expected exactly 1 H1 heading, got ${h1Count}`);
|
|
223
|
+
|
|
224
|
+
// Content should match the full_summary_md exactly
|
|
225
|
+
assert.equal(result, existingSummary);
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
test('renderSummaryContent: synthesizes from DB columns when full_summary_md is empty', () => {
|
|
229
|
+
const task = makeTaskRow({
|
|
230
|
+
id: 'T01',
|
|
231
|
+
status: 'complete',
|
|
232
|
+
title: 'Did the thing',
|
|
233
|
+
one_liner: 'One-liner summary',
|
|
234
|
+
narrative: 'Built the feature.',
|
|
235
|
+
full_summary_md: '',
|
|
236
|
+
deviations: 'Deviated slightly.',
|
|
237
|
+
known_issues: 'None.',
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
const result = renderSummaryContent(task, 'S01', 'M001');
|
|
241
|
+
|
|
242
|
+
// Should have exactly one frontmatter block
|
|
243
|
+
const frontmatterCount = (result.match(/^---$/gm) || []).length;
|
|
244
|
+
assert.equal(frontmatterCount, 2, 'Should have one frontmatter block (2 delimiters)');
|
|
245
|
+
|
|
246
|
+
// Should contain synthesized sections
|
|
247
|
+
assert.ok(result.includes('## What Happened'), 'Should have What Happened section');
|
|
248
|
+
assert.ok(result.includes('Built the feature.'), 'Should use narrative for content');
|
|
249
|
+
assert.ok(result.includes('## Deviations'), 'Should have Deviations section');
|
|
250
|
+
assert.ok(result.includes('Deviated slightly.'), 'Should include deviation text');
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
test('renderSummaryContent: synthesizes when full_summary_md has no frontmatter', () => {
|
|
254
|
+
const task = makeTaskRow({
|
|
255
|
+
id: 'T02',
|
|
256
|
+
status: 'complete',
|
|
257
|
+
title: 'Partial summary',
|
|
258
|
+
narrative: 'Did some work.',
|
|
259
|
+
full_summary_md: 'Just a plain text summary with no frontmatter.',
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
const result = renderSummaryContent(task, 'S01', 'M001');
|
|
263
|
+
|
|
264
|
+
// Should synthesize with proper frontmatter since the stored md lacks it
|
|
265
|
+
assert.ok(result.startsWith('---'), 'Should start with frontmatter');
|
|
266
|
+
assert.ok(result.includes('id: T02'), 'Should have task ID in frontmatter');
|
|
267
|
+
assert.ok(result.includes('## What Happened'), 'Should have What Happened section');
|
|
268
|
+
assert.ok(result.includes('Did some work.'), 'Should use narrative');
|
|
269
|
+
});
|