gsd-pi 2.66.1-dev.ed243f2 → 2.67.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/dist/claude-cli-check.d.ts +8 -0
- package/dist/claude-cli-check.js +36 -0
- package/dist/cli.js +40 -0
- package/dist/onboarding.js +19 -2
- package/dist/resources/extensions/ask-user-questions.js +79 -11
- package/dist/resources/extensions/claude-code-cli/partial-builder.js +4 -3
- package/dist/resources/extensions/claude-code-cli/readiness.js +63 -12
- package/dist/resources/extensions/claude-code-cli/stream-adapter.js +10 -3
- package/dist/resources/extensions/gsd/auto/loop.js +13 -1
- package/dist/resources/extensions/gsd/auto/phases.js +22 -3
- package/dist/resources/extensions/gsd/auto/run-unit.js +10 -2
- package/dist/resources/extensions/gsd/auto/session.js +1 -1
- package/dist/resources/extensions/gsd/auto-dashboard.js +65 -15
- package/dist/resources/extensions/gsd/auto-dispatch.js +30 -28
- package/dist/resources/extensions/gsd/auto-model-selection.js +12 -3
- package/dist/resources/extensions/gsd/auto-prompts.js +173 -25
- package/dist/resources/extensions/gsd/auto-recovery.js +11 -12
- package/dist/resources/extensions/gsd/auto.js +13 -1
- package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +32 -1
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +18 -6
- package/dist/resources/extensions/gsd/bootstrap/provider-error-resume.js +5 -0
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +59 -5
- package/dist/resources/extensions/gsd/bootstrap/register-shortcuts.js +8 -5
- package/dist/resources/extensions/gsd/bootstrap/write-gate.js +186 -14
- package/dist/resources/extensions/gsd/codebase-generator.js +4 -0
- package/dist/resources/extensions/gsd/commands/handlers/core.js +3 -3
- package/dist/resources/extensions/gsd/commands-prefs-wizard.js +10 -4
- package/dist/resources/extensions/gsd/context-store.js +134 -2
- package/dist/resources/extensions/gsd/custom-workflow-engine.js +3 -1
- package/dist/resources/extensions/gsd/detection.js +6 -0
- package/dist/resources/extensions/gsd/files.js +19 -2
- package/dist/resources/extensions/gsd/guided-flow.js +12 -8
- package/dist/resources/extensions/gsd/index.js +1 -1
- package/dist/resources/extensions/gsd/parallel-monitor-overlay.js +2 -0
- package/dist/resources/extensions/gsd/parsers-legacy.js +3 -1
- package/dist/resources/extensions/gsd/preferences.js +6 -1
- package/dist/resources/extensions/gsd/prompts/complete-milestone.md +1 -1
- package/dist/resources/extensions/gsd/prompts/discuss-prepared.md +7 -7
- package/dist/resources/extensions/gsd/prompts/discuss.md +3 -3
- package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +3 -3
- package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +3 -1
- package/dist/resources/extensions/gsd/prompts/rethink.md +6 -2
- package/dist/resources/extensions/gsd/prompts/system.md +1 -1
- package/dist/resources/extensions/gsd/prompts/triage-captures.md +1 -1
- package/dist/resources/extensions/gsd/prompts/validate-milestone.md +4 -4
- package/dist/resources/extensions/gsd/prompts/worktree-merge.md +3 -1
- package/dist/resources/extensions/gsd/safety/file-change-validator.js +2 -1
- package/dist/resources/extensions/gsd/state.js +2 -1
- package/dist/resources/extensions/gsd/visualizer-overlay.js +27 -26
- package/dist/resources/extensions/gsd/workflow-reconcile.js +46 -7
- package/dist/resources/extensions/remote-questions/manager.js +8 -0
- package/dist/resources/extensions/shared/interview-ui.js +10 -0
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +14 -14
- 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 +2 -2
- 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 +14 -14
- package/dist/web/standalone/.next/server/chunks/6897.js +1 -1
- package/dist/web/standalone/.next/server/chunks/7471.js +3 -3
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware.js +2 -2
- package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
- package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +2 -2
- package/dist/web/standalone/.next/server/server-reference-manifest.json +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-62be3b5fa91e4c8f.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/providers/anthropic-shared.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic-shared.js +4 -3
- package/packages/pi-ai/dist/providers/anthropic-shared.js.map +1 -1
- package/packages/pi-ai/dist/utils/json-parse.d.ts.map +1 -1
- package/packages/pi-ai/dist/utils/json-parse.js +11 -1
- package/packages/pi-ai/dist/utils/json-parse.js.map +1 -1
- package/packages/pi-ai/dist/utils/repair-tool-json.d.ts.map +1 -1
- package/packages/pi-ai/dist/utils/repair-tool-json.js +60 -1
- package/packages/pi-ai/dist/utils/repair-tool-json.js.map +1 -1
- package/packages/pi-ai/dist/utils/tests/json-parse.test.d.ts +2 -0
- package/packages/pi-ai/dist/utils/tests/json-parse.test.d.ts.map +1 -0
- package/packages/pi-ai/dist/utils/tests/json-parse.test.js +14 -0
- package/packages/pi-ai/dist/utils/tests/json-parse.test.js.map +1 -0
- package/packages/pi-ai/dist/utils/tests/repair-tool-json.test.js +10 -0
- package/packages/pi-ai/dist/utils/tests/repair-tool-json.test.js.map +1 -1
- package/packages/pi-ai/src/providers/anthropic-shared.ts +4 -3
- package/packages/pi-ai/src/utils/json-parse.ts +11 -1
- package/packages/pi-ai/src/utils/repair-tool-json.ts +69 -1
- package/packages/pi-ai/src/utils/tests/json-parse.test.ts +17 -0
- package/packages/pi-ai/src/utils/tests/repair-tool-json.test.ts +13 -0
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts +3 -0
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.js +1 -0
- package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/retry-handler.d.ts +16 -0
- package/packages/pi-coding-agent/dist/core/retry-handler.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/retry-handler.js +58 -1
- package/packages/pi-coding-agent/dist/core/retry-handler.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/retry-handler.test.js +58 -0
- package/packages/pi-coding-agent/dist/core/retry-handler.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/sdk.d.ts +3 -0
- package/packages/pi-coding-agent/dist/core/sdk.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/sdk.js +1 -0
- package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/provider-display-name.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/provider-display-name.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/provider-display-name.test.js +17 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/provider-display-name.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js +2 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.d.ts +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.js +9 -2
- package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.js +2 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/scoped-models-selector.js +2 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/scoped-models-selector.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +2 -2
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/package.json +1 -1
- package/packages/pi-coding-agent/src/core/agent-session.ts +4 -0
- package/packages/pi-coding-agent/src/core/retry-handler.test.ts +69 -0
- package/packages/pi-coding-agent/src/core/retry-handler.ts +66 -1
- package/packages/pi-coding-agent/src/core/sdk.ts +5 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/provider-display-name.test.ts +18 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/footer.ts +2 -1
- package/packages/pi-coding-agent/src/modes/interactive/components/model-selector.ts +11 -2
- package/packages/pi-coding-agent/src/modes/interactive/components/provider-manager.ts +2 -1
- package/packages/pi-coding-agent/src/modes/interactive/components/scoped-models-selector.ts +2 -1
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +2 -2
- package/packages/pi-tui/dist/__tests__/autocomplete.test.js +13 -0
- package/packages/pi-tui/dist/__tests__/autocomplete.test.js.map +1 -1
- package/packages/pi-tui/dist/__tests__/stdin-buffer.test.d.ts +2 -0
- package/packages/pi-tui/dist/__tests__/stdin-buffer.test.d.ts.map +1 -0
- package/packages/pi-tui/dist/__tests__/stdin-buffer.test.js +35 -0
- package/packages/pi-tui/dist/__tests__/stdin-buffer.test.js.map +1 -0
- package/packages/pi-tui/dist/__tests__/tui.test.d.ts +2 -0
- package/packages/pi-tui/dist/__tests__/tui.test.d.ts.map +1 -0
- package/packages/pi-tui/dist/__tests__/tui.test.js +43 -0
- package/packages/pi-tui/dist/__tests__/tui.test.js.map +1 -0
- package/packages/pi-tui/dist/autocomplete.d.ts.map +1 -1
- package/packages/pi-tui/dist/autocomplete.js +9 -7
- package/packages/pi-tui/dist/autocomplete.js.map +1 -1
- package/packages/pi-tui/dist/components/__tests__/editor.test.d.ts +2 -0
- package/packages/pi-tui/dist/components/__tests__/editor.test.d.ts.map +1 -0
- package/packages/pi-tui/dist/components/__tests__/editor.test.js +54 -0
- package/packages/pi-tui/dist/components/__tests__/editor.test.js.map +1 -0
- package/packages/pi-tui/dist/components/editor.d.ts +3 -1
- package/packages/pi-tui/dist/components/editor.d.ts.map +1 -1
- package/packages/pi-tui/dist/components/editor.js +14 -3
- package/packages/pi-tui/dist/components/editor.js.map +1 -1
- package/packages/pi-tui/dist/stdin-buffer.d.ts.map +1 -1
- package/packages/pi-tui/dist/stdin-buffer.js +6 -0
- package/packages/pi-tui/dist/stdin-buffer.js.map +1 -1
- package/packages/pi-tui/dist/tui.d.ts.map +1 -1
- package/packages/pi-tui/dist/tui.js +8 -0
- package/packages/pi-tui/dist/tui.js.map +1 -1
- package/packages/pi-tui/src/__tests__/autocomplete.test.ts +15 -0
- package/packages/pi-tui/src/__tests__/stdin-buffer.test.ts +43 -0
- package/packages/pi-tui/src/__tests__/tui.test.ts +50 -0
- package/packages/pi-tui/src/autocomplete.ts +9 -7
- package/packages/pi-tui/src/components/__tests__/editor.test.ts +64 -0
- package/packages/pi-tui/src/components/editor.ts +14 -3
- package/packages/pi-tui/src/stdin-buffer.ts +7 -0
- package/packages/pi-tui/src/tui.ts +9 -0
- package/pkg/package.json +1 -1
- package/src/resources/extensions/ask-user-questions.ts +103 -11
- package/src/resources/extensions/claude-code-cli/partial-builder.ts +4 -3
- package/src/resources/extensions/claude-code-cli/readiness.ts +67 -12
- package/src/resources/extensions/claude-code-cli/stream-adapter.ts +12 -3
- package/src/resources/extensions/claude-code-cli/tests/partial-builder.test.ts +17 -0
- package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +18 -0
- package/src/resources/extensions/gsd/auto/loop-deps.ts +2 -1
- package/src/resources/extensions/gsd/auto/loop.ts +14 -1
- package/src/resources/extensions/gsd/auto/phases.ts +27 -4
- package/src/resources/extensions/gsd/auto/run-unit.ts +14 -2
- package/src/resources/extensions/gsd/auto/session.ts +1 -1
- package/src/resources/extensions/gsd/auto-dashboard.ts +76 -16
- package/src/resources/extensions/gsd/auto-dispatch.ts +36 -35
- package/src/resources/extensions/gsd/auto-model-selection.ts +12 -3
- package/src/resources/extensions/gsd/auto-prompts.ts +195 -25
- package/src/resources/extensions/gsd/auto-recovery.ts +15 -15
- package/src/resources/extensions/gsd/auto.ts +12 -1
- package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +34 -1
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +27 -6
- package/src/resources/extensions/gsd/bootstrap/provider-error-resume.ts +6 -0
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +67 -6
- package/src/resources/extensions/gsd/bootstrap/register-shortcuts.ts +11 -8
- package/src/resources/extensions/gsd/bootstrap/write-gate.ts +209 -16
- package/src/resources/extensions/gsd/codebase-generator.ts +4 -0
- package/src/resources/extensions/gsd/commands/handlers/core.ts +6 -6
- package/src/resources/extensions/gsd/commands-prefs-wizard.ts +11 -4
- package/src/resources/extensions/gsd/context-store.ts +167 -2
- package/src/resources/extensions/gsd/custom-workflow-engine.ts +3 -1
- package/src/resources/extensions/gsd/detection.ts +6 -0
- package/src/resources/extensions/gsd/files.ts +21 -2
- package/src/resources/extensions/gsd/guided-flow.ts +15 -8
- package/src/resources/extensions/gsd/index.ts +6 -0
- package/src/resources/extensions/gsd/parallel-monitor-overlay.ts +2 -0
- package/src/resources/extensions/gsd/parsers-legacy.ts +3 -1
- package/src/resources/extensions/gsd/preferences.ts +6 -1
- package/src/resources/extensions/gsd/prompts/complete-milestone.md +1 -1
- package/src/resources/extensions/gsd/prompts/discuss-prepared.md +7 -7
- package/src/resources/extensions/gsd/prompts/discuss.md +3 -3
- package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +3 -3
- package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +3 -1
- package/src/resources/extensions/gsd/prompts/rethink.md +6 -2
- package/src/resources/extensions/gsd/prompts/system.md +1 -1
- package/src/resources/extensions/gsd/prompts/triage-captures.md +1 -1
- package/src/resources/extensions/gsd/prompts/validate-milestone.md +4 -4
- package/src/resources/extensions/gsd/prompts/worktree-merge.md +3 -1
- package/src/resources/extensions/gsd/safety/file-change-validator.ts +4 -1
- package/src/resources/extensions/gsd/state.ts +2 -1
- package/src/resources/extensions/gsd/tests/auto-dashboard.test.ts +52 -1
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +50 -2
- package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +21 -7
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +48 -0
- package/src/resources/extensions/gsd/tests/codebase-generator.test.ts +22 -0
- package/src/resources/extensions/gsd/tests/context-store.test.ts +176 -0
- package/src/resources/extensions/gsd/tests/core-overlay-fallback.test.ts +44 -0
- package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +7 -1
- package/src/resources/extensions/gsd/tests/custom-workflow-engine.test.ts +31 -0
- package/src/resources/extensions/gsd/tests/decision-scope-cascade.test.ts +370 -0
- package/src/resources/extensions/gsd/tests/detection.test.ts +37 -0
- package/src/resources/extensions/gsd/tests/file-change-validator.test.ts +50 -0
- package/src/resources/extensions/gsd/tests/gsd-tools.test.ts +35 -0
- package/src/resources/extensions/gsd/tests/guided-flow-session-isolation.test.ts +34 -0
- package/src/resources/extensions/gsd/tests/health-widget.test.ts +45 -0
- package/src/resources/extensions/gsd/tests/integration/auto-recovery.test.ts +53 -13
- package/src/resources/extensions/gsd/tests/integration/state-machine-runtime-failures.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/journal-integration.test.ts +3 -3
- package/src/resources/extensions/gsd/tests/measurement.test.ts +531 -0
- package/src/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +3 -4
- package/src/resources/extensions/gsd/tests/parallel-monitor-overlay.test.ts +21 -0
- package/src/resources/extensions/gsd/tests/parallel-research-dispatch.test.ts +71 -2
- package/src/resources/extensions/gsd/tests/parsers.test.ts +25 -0
- package/src/resources/extensions/gsd/tests/preferences.test.ts +20 -0
- package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +8 -1
- package/src/resources/extensions/gsd/tests/provider-errors.test.ts +60 -0
- package/src/resources/extensions/gsd/tests/queue-execution-guard.test.ts +9 -0
- package/src/resources/extensions/gsd/tests/reactive-graph.test.ts +19 -0
- package/src/resources/extensions/gsd/tests/register-shortcuts.test.ts +73 -0
- package/src/resources/extensions/gsd/tests/remote-questions.test.ts +98 -0
- package/src/resources/extensions/gsd/tests/smart-entry-complete.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +26 -0
- package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +59 -0
- package/src/resources/extensions/gsd/tests/workflow-reconcile.test.ts +91 -0
- package/src/resources/extensions/gsd/tests/write-gate.test.ts +210 -35
- package/src/resources/extensions/gsd/visualizer-overlay.ts +31 -27
- package/src/resources/extensions/gsd/workflow-reconcile.ts +59 -8
- package/src/resources/extensions/remote-questions/manager.ts +9 -0
- package/src/resources/extensions/shared/interview-ui.ts +13 -0
- package/dist/web/standalone/.next/static/chunks/app/page-0c485498795110d6.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/dist/web/standalone/.next/static/{HAq0VE4k68rhRvJbQL1VW → DFZllMYDbO0OwyS6FSvm5}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{HAq0VE4k68rhRvJbQL1VW → DFZllMYDbO0OwyS6FSvm5}/_ssgManifest.js +0 -0
|
@@ -15,6 +15,8 @@ import {
|
|
|
15
15
|
formatRequirementsForPrompt,
|
|
16
16
|
queryArtifact,
|
|
17
17
|
queryProject,
|
|
18
|
+
formatRoadmapExcerpt,
|
|
19
|
+
queryKnowledge,
|
|
18
20
|
} from '../context-store.ts';
|
|
19
21
|
|
|
20
22
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
@@ -452,3 +454,177 @@ describe("context-store: queryProject", () => {
|
|
|
452
454
|
assert.strictEqual(content, null, 'queryProject returns null when DB closed');
|
|
453
455
|
});
|
|
454
456
|
});
|
|
457
|
+
|
|
458
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
459
|
+
// context-store: formatRoadmapExcerpt
|
|
460
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
461
|
+
|
|
462
|
+
describe("context-store: formatRoadmapExcerpt", () => {
|
|
463
|
+
// Sample roadmap content matching actual M005-ROADMAP.md format
|
|
464
|
+
const sampleRoadmap = `# M005: Tiered Context Injection
|
|
465
|
+
|
|
466
|
+
## Vision
|
|
467
|
+
Refactor prompt builders to inject relevance-scoped context.
|
|
468
|
+
|
|
469
|
+
## Slice Overview
|
|
470
|
+
| ID | Slice | Risk | Depends | Done | After this |
|
|
471
|
+
|----|-------|------|---------|------|------------|
|
|
472
|
+
| S01 | Scope existing queries | low | — | ✅ | planSlice prompt scoped. |
|
|
473
|
+
| S02 | KNOWLEDGE scoping | medium | S01 | ⬜ | KNOWLEDGE sections filtered. |
|
|
474
|
+
| S03 | Measurement test | low | S02 | ⬜ | 40% reduction confirmed. |
|
|
475
|
+
`;
|
|
476
|
+
|
|
477
|
+
test("S02 with S01 predecessor includes both rows", () => {
|
|
478
|
+
const result = formatRoadmapExcerpt(sampleRoadmap, 'S02', '.gsd/milestones/M005/M005-ROADMAP.md');
|
|
479
|
+
|
|
480
|
+
// Should have header
|
|
481
|
+
assert.match(result, /\| ID \| Slice \| Risk \| Depends \| Done \| After this \|/, 'has header row');
|
|
482
|
+
// Should have separator
|
|
483
|
+
assert.match(result, /\|----\|/, 'has separator row');
|
|
484
|
+
// Should have S01 predecessor
|
|
485
|
+
assert.match(result, /\| S01 \|/, 'has predecessor S01 row');
|
|
486
|
+
// Should have S02 target
|
|
487
|
+
assert.match(result, /\| S02 \|/, 'has target S02 row');
|
|
488
|
+
// Should have reference directive
|
|
489
|
+
assert.match(result, /See full roadmap:.*M005-ROADMAP\.md/, 'has reference directive');
|
|
490
|
+
// Should NOT have S03 (not relevant)
|
|
491
|
+
assert.ok(!result.includes('| S03 |'), 'does not include unrelated S03');
|
|
492
|
+
});
|
|
493
|
+
|
|
494
|
+
test("S01 with no predecessor includes only target row", () => {
|
|
495
|
+
const result = formatRoadmapExcerpt(sampleRoadmap, 'S01');
|
|
496
|
+
|
|
497
|
+
// Should have header + separator + S01 only
|
|
498
|
+
assert.match(result, /\| ID \| Slice \|/, 'has header row');
|
|
499
|
+
assert.match(result, /\| S01 \|/, 'has target S01 row');
|
|
500
|
+
// Should NOT have S02 or S03
|
|
501
|
+
assert.ok(!result.includes('| S02 |'), 'does not include S02');
|
|
502
|
+
assert.ok(!result.includes('| S03 |'), 'does not include S03');
|
|
503
|
+
// Should have reference
|
|
504
|
+
assert.match(result, /See full roadmap:/, 'has reference directive');
|
|
505
|
+
|
|
506
|
+
// Count rows: header + separator + S01 + blank + directive = 5 lines
|
|
507
|
+
const lines = result.split('\n');
|
|
508
|
+
assert.strictEqual(lines.length, 5, 'correct number of lines (no predecessor)');
|
|
509
|
+
});
|
|
510
|
+
|
|
511
|
+
test("missing slice returns empty string", () => {
|
|
512
|
+
const result = formatRoadmapExcerpt(sampleRoadmap, 'S99');
|
|
513
|
+
|
|
514
|
+
assert.strictEqual(result, '', 'missing slice returns empty string');
|
|
515
|
+
});
|
|
516
|
+
|
|
517
|
+
test("empty input returns empty string", () => {
|
|
518
|
+
assert.strictEqual(formatRoadmapExcerpt('', 'S01'), '', 'empty content returns empty');
|
|
519
|
+
assert.strictEqual(formatRoadmapExcerpt(sampleRoadmap, ''), '', 'empty sliceId returns empty');
|
|
520
|
+
});
|
|
521
|
+
|
|
522
|
+
test("handles table with various column formats", () => {
|
|
523
|
+
// Table with different spacing and content
|
|
524
|
+
const variantRoadmap = `# Milestone
|
|
525
|
+
|
|
526
|
+
| ID | Slice | Risk | Depends | Done | After this |
|
|
527
|
+
|:---|:------|:-----|:--------|:-----|:-----------|
|
|
528
|
+
| S01 | First slice title | low | — | ✅ | First complete. |
|
|
529
|
+
| S02 | Second longer slice title here | medium | S01 | ⬜ | Second working. |
|
|
530
|
+
`;
|
|
531
|
+
|
|
532
|
+
const result = formatRoadmapExcerpt(variantRoadmap, 'S02');
|
|
533
|
+
|
|
534
|
+
assert.match(result, /\| S01 \|/, 'has predecessor with different spacing');
|
|
535
|
+
assert.match(result, /\| S02 \|/, 'has target with different spacing');
|
|
536
|
+
assert.match(result, /Second longer slice title/, 'preserves full slice title');
|
|
537
|
+
});
|
|
538
|
+
|
|
539
|
+
test("handles multiple dependencies by using first one", () => {
|
|
540
|
+
const multiDepRoadmap = `| ID | Slice | Risk | Depends | Done | After this |
|
|
541
|
+
|----|-------|------|---------|------|------------|
|
|
542
|
+
| S01 | First | low | — | ✅ | Done. |
|
|
543
|
+
| S02 | Second | low | — | ✅ | Done. |
|
|
544
|
+
| S03 | Third | medium | S01, S02 | ⬜ | Working. |
|
|
545
|
+
`;
|
|
546
|
+
|
|
547
|
+
const result = formatRoadmapExcerpt(multiDepRoadmap, 'S03');
|
|
548
|
+
|
|
549
|
+
// Should include S01 (first dependency) and S03
|
|
550
|
+
assert.match(result, /\| S01 \|/, 'has first dependency S01');
|
|
551
|
+
assert.match(result, /\| S03 \|/, 'has target S03');
|
|
552
|
+
// S02 is also a dependency but we only include the first one
|
|
553
|
+
// (This is intentional to keep excerpts minimal)
|
|
554
|
+
});
|
|
555
|
+
});
|
|
556
|
+
|
|
557
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
558
|
+
// context-store: queryKnowledge
|
|
559
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
560
|
+
|
|
561
|
+
describe("context-store: queryKnowledge", () => {
|
|
562
|
+
// Sample KNOWLEDGE.md content
|
|
563
|
+
const sampleKnowledge = `# Project Knowledge
|
|
564
|
+
|
|
565
|
+
## Database Patterns
|
|
566
|
+
SQLite is used with WAL mode for concurrent reads.
|
|
567
|
+
Always use prepared statements.
|
|
568
|
+
|
|
569
|
+
More database details here.
|
|
570
|
+
|
|
571
|
+
## API Design
|
|
572
|
+
REST endpoints follow OpenAPI spec.
|
|
573
|
+
Use versioned paths like /v1/resource.
|
|
574
|
+
|
|
575
|
+
## Testing Guidelines
|
|
576
|
+
Unit tests use node:test.
|
|
577
|
+
Integration tests mock external services.
|
|
578
|
+
`;
|
|
579
|
+
|
|
580
|
+
test("single keyword matches header", async () => {
|
|
581
|
+
const result = await queryKnowledge(sampleKnowledge, ['database']);
|
|
582
|
+
|
|
583
|
+
assert.match(result, /## Database Patterns/, 'includes matching section header');
|
|
584
|
+
assert.match(result, /SQLite is used with WAL mode/, 'includes section content');
|
|
585
|
+
// Should NOT include other sections
|
|
586
|
+
assert.ok(!result.includes('## API Design'), 'does not include non-matching API section');
|
|
587
|
+
assert.ok(!result.includes('## Testing Guidelines'), 'does not include non-matching Testing section');
|
|
588
|
+
});
|
|
589
|
+
|
|
590
|
+
test("multiple keywords match multiple sections", async () => {
|
|
591
|
+
const result = await queryKnowledge(sampleKnowledge, ['database', 'testing']);
|
|
592
|
+
|
|
593
|
+
assert.match(result, /## Database Patterns/, 'includes Database section');
|
|
594
|
+
assert.match(result, /## Testing Guidelines/, 'includes Testing section');
|
|
595
|
+
assert.ok(!result.includes('## API Design'), 'does not include API section');
|
|
596
|
+
});
|
|
597
|
+
|
|
598
|
+
test("no matches returns empty string", async () => {
|
|
599
|
+
const result = await queryKnowledge(sampleKnowledge, ['nonexistent']);
|
|
600
|
+
|
|
601
|
+
assert.strictEqual(result, '', 'no matches returns empty string per D020');
|
|
602
|
+
});
|
|
603
|
+
|
|
604
|
+
test("keyword in first paragraph matches", async () => {
|
|
605
|
+
const result = await queryKnowledge(sampleKnowledge, ['sqlite']);
|
|
606
|
+
|
|
607
|
+
// 'sqlite' appears in first paragraph of Database Patterns
|
|
608
|
+
assert.match(result, /## Database Patterns/, 'matches keyword in first paragraph');
|
|
609
|
+
assert.match(result, /SQLite is used/, 'includes the section with matching paragraph');
|
|
610
|
+
});
|
|
611
|
+
|
|
612
|
+
test("case-insensitive matching", async () => {
|
|
613
|
+
const result = await queryKnowledge(sampleKnowledge, ['DATABASE', 'API']);
|
|
614
|
+
|
|
615
|
+
assert.match(result, /## Database Patterns/, 'case-insensitive header match');
|
|
616
|
+
assert.match(result, /## API Design/, 'case-insensitive header match for API');
|
|
617
|
+
});
|
|
618
|
+
|
|
619
|
+
test("empty keywords returns empty string", async () => {
|
|
620
|
+
const result = await queryKnowledge(sampleKnowledge, []);
|
|
621
|
+
|
|
622
|
+
assert.strictEqual(result, '', 'empty keywords returns empty string');
|
|
623
|
+
});
|
|
624
|
+
|
|
625
|
+
test("empty content returns empty string", async () => {
|
|
626
|
+
const result = await queryKnowledge('', ['database']);
|
|
627
|
+
|
|
628
|
+
assert.strictEqual(result, '', 'empty content returns empty string');
|
|
629
|
+
});
|
|
630
|
+
});
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
|
|
4
|
+
import { handleCoreCommand } from "../commands/handlers/core.ts";
|
|
5
|
+
|
|
6
|
+
function makeCtx(customResult: unknown) {
|
|
7
|
+
const notices: Array<{ message: string; type?: string }> = [];
|
|
8
|
+
return {
|
|
9
|
+
hasUI: true,
|
|
10
|
+
ui: {
|
|
11
|
+
custom: async () => customResult,
|
|
12
|
+
notify: (message: string, type?: string) => {
|
|
13
|
+
notices.push({ message, type });
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
notices,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
test("visualize only falls back when ctx.ui.custom() is unavailable", async () => {
|
|
21
|
+
const successCtx = makeCtx(true);
|
|
22
|
+
const success = await handleCoreCommand("visualize", successCtx as any);
|
|
23
|
+
assert.equal(success, true);
|
|
24
|
+
assert.equal(successCtx.notices.length, 0, "successful overlay close does not trigger fallback");
|
|
25
|
+
|
|
26
|
+
const fallbackCtx = makeCtx(undefined);
|
|
27
|
+
const fallback = await handleCoreCommand("visualize", fallbackCtx as any);
|
|
28
|
+
assert.equal(fallback, true);
|
|
29
|
+
assert.equal(fallbackCtx.notices.length, 1, "unavailable overlay triggers fallback warning");
|
|
30
|
+
assert.match(fallbackCtx.notices[0]!.message, /interactive terminal/i);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test("show-config only falls back when ctx.ui.custom() is unavailable", async () => {
|
|
34
|
+
const successCtx = makeCtx(true);
|
|
35
|
+
const success = await handleCoreCommand("show-config", successCtx as any);
|
|
36
|
+
assert.equal(success, true);
|
|
37
|
+
assert.equal(successCtx.notices.length, 0, "successful overlay close does not trigger fallback");
|
|
38
|
+
|
|
39
|
+
const fallbackCtx = makeCtx(undefined);
|
|
40
|
+
const fallback = await handleCoreCommand("show-config", fallbackCtx as any);
|
|
41
|
+
assert.equal(fallback, true);
|
|
42
|
+
assert.equal(fallbackCtx.notices.length, 1, "unavailable overlay triggers text fallback");
|
|
43
|
+
assert.match(fallbackCtx.notices[0]!.message, /GSD Configuration/);
|
|
44
|
+
});
|
|
@@ -178,7 +178,7 @@ function makeMockDeps(overrides?: Partial<LoopDeps>): LoopDeps & { callLog: stri
|
|
|
178
178
|
getCurrentBranch: () => "main",
|
|
179
179
|
autoWorktreeBranch: () => "auto/M001",
|
|
180
180
|
resolveMilestoneFile: () => null,
|
|
181
|
-
reconcileMergeState: () =>
|
|
181
|
+
reconcileMergeState: () => "clean",
|
|
182
182
|
getLedger: () => null,
|
|
183
183
|
getProjectTotals: () => ({ cost: 0 }),
|
|
184
184
|
formatCost: (c: number) => `$${c.toFixed(2)}`,
|
|
@@ -311,6 +311,12 @@ describe("Custom engine loop integration", () => {
|
|
|
311
311
|
`stopAuto reason should include "Workflow complete", got: ${stopEntry}`,
|
|
312
312
|
);
|
|
313
313
|
|
|
314
|
+
assert.equal(
|
|
315
|
+
deps.callLog.filter((e: string) => e === "deriveState").length,
|
|
316
|
+
3,
|
|
317
|
+
"custom engine should stop immediately after a milestone-complete reconcile",
|
|
318
|
+
);
|
|
319
|
+
|
|
314
320
|
// Verify dev path was NOT used (resolveDispatch should not appear)
|
|
315
321
|
assert.ok(
|
|
316
322
|
!deps.callLog.includes("resolveDispatch"),
|
|
@@ -249,6 +249,37 @@ describe("CustomWorkflowEngine.reconcile", () => {
|
|
|
249
249
|
const graph = readGraph(runDir);
|
|
250
250
|
assert.equal(graph.steps[0].status, "complete");
|
|
251
251
|
});
|
|
252
|
+
|
|
253
|
+
it("re-reads GRAPH.yaml before reconcile so concurrent edits are preserved", async () => {
|
|
254
|
+
const { engine, runDir } = setupEngine([
|
|
255
|
+
makeStep({ id: "step-1" }),
|
|
256
|
+
makeStep({ id: "step-2", dependsOn: ["step-1"] }),
|
|
257
|
+
], "wf");
|
|
258
|
+
|
|
259
|
+
const staleState = await engine.deriveState("/unused");
|
|
260
|
+
|
|
261
|
+
// Simulate another process appending a new step after deriveState() ran.
|
|
262
|
+
writeGraph(runDir, makeGraph([
|
|
263
|
+
makeStep({ id: "step-1" }),
|
|
264
|
+
makeStep({ id: "step-2", dependsOn: ["step-1"] }),
|
|
265
|
+
makeStep({ id: "step-3", dependsOn: ["step-2"] }),
|
|
266
|
+
], "wf"));
|
|
267
|
+
|
|
268
|
+
const result = await engine.reconcile(staleState, {
|
|
269
|
+
unitType: "custom-step",
|
|
270
|
+
unitId: "wf/step-1",
|
|
271
|
+
startedAt: Date.now() - 1000,
|
|
272
|
+
finishedAt: Date.now(),
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
assert.equal(result.outcome, "continue");
|
|
276
|
+
|
|
277
|
+
const graph = readGraph(runDir);
|
|
278
|
+
assert.equal(graph.steps.length, 3, "reconcile should preserve the concurrent graph edit");
|
|
279
|
+
assert.equal(graph.steps[0].status, "complete");
|
|
280
|
+
assert.equal(graph.steps[1].status, "pending");
|
|
281
|
+
assert.equal(graph.steps[2].status, "pending");
|
|
282
|
+
});
|
|
252
283
|
});
|
|
253
284
|
|
|
254
285
|
// ─── getDisplayMetadata ──────────────────────────────────────────────────
|
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
// decision-scope-cascade: Tests for R005 fallback cascade and scope derivation
|
|
2
|
+
//
|
|
3
|
+
// Validates:
|
|
4
|
+
// (a) inlineDecisionsFromDb cascade: milestone + scope → milestone only → null
|
|
5
|
+
// (b) deriveSliceScope extracts meaningful scope keywords from slice titles
|
|
6
|
+
// (c) deriveSliceScope returns undefined for generic titles
|
|
7
|
+
|
|
8
|
+
import { describe, test, afterEach, beforeEach } from "node:test";
|
|
9
|
+
import assert from "node:assert/strict";
|
|
10
|
+
import {
|
|
11
|
+
openDatabase,
|
|
12
|
+
closeDatabase,
|
|
13
|
+
isDbAvailable,
|
|
14
|
+
insertDecision,
|
|
15
|
+
} from '../gsd-db.ts';
|
|
16
|
+
import {
|
|
17
|
+
queryDecisions,
|
|
18
|
+
formatDecisionsForPrompt,
|
|
19
|
+
} from '../context-store.ts';
|
|
20
|
+
import { deriveSliceScope } from '../auto-prompts.ts';
|
|
21
|
+
|
|
22
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
23
|
+
// deriveSliceScope: Extract meaningful scope from slice titles
|
|
24
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
25
|
+
|
|
26
|
+
describe("deriveSliceScope: keyword extraction", () => {
|
|
27
|
+
test("extracts first meaningful noun from title", () => {
|
|
28
|
+
// "Auth Middleware & Protected Route" → "auth"
|
|
29
|
+
assert.strictEqual(
|
|
30
|
+
deriveSliceScope("Auth Middleware & Protected Route"),
|
|
31
|
+
"auth",
|
|
32
|
+
"extracts 'auth' from auth-related title",
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
// "Database & User Model Setup" → "database" (not "setup" which is generic)
|
|
36
|
+
const dbScope = deriveSliceScope("Database & User Model Setup");
|
|
37
|
+
assert.ok(
|
|
38
|
+
dbScope === "database" || dbScope === "user",
|
|
39
|
+
`expected 'database' or 'user', got '${dbScope}'`,
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
// "API Rate Limiting" → "api"
|
|
43
|
+
assert.strictEqual(
|
|
44
|
+
deriveSliceScope("API Rate Limiting"),
|
|
45
|
+
"api",
|
|
46
|
+
"extracts 'api' from API-related title",
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
// "Stripe Payment Integration" → "stripe"
|
|
50
|
+
assert.strictEqual(
|
|
51
|
+
deriveSliceScope("Stripe Payment Integration"),
|
|
52
|
+
"stripe",
|
|
53
|
+
"extracts 'stripe' from payment-related title",
|
|
54
|
+
);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test("returns undefined for generic titles", () => {
|
|
58
|
+
// "Integration Testing" → undefined (both words are generic)
|
|
59
|
+
assert.strictEqual(
|
|
60
|
+
deriveSliceScope("Integration Testing"),
|
|
61
|
+
undefined,
|
|
62
|
+
"returns undefined for generic 'Integration Testing'",
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
// "Setup & Configuration" → undefined (all generic)
|
|
66
|
+
assert.strictEqual(
|
|
67
|
+
deriveSliceScope("Setup & Configuration"),
|
|
68
|
+
undefined,
|
|
69
|
+
"returns undefined for generic 'Setup & Configuration'",
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
// "Final Review" → undefined
|
|
73
|
+
assert.strictEqual(
|
|
74
|
+
deriveSliceScope("Final Review"),
|
|
75
|
+
undefined,
|
|
76
|
+
"returns undefined for generic 'Final Review'",
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
// "Basic Implementation" → undefined
|
|
80
|
+
assert.strictEqual(
|
|
81
|
+
deriveSliceScope("Basic Implementation"),
|
|
82
|
+
undefined,
|
|
83
|
+
"returns undefined for generic 'Basic Implementation'",
|
|
84
|
+
);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test("handles description as additional context", () => {
|
|
88
|
+
// Generic title but specific description
|
|
89
|
+
const scope = deriveSliceScope(
|
|
90
|
+
"Initial Setup",
|
|
91
|
+
"Configure PostgreSQL database connection",
|
|
92
|
+
);
|
|
93
|
+
assert.ok(
|
|
94
|
+
scope === "postgresql" || scope === "database" || scope === "configure",
|
|
95
|
+
`expected meaningful scope from description, got '${scope}'`,
|
|
96
|
+
);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
test("handles edge cases", () => {
|
|
100
|
+
// Empty title
|
|
101
|
+
assert.strictEqual(
|
|
102
|
+
deriveSliceScope(""),
|
|
103
|
+
undefined,
|
|
104
|
+
"returns undefined for empty title",
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
// Short words only
|
|
108
|
+
assert.strictEqual(
|
|
109
|
+
deriveSliceScope("A B C"),
|
|
110
|
+
undefined,
|
|
111
|
+
"returns undefined for very short words",
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
// Mixed case and punctuation
|
|
115
|
+
assert.strictEqual(
|
|
116
|
+
deriveSliceScope("OAuth2 + JWT Authentication"),
|
|
117
|
+
"oauth2",
|
|
118
|
+
"handles mixed case and punctuation",
|
|
119
|
+
);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
test("filters unit IDs (S01, M001, T03)", () => {
|
|
123
|
+
// "S01: Infrastructure" → undefined (S01 is a unit ID, infrastructure is generic)
|
|
124
|
+
assert.strictEqual(
|
|
125
|
+
deriveSliceScope("S01: Infrastructure"),
|
|
126
|
+
undefined,
|
|
127
|
+
"skips S01 ID and returns undefined for generic 'Infrastructure'",
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
// "M001 Setup" → undefined (M001 is a unit ID, setup is generic)
|
|
131
|
+
assert.strictEqual(
|
|
132
|
+
deriveSliceScope("M001 Setup"),
|
|
133
|
+
undefined,
|
|
134
|
+
"skips M001 ID and returns undefined for generic 'Setup'",
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
// "T03: Database Migration" → "database" (skips T03, returns meaningful word)
|
|
138
|
+
assert.strictEqual(
|
|
139
|
+
deriveSliceScope("T03: Database Migration"),
|
|
140
|
+
"database",
|
|
141
|
+
"skips T03 ID and returns 'database'",
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
// "S02 Auth Flow" → "auth" (skips S02, returns meaningful word)
|
|
145
|
+
assert.strictEqual(
|
|
146
|
+
deriveSliceScope("S02 Auth Flow"),
|
|
147
|
+
"auth",
|
|
148
|
+
"skips S02 ID and returns 'auth'",
|
|
149
|
+
);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
test("filters process/activity words", () => {
|
|
153
|
+
// "Integration Testing + Hardening" → undefined (all generic/process words)
|
|
154
|
+
assert.strictEqual(
|
|
155
|
+
deriveSliceScope("Integration Testing + Hardening"),
|
|
156
|
+
undefined,
|
|
157
|
+
"returns undefined for 'Integration Testing + Hardening'",
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
// "Validation & Verification" → undefined (both are process words)
|
|
161
|
+
assert.strictEqual(
|
|
162
|
+
deriveSliceScope("Validation & Verification"),
|
|
163
|
+
undefined,
|
|
164
|
+
"returns undefined for 'Validation & Verification'",
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
// "Performance Optimization" → "performance" (optimization is generic, performance is domain)
|
|
168
|
+
assert.strictEqual(
|
|
169
|
+
deriveSliceScope("Performance Optimization"),
|
|
170
|
+
"performance",
|
|
171
|
+
"extracts 'performance' before generic 'optimization'",
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
// "Security Enhancement" → "security" (enhancement is generic, security is domain)
|
|
175
|
+
assert.strictEqual(
|
|
176
|
+
deriveSliceScope("Security Enhancement"),
|
|
177
|
+
"security",
|
|
178
|
+
"extracts 'security' before generic 'enhancement'",
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
// "WebSocket Delivery Pipeline" → "websocket"
|
|
182
|
+
assert.strictEqual(
|
|
183
|
+
deriveSliceScope("WebSocket Delivery Pipeline"),
|
|
184
|
+
"websocket",
|
|
185
|
+
"extracts 'websocket' from delivery pipeline title",
|
|
186
|
+
);
|
|
187
|
+
|
|
188
|
+
// "Prisma Schema + Migration" → "prisma"
|
|
189
|
+
assert.strictEqual(
|
|
190
|
+
deriveSliceScope("Prisma Schema + Migration"),
|
|
191
|
+
"prisma",
|
|
192
|
+
"extracts 'prisma' from schema migration title",
|
|
193
|
+
);
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
198
|
+
// inlineDecisionsFromDb cascade: R005 implementation
|
|
199
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
200
|
+
|
|
201
|
+
describe("inlineDecisionsFromDb: cascade fallback (R005)", () => {
|
|
202
|
+
beforeEach(() => {
|
|
203
|
+
openDatabase(':memory:');
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
afterEach(() => {
|
|
207
|
+
closeDatabase();
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
test("cascade: scoped query returns scoped decisions when they exist", () => {
|
|
211
|
+
// Insert decisions with different scopes
|
|
212
|
+
insertDecision({
|
|
213
|
+
id: 'D001', when_context: 'M001/S01', scope: 'auth',
|
|
214
|
+
decision: 'use JWT', choice: 'JWT', rationale: 'standard',
|
|
215
|
+
revisable: 'yes', made_by: 'agent', superseded_by: null,
|
|
216
|
+
});
|
|
217
|
+
insertDecision({
|
|
218
|
+
id: 'D002', when_context: 'M001/S02', scope: 'database',
|
|
219
|
+
decision: 'use PostgreSQL', choice: 'PostgreSQL', rationale: 'relational',
|
|
220
|
+
revisable: 'yes', made_by: 'agent', superseded_by: null,
|
|
221
|
+
});
|
|
222
|
+
insertDecision({
|
|
223
|
+
id: 'D003', when_context: 'M001/S01', scope: 'architecture',
|
|
224
|
+
decision: 'use microservices', choice: 'microservices', rationale: 'scalable',
|
|
225
|
+
revisable: 'yes', made_by: 'agent', superseded_by: null,
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
// Query with scope 'auth' should return D001 only
|
|
229
|
+
const authDecisions = queryDecisions({ milestoneId: 'M001', scope: 'auth' });
|
|
230
|
+
assert.strictEqual(authDecisions.length, 1, 'scoped query returns 1 decision');
|
|
231
|
+
assert.strictEqual(authDecisions[0]?.id, 'D001', 'returns D001 for auth scope');
|
|
232
|
+
|
|
233
|
+
// Query with scope 'database' should return D002 only
|
|
234
|
+
const dbDecisions = queryDecisions({ milestoneId: 'M001', scope: 'database' });
|
|
235
|
+
assert.strictEqual(dbDecisions.length, 1, 'scoped query returns 1 decision');
|
|
236
|
+
assert.strictEqual(dbDecisions[0]?.id, 'D002', 'returns D002 for database scope');
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
test("cascade: milestone-only fallback when scoped query returns empty", () => {
|
|
240
|
+
// Insert decisions for M001 with generic scope (e.g. 'architecture')
|
|
241
|
+
insertDecision({
|
|
242
|
+
id: 'D001', when_context: 'M001/S01', scope: 'architecture',
|
|
243
|
+
decision: 'use microservices', choice: 'microservices', rationale: 'scalable',
|
|
244
|
+
revisable: 'yes', made_by: 'agent', superseded_by: null,
|
|
245
|
+
});
|
|
246
|
+
insertDecision({
|
|
247
|
+
id: 'D002', when_context: 'M001/S02', scope: 'performance',
|
|
248
|
+
decision: 'use caching', choice: 'Redis', rationale: 'fast',
|
|
249
|
+
revisable: 'yes', made_by: 'agent', superseded_by: null,
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
// Query with scope 'auth' (no decisions with this scope) should return empty
|
|
253
|
+
const authDecisions = queryDecisions({ milestoneId: 'M001', scope: 'auth' });
|
|
254
|
+
assert.strictEqual(authDecisions.length, 0, 'scoped query for auth returns empty');
|
|
255
|
+
|
|
256
|
+
// Simulate cascade: fallback to milestone-only query
|
|
257
|
+
const milestoneDecisions = queryDecisions({ milestoneId: 'M001' });
|
|
258
|
+
assert.strictEqual(milestoneDecisions.length, 2, 'milestone-only query returns 2 decisions');
|
|
259
|
+
const ids = milestoneDecisions.map(d => d.id).sort();
|
|
260
|
+
assert.deepStrictEqual(ids, ['D001', 'D002'], 'milestone fallback returns all M001 decisions');
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
test("cascade: returns null when both scoped and milestone queries are empty", () => {
|
|
264
|
+
// Insert decisions only for M002
|
|
265
|
+
insertDecision({
|
|
266
|
+
id: 'D001', when_context: 'M002/S01', scope: 'auth',
|
|
267
|
+
decision: 'use OAuth', choice: 'OAuth2', rationale: 'standard',
|
|
268
|
+
revisable: 'yes', made_by: 'agent', superseded_by: null,
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
// Query M001 with scope should return empty (no M001 decisions at all)
|
|
272
|
+
const scopedDecisions = queryDecisions({ milestoneId: 'M001', scope: 'auth' });
|
|
273
|
+
assert.strictEqual(scopedDecisions.length, 0, 'scoped query returns empty');
|
|
274
|
+
|
|
275
|
+
// Fallback to milestone-only should also return empty (no M001 decisions)
|
|
276
|
+
const milestoneDecisions = queryDecisions({ milestoneId: 'M001' });
|
|
277
|
+
assert.strictEqual(milestoneDecisions.length, 0, 'milestone-only query returns empty');
|
|
278
|
+
|
|
279
|
+
// This scenario would result in null from inlineDecisionsFromDb
|
|
280
|
+
// (we can't directly test inlineDecisionsFromDb here without mocking fs)
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
test("cascade: demonstrates the full cascade behavior", () => {
|
|
284
|
+
// This test demonstrates the cascade logic that inlineDecisionsFromDb implements:
|
|
285
|
+
// 1. First try { milestoneId: 'M001', scope: 'payment' } → empty
|
|
286
|
+
// 2. Then try { milestoneId: 'M001' } → gets D001, D002
|
|
287
|
+
// 3. Return the milestone-level decisions
|
|
288
|
+
|
|
289
|
+
// Setup: decisions exist at milestone level but not for 'payment' scope
|
|
290
|
+
insertDecision({
|
|
291
|
+
id: 'D001', when_context: 'M001/S01', scope: 'architecture',
|
|
292
|
+
decision: 'use REST', choice: 'REST API', rationale: 'standard',
|
|
293
|
+
revisable: 'yes', made_by: 'agent', superseded_by: null,
|
|
294
|
+
});
|
|
295
|
+
insertDecision({
|
|
296
|
+
id: 'D002', when_context: 'M001/S02', scope: 'security',
|
|
297
|
+
decision: 'use HTTPS', choice: 'TLS 1.3', rationale: 'secure',
|
|
298
|
+
revisable: 'yes', made_by: 'agent', superseded_by: null,
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
// Step 1: Query with scope 'payment' (no matches)
|
|
302
|
+
const paymentDecisions = queryDecisions({ milestoneId: 'M001', scope: 'payment' });
|
|
303
|
+
assert.strictEqual(paymentDecisions.length, 0, 'payment scope query returns empty');
|
|
304
|
+
|
|
305
|
+
// Step 2: Since scope was provided but returned empty, cascade to milestone-only
|
|
306
|
+
const milestoneDecisions = queryDecisions({ milestoneId: 'M001' });
|
|
307
|
+
assert.strictEqual(milestoneDecisions.length, 2, 'milestone fallback returns 2 decisions');
|
|
308
|
+
|
|
309
|
+
// Step 3: Format and verify content
|
|
310
|
+
const formatted = formatDecisionsForPrompt(milestoneDecisions);
|
|
311
|
+
assert.match(formatted, /D001/, 'formatted output includes D001');
|
|
312
|
+
assert.match(formatted, /D002/, 'formatted output includes D002');
|
|
313
|
+
assert.match(formatted, /architecture/, 'formatted output includes architecture scope');
|
|
314
|
+
assert.match(formatted, /security/, 'formatted output includes security scope');
|
|
315
|
+
});
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
319
|
+
// Integration: scope derivation feeds into cascade
|
|
320
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
321
|
+
|
|
322
|
+
describe("integration: scope derivation with cascade", () => {
|
|
323
|
+
beforeEach(() => {
|
|
324
|
+
openDatabase(':memory:');
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
afterEach(() => {
|
|
328
|
+
closeDatabase();
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
test("derived scope finds matching decisions when they exist", () => {
|
|
332
|
+
// Insert decisions with 'auth' scope
|
|
333
|
+
insertDecision({
|
|
334
|
+
id: 'D001', when_context: 'M001/S01', scope: 'auth',
|
|
335
|
+
decision: 'use JWT', choice: 'JWT tokens', rationale: 'stateless',
|
|
336
|
+
revisable: 'yes', made_by: 'agent', superseded_by: null,
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
// Derive scope from slice title
|
|
340
|
+
const derivedScope = deriveSliceScope("Auth Middleware & Protected Routes");
|
|
341
|
+
assert.strictEqual(derivedScope, 'auth', 'derives auth scope from title');
|
|
342
|
+
|
|
343
|
+
// Query with derived scope should find the decision
|
|
344
|
+
const decisions = queryDecisions({ milestoneId: 'M001', scope: derivedScope });
|
|
345
|
+
assert.strictEqual(decisions.length, 1, 'scoped query finds matching decision');
|
|
346
|
+
assert.strictEqual(decisions[0]?.id, 'D001', 'finds the auth decision');
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
test("generic title triggers milestone-level fallback", () => {
|
|
350
|
+
// Insert decisions with various scopes
|
|
351
|
+
insertDecision({
|
|
352
|
+
id: 'D001', when_context: 'M001/S01', scope: 'architecture',
|
|
353
|
+
decision: 'use monolith', choice: 'monolith', rationale: 'simple',
|
|
354
|
+
revisable: 'yes', made_by: 'agent', superseded_by: null,
|
|
355
|
+
});
|
|
356
|
+
insertDecision({
|
|
357
|
+
id: 'D002', when_context: 'M001/S02', scope: 'tooling',
|
|
358
|
+
decision: 'use TypeScript', choice: 'TypeScript', rationale: 'type safety',
|
|
359
|
+
revisable: 'yes', made_by: 'agent', superseded_by: null,
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
// Derive scope from generic slice title
|
|
363
|
+
const derivedScope = deriveSliceScope("Integration Testing");
|
|
364
|
+
assert.strictEqual(derivedScope, undefined, 'generic title returns undefined scope');
|
|
365
|
+
|
|
366
|
+
// Without a scope, query returns all milestone decisions
|
|
367
|
+
const decisions = queryDecisions({ milestoneId: 'M001', scope: derivedScope });
|
|
368
|
+
assert.strictEqual(decisions.length, 2, 'no scope filter returns all decisions');
|
|
369
|
+
});
|
|
370
|
+
});
|