gsd-pi 2.73.1 → 2.74.0-dev.b741afb
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/cli-web-branch.d.ts +4 -3
- package/dist/cli-web-branch.js +10 -7
- package/dist/cli.js +184 -206
- package/dist/headless-query.js +4 -1
- package/dist/help-text.js +23 -0
- package/dist/logo.d.ts +1 -1
- package/dist/logo.js +1 -1
- package/dist/onboarding.js +59 -53
- package/dist/resource-loader.js +2 -2
- package/dist/resources/extensions/claude-code-cli/stream-adapter.js +68 -4
- package/dist/resources/extensions/gsd/auto/detect-stuck.js +11 -4
- package/dist/resources/extensions/gsd/auto/phases.js +60 -10
- package/dist/resources/extensions/gsd/auto-dispatch.js +11 -3
- package/dist/resources/extensions/gsd/auto-model-selection.js +54 -11
- package/dist/resources/extensions/gsd/auto-post-unit.js +93 -57
- package/dist/resources/extensions/gsd/auto-prompts.js +12 -0
- package/dist/resources/extensions/gsd/auto-start.js +23 -6
- package/dist/resources/extensions/gsd/auto-timeout-recovery.js +13 -0
- package/dist/resources/extensions/gsd/auto-verification.js +88 -3
- package/dist/resources/extensions/gsd/auto.js +37 -10
- package/dist/resources/extensions/gsd/bootstrap/register-extension.js +21 -8
- package/dist/resources/extensions/gsd/commands/catalog.js +26 -1
- package/dist/resources/extensions/gsd/commands/handlers/ops.js +20 -0
- package/dist/resources/extensions/gsd/commands/handlers/workflow.js +68 -9
- package/dist/resources/extensions/gsd/commands-add-tests.js +111 -0
- package/dist/resources/extensions/gsd/commands-backlog.js +140 -0
- package/dist/resources/extensions/gsd/commands-do.js +79 -0
- package/dist/resources/extensions/gsd/commands-handlers.js +8 -2
- package/dist/resources/extensions/gsd/commands-maintenance.js +6 -6
- package/dist/resources/extensions/gsd/commands-pr-branch.js +180 -0
- package/dist/resources/extensions/gsd/commands-session-report.js +82 -0
- package/dist/resources/extensions/gsd/commands-ship.js +187 -0
- package/dist/resources/extensions/gsd/db-writer.js +3 -5
- package/dist/resources/extensions/gsd/docs/preferences-reference.md +1 -1
- package/dist/resources/extensions/gsd/graph-context.js +66 -0
- package/dist/resources/extensions/gsd/gsd-db.js +321 -0
- package/dist/resources/extensions/gsd/index.js +15 -2
- package/dist/resources/extensions/gsd/md-importer.js +3 -4
- package/dist/resources/extensions/gsd/memory-store.js +19 -51
- package/dist/resources/extensions/gsd/milestone-validation-gates.js +13 -12
- package/dist/resources/extensions/gsd/native-git-bridge.js +7 -4
- package/dist/resources/extensions/gsd/notification-widget.js +2 -2
- package/dist/resources/extensions/gsd/preferences-models.js +43 -0
- package/dist/resources/extensions/gsd/preferences-types.js +1 -0
- package/dist/resources/extensions/gsd/preferences-validation.js +22 -0
- package/dist/resources/extensions/gsd/prompts/add-tests.md +35 -0
- package/dist/resources/extensions/gsd/state.js +66 -15
- package/dist/resources/extensions/gsd/tools/complete-slice.js +15 -0
- package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +3 -14
- package/dist/resources/extensions/gsd/triage-resolution.js +2 -5
- package/dist/resources/extensions/gsd/workflow-manifest.js +8 -69
- package/dist/resources/extensions/gsd/workflow-migration.js +21 -22
- package/dist/resources/extensions/gsd/workflow-projections.js +4 -1
- package/dist/resources/extensions/gsd/workflow-reconcile.js +14 -11
- package/dist/tsconfig.extensions.tsbuildinfo +1 -0
- package/dist/update-check.d.ts +1 -0
- package/dist/update-check.js +13 -5
- package/dist/update-cmd.js +4 -3
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +9 -9
- package/dist/web/standalone/.next/build-manifest.json +3 -3
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/required-server-files.json +3 -3
- package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
- package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
- package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +3 -3
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +3 -3
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/experimental/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/experimental/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/notifications/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/notifications/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +4 -4
- package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +4 -4
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -4
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +3 -3
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/page.js +2 -2
- package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +9 -9
- package/dist/web/standalone/.next/server/chunks/63.js +3 -3
- package/dist/web/standalone/.next/server/chunks/6897.js +1 -1
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware-manifest.json +5 -5
- package/dist/web/standalone/.next/server/middleware.js +2 -2
- package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
- package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +1 -1
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/dist/web/standalone/.next/static/chunks/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 +3 -3
- package/packages/daemon/package.json +2 -2
- package/packages/mcp-server/dist/index.d.ts +3 -0
- package/packages/mcp-server/dist/index.d.ts.map +1 -1
- package/packages/mcp-server/dist/index.js +3 -0
- package/packages/mcp-server/dist/index.js.map +1 -1
- package/packages/mcp-server/dist/readers/graph.d.ts +87 -0
- package/packages/mcp-server/dist/readers/graph.d.ts.map +1 -0
- package/packages/mcp-server/dist/readers/graph.js +548 -0
- package/packages/mcp-server/dist/readers/graph.js.map +1 -0
- package/packages/mcp-server/dist/readers/index.d.ts +2 -0
- package/packages/mcp-server/dist/readers/index.d.ts.map +1 -1
- package/packages/mcp-server/dist/readers/index.js +1 -0
- package/packages/mcp-server/dist/readers/index.js.map +1 -1
- package/packages/mcp-server/dist/server.d.ts.map +1 -1
- package/packages/mcp-server/dist/server.js +65 -0
- package/packages/mcp-server/dist/server.js.map +1 -1
- package/packages/mcp-server/package.json +2 -2
- package/packages/mcp-server/src/index.ts +15 -0
- package/packages/mcp-server/src/readers/graph.test.ts +426 -0
- package/packages/mcp-server/src/readers/graph.ts +708 -0
- package/packages/mcp-server/src/readers/index.ts +12 -0
- package/packages/mcp-server/src/server.ts +83 -0
- package/packages/mcp-server/tsconfig.json +1 -0
- package/packages/mcp-server/tsconfig.tsbuildinfo +1 -0
- package/packages/native/package.json +2 -2
- package/packages/native/tsconfig.tsbuildinfo +1 -0
- package/packages/pi-agent-core/package.json +1 -1
- package/packages/pi-agent-core/tsconfig.json +1 -0
- package/packages/pi-agent-core/tsconfig.tsbuildinfo +1 -0
- package/packages/pi-ai/dist/index.d.ts +1 -0
- package/packages/pi-ai/dist/index.d.ts.map +1 -1
- package/packages/pi-ai/dist/index.js +1 -0
- package/packages/pi-ai/dist/index.js.map +1 -1
- package/packages/pi-ai/dist/utils/overflow.d.ts.map +1 -1
- package/packages/pi-ai/dist/utils/overflow.js +12 -0
- package/packages/pi-ai/dist/utils/overflow.js.map +1 -1
- package/packages/pi-ai/dist/utils/tests/overflow.test.d.ts +2 -0
- package/packages/pi-ai/dist/utils/tests/overflow.test.d.ts.map +1 -0
- package/packages/pi-ai/dist/utils/tests/overflow.test.js +50 -0
- package/packages/pi-ai/dist/utils/tests/overflow.test.js.map +1 -0
- package/packages/pi-ai/package.json +1 -1
- package/packages/pi-ai/src/index.ts +4 -0
- package/packages/pi-ai/src/utils/overflow.ts +14 -1
- package/packages/pi-ai/src/utils/tests/overflow.test.ts +58 -0
- package/packages/pi-ai/tsconfig.json +1 -0
- package/packages/pi-ai/tsconfig.tsbuildinfo +1 -0
- package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js +313 -8
- package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/compaction/utils.js +5 -5
- package/packages/pi-coding-agent/dist/core/compaction/utils.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/compaction-utils.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/compaction-utils.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/compaction-utils.test.js +45 -0
- package/packages/pi-coding-agent/dist/core/compaction-utils.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts +12 -2
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js +61 -28
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.d.ts +2 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.js +9 -3
- package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.test.js +52 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.test.js.map +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 +94 -16
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +11 -3
- 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/chat-controller-ordering.test.ts +355 -8
- package/packages/pi-coding-agent/src/core/compaction/utils.ts +5 -5
- package/packages/pi-coding-agent/src/core/compaction-utils.test.ts +50 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/assistant-message.ts +74 -32
- package/packages/pi-coding-agent/src/modes/interactive/components/dynamic-border.test.ts +73 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/dynamic-border.ts +9 -3
- package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +113 -21
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +11 -3
- package/packages/pi-coding-agent/tsconfig.json +1 -0
- package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -0
- package/packages/pi-tui/dist/__tests__/tui.test.js +60 -1
- package/packages/pi-tui/dist/__tests__/tui.test.js.map +1 -1
- package/packages/pi-tui/dist/tui.d.ts +8 -0
- package/packages/pi-tui/dist/tui.d.ts.map +1 -1
- package/packages/pi-tui/dist/tui.js +32 -3
- package/packages/pi-tui/dist/tui.js.map +1 -1
- package/packages/pi-tui/package.json +1 -1
- package/packages/pi-tui/src/__tests__/tui.test.ts +76 -1
- package/packages/pi-tui/src/tui.ts +31 -3
- package/packages/pi-tui/tsconfig.json +1 -0
- package/packages/pi-tui/tsconfig.tsbuildinfo +1 -0
- package/packages/rpc-client/package.json +1 -1
- package/packages/rpc-client/tsconfig.json +1 -0
- package/packages/rpc-client/tsconfig.tsbuildinfo +1 -0
- package/pkg/package.json +1 -1
- package/src/resources/extensions/claude-code-cli/stream-adapter.ts +107 -5
- package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +111 -2
- package/src/resources/extensions/gsd/auto/detect-stuck.ts +12 -4
- package/src/resources/extensions/gsd/auto/loop-deps.ts +6 -0
- package/src/resources/extensions/gsd/auto/phases.ts +90 -10
- package/src/resources/extensions/gsd/auto-dispatch.ts +10 -4
- package/src/resources/extensions/gsd/auto-model-selection.ts +85 -11
- package/src/resources/extensions/gsd/auto-post-unit.ts +107 -58
- package/src/resources/extensions/gsd/auto-prompts.ts +13 -0
- package/src/resources/extensions/gsd/auto-start.ts +30 -6
- package/src/resources/extensions/gsd/auto-timeout-recovery.ts +17 -0
- package/src/resources/extensions/gsd/auto-verification.ts +98 -3
- package/src/resources/extensions/gsd/auto.ts +38 -14
- package/src/resources/extensions/gsd/bootstrap/register-extension.ts +24 -8
- package/src/resources/extensions/gsd/commands/catalog.ts +26 -1
- package/src/resources/extensions/gsd/commands/handlers/ops.ts +20 -0
- package/src/resources/extensions/gsd/commands/handlers/workflow.ts +74 -9
- package/src/resources/extensions/gsd/commands-add-tests.ts +137 -0
- package/src/resources/extensions/gsd/commands-backlog.ts +182 -0
- package/src/resources/extensions/gsd/commands-do.ts +109 -0
- package/src/resources/extensions/gsd/commands-handlers.ts +8 -2
- package/src/resources/extensions/gsd/commands-maintenance.ts +6 -6
- package/src/resources/extensions/gsd/commands-pr-branch.ts +234 -0
- package/src/resources/extensions/gsd/commands-session-report.ts +101 -0
- package/src/resources/extensions/gsd/commands-ship.ts +219 -0
- package/src/resources/extensions/gsd/db-writer.ts +3 -5
- package/src/resources/extensions/gsd/docs/preferences-reference.md +1 -1
- package/src/resources/extensions/gsd/graph-context.ts +85 -0
- package/src/resources/extensions/gsd/gsd-db.ts +467 -0
- package/src/resources/extensions/gsd/index.ts +18 -2
- package/src/resources/extensions/gsd/md-importer.ts +3 -5
- package/src/resources/extensions/gsd/memory-store.ts +31 -62
- package/src/resources/extensions/gsd/milestone-validation-gates.ts +13 -14
- package/src/resources/extensions/gsd/native-git-bridge.ts +11 -12
- package/src/resources/extensions/gsd/notification-widget.ts +2 -2
- package/src/resources/extensions/gsd/preferences-models.ts +41 -0
- package/src/resources/extensions/gsd/preferences-types.ts +12 -0
- package/src/resources/extensions/gsd/preferences-validation.ts +23 -0
- package/src/resources/extensions/gsd/prompts/add-tests.md +35 -0
- package/src/resources/extensions/gsd/state.ts +80 -17
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/auto-post-unit-step-message.test.ts +53 -0
- package/src/resources/extensions/gsd/tests/auto-start-model-capture.test.ts +51 -2
- package/src/resources/extensions/gsd/tests/commands-backlog.test.ts +158 -0
- package/src/resources/extensions/gsd/tests/commands-do.test.ts +127 -0
- package/src/resources/extensions/gsd/tests/commands-pr-branch.test.ts +68 -0
- package/src/resources/extensions/gsd/tests/commands-session-report.test.ts +82 -0
- package/src/resources/extensions/gsd/tests/commands-ship.test.ts +71 -0
- package/src/resources/extensions/gsd/tests/commands-workflow-custom.test.ts +14 -0
- package/src/resources/extensions/gsd/tests/complete-milestone-false-merge.test.ts +142 -0
- package/src/resources/extensions/gsd/tests/completed-at-reconcile.test.ts +42 -0
- package/src/resources/extensions/gsd/tests/derive-state-crossval.test.ts +3 -2
- package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +3 -2
- package/src/resources/extensions/gsd/tests/derive-state-helpers.test.ts +68 -8
- package/src/resources/extensions/gsd/tests/derive-state.test.ts +3 -3
- package/src/resources/extensions/gsd/tests/extension-bootstrap-isolation.test.ts +154 -0
- package/src/resources/extensions/gsd/tests/flat-rate-routing-guard.test.ts +137 -1
- package/src/resources/extensions/gsd/tests/graph-context.test.ts +337 -0
- package/src/resources/extensions/gsd/tests/integration/state-machine-edge-cases.test.ts +4 -2
- package/src/resources/extensions/gsd/tests/journal-integration.test.ts +68 -1
- package/src/resources/extensions/gsd/tests/model-isolation.test.ts +91 -2
- package/src/resources/extensions/gsd/tests/native-git-bridge-exec-fallback.test.ts +140 -0
- package/src/resources/extensions/gsd/tests/preferences.test.ts +47 -0
- package/src/resources/extensions/gsd/tests/single-writer-invariant.test.ts +180 -0
- package/src/resources/extensions/gsd/tests/state-machine-full-walkthrough.test.ts +5 -7
- package/src/resources/extensions/gsd/tests/token-profile.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/validate-milestone-stuck-guard.test.ts +179 -0
- package/src/resources/extensions/gsd/tests/workflow-logger-wiring.test.ts +223 -0
- package/src/resources/extensions/gsd/tools/complete-slice.ts +19 -0
- package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +3 -11
- package/src/resources/extensions/gsd/triage-resolution.ts +2 -7
- package/src/resources/extensions/gsd/workflow-manifest.ts +9 -104
- package/src/resources/extensions/gsd/workflow-migration.ts +21 -29
- package/src/resources/extensions/gsd/workflow-projections.ts +8 -1
- package/src/resources/extensions/gsd/workflow-reconcile.ts +15 -15
- package/dist/web/standalone/.next/static/chunks/app/page-7115e62689b5fd84.js +0 -1
- package/dist/web/standalone/.next/static/chunks/main-app-d3d4c336195465f9.js +0 -1
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ab5a8926e07ec673.js +0 -1
- /package/dist/web/standalone/.next/static/{Qr27MOHx0lxRGnJvlhxxu → XnHY5eXUsTCFmNodWHetD}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{Qr27MOHx0lxRGnJvlhxxu → XnHY5eXUsTCFmNodWHetD}/_ssgManifest.js +0 -0
|
@@ -3,7 +3,19 @@
|
|
|
3
3
|
// Storage layer for auto-learned project memories. Follows context-store.ts patterns.
|
|
4
4
|
// All functions degrade gracefully: return empty results when DB unavailable, never throw.
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
isDbAvailable,
|
|
8
|
+
_getAdapter,
|
|
9
|
+
transaction,
|
|
10
|
+
insertMemoryRow,
|
|
11
|
+
rewriteMemoryId,
|
|
12
|
+
updateMemoryContentRow,
|
|
13
|
+
incrementMemoryHitCount,
|
|
14
|
+
supersedeMemoryRow,
|
|
15
|
+
markMemoryUnitProcessed,
|
|
16
|
+
decayMemoriesBefore,
|
|
17
|
+
supersedeLowestRankedMemories,
|
|
18
|
+
} from './gsd-db.js';
|
|
7
19
|
|
|
8
20
|
// ─── Types ──────────────────────────────────────────────────────────────────
|
|
9
21
|
|
|
@@ -170,28 +182,22 @@ export function createMemory(fields: {
|
|
|
170
182
|
const now = new Date().toISOString();
|
|
171
183
|
// Insert with a temporary placeholder ID — seq is auto-assigned
|
|
172
184
|
const placeholder = `_TMP_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
':source_unit_id': fields.source_unit_id ?? null,
|
|
183
|
-
':created_at': now,
|
|
184
|
-
':updated_at': now,
|
|
185
|
+
insertMemoryRow({
|
|
186
|
+
id: placeholder,
|
|
187
|
+
category: fields.category,
|
|
188
|
+
content: fields.content,
|
|
189
|
+
confidence: fields.confidence ?? 0.8,
|
|
190
|
+
sourceUnitType: fields.source_unit_type ?? null,
|
|
191
|
+
sourceUnitId: fields.source_unit_id ?? null,
|
|
192
|
+
createdAt: now,
|
|
193
|
+
updatedAt: now,
|
|
185
194
|
});
|
|
186
|
-
// Derive the real ID from the assigned seq
|
|
195
|
+
// Derive the real ID from the assigned seq (SELECT is still fine via adapter)
|
|
187
196
|
const row = adapter.prepare('SELECT seq FROM memories WHERE id = :id').get({ ':id': placeholder });
|
|
188
197
|
if (!row) return placeholder; // fallback — should not happen
|
|
189
198
|
const seq = row['seq'] as number;
|
|
190
199
|
const realId = `MEM${String(seq).padStart(3, '0')}`;
|
|
191
|
-
|
|
192
|
-
':real_id': realId,
|
|
193
|
-
':placeholder': placeholder,
|
|
194
|
-
});
|
|
200
|
+
rewriteMemoryId(placeholder, realId);
|
|
195
201
|
return realId;
|
|
196
202
|
} catch {
|
|
197
203
|
return null;
|
|
@@ -203,20 +209,9 @@ export function createMemory(fields: {
|
|
|
203
209
|
*/
|
|
204
210
|
export function updateMemoryContent(id: string, content: string, confidence?: number): boolean {
|
|
205
211
|
if (!isDbAvailable()) return false;
|
|
206
|
-
const adapter = _getAdapter();
|
|
207
|
-
if (!adapter) return false;
|
|
208
212
|
|
|
209
213
|
try {
|
|
210
|
-
|
|
211
|
-
if (confidence != null) {
|
|
212
|
-
adapter.prepare(
|
|
213
|
-
'UPDATE memories SET content = :content, confidence = :confidence, updated_at = :updated_at WHERE id = :id',
|
|
214
|
-
).run({ ':content': content, ':confidence': confidence, ':updated_at': now, ':id': id });
|
|
215
|
-
} else {
|
|
216
|
-
adapter.prepare(
|
|
217
|
-
'UPDATE memories SET content = :content, updated_at = :updated_at WHERE id = :id',
|
|
218
|
-
).run({ ':content': content, ':updated_at': now, ':id': id });
|
|
219
|
-
}
|
|
214
|
+
updateMemoryContentRow(id, content, confidence, new Date().toISOString());
|
|
220
215
|
return true;
|
|
221
216
|
} catch {
|
|
222
217
|
return false;
|
|
@@ -228,13 +223,9 @@ export function updateMemoryContent(id: string, content: string, confidence?: nu
|
|
|
228
223
|
*/
|
|
229
224
|
export function reinforceMemory(id: string): boolean {
|
|
230
225
|
if (!isDbAvailable()) return false;
|
|
231
|
-
const adapter = _getAdapter();
|
|
232
|
-
if (!adapter) return false;
|
|
233
226
|
|
|
234
227
|
try {
|
|
235
|
-
|
|
236
|
-
'UPDATE memories SET hit_count = hit_count + 1, updated_at = :updated_at WHERE id = :id',
|
|
237
|
-
).run({ ':updated_at': new Date().toISOString(), ':id': id });
|
|
228
|
+
incrementMemoryHitCount(id, new Date().toISOString());
|
|
238
229
|
return true;
|
|
239
230
|
} catch {
|
|
240
231
|
return false;
|
|
@@ -246,13 +237,9 @@ export function reinforceMemory(id: string): boolean {
|
|
|
246
237
|
*/
|
|
247
238
|
export function supersedeMemory(oldId: string, newId: string): boolean {
|
|
248
239
|
if (!isDbAvailable()) return false;
|
|
249
|
-
const adapter = _getAdapter();
|
|
250
|
-
if (!adapter) return false;
|
|
251
240
|
|
|
252
241
|
try {
|
|
253
|
-
|
|
254
|
-
'UPDATE memories SET superseded_by = :new_id, updated_at = :updated_at WHERE id = :old_id',
|
|
255
|
-
).run({ ':new_id': newId, ':updated_at': new Date().toISOString(), ':old_id': oldId });
|
|
242
|
+
supersedeMemoryRow(oldId, newId, new Date().toISOString());
|
|
256
243
|
return true;
|
|
257
244
|
} catch {
|
|
258
245
|
return false;
|
|
@@ -284,14 +271,9 @@ export function isUnitProcessed(unitKey: string): boolean {
|
|
|
284
271
|
*/
|
|
285
272
|
export function markUnitProcessed(unitKey: string, activityFile: string): boolean {
|
|
286
273
|
if (!isDbAvailable()) return false;
|
|
287
|
-
const adapter = _getAdapter();
|
|
288
|
-
if (!adapter) return false;
|
|
289
274
|
|
|
290
275
|
try {
|
|
291
|
-
|
|
292
|
-
`INSERT OR IGNORE INTO memory_processed_units (unit_key, activity_file, processed_at)
|
|
293
|
-
VALUES (:key, :file, :at)`,
|
|
294
|
-
).run({ ':key': unitKey, ':file': activityFile, ':at': new Date().toISOString() });
|
|
276
|
+
markMemoryUnitProcessed(unitKey, activityFile, new Date().toISOString());
|
|
295
277
|
return true;
|
|
296
278
|
} catch {
|
|
297
279
|
return false;
|
|
@@ -310,7 +292,7 @@ export function decayStaleMemories(thresholdUnits = 20): void {
|
|
|
310
292
|
if (!adapter) return;
|
|
311
293
|
|
|
312
294
|
try {
|
|
313
|
-
// Find the timestamp of the Nth most recent processed unit
|
|
295
|
+
// Find the timestamp of the Nth most recent processed unit (read-only SELECT)
|
|
314
296
|
const row = adapter.prepare(
|
|
315
297
|
`SELECT processed_at FROM memory_processed_units
|
|
316
298
|
ORDER BY processed_at DESC
|
|
@@ -320,11 +302,7 @@ export function decayStaleMemories(thresholdUnits = 20): void {
|
|
|
320
302
|
if (!row) return; // not enough processed units yet
|
|
321
303
|
|
|
322
304
|
const cutoff = row['processed_at'] as string;
|
|
323
|
-
|
|
324
|
-
`UPDATE memories
|
|
325
|
-
SET confidence = MAX(0.1, confidence - 0.1), updated_at = :now
|
|
326
|
-
WHERE superseded_by IS NULL AND updated_at < :cutoff AND confidence > 0.1`,
|
|
327
|
-
).run({ ':now': new Date().toISOString(), ':cutoff': cutoff });
|
|
305
|
+
decayMemoriesBefore(cutoff, new Date().toISOString());
|
|
328
306
|
} catch {
|
|
329
307
|
// non-fatal
|
|
330
308
|
}
|
|
@@ -346,16 +324,7 @@ export function enforceMemoryCap(max = 50): void {
|
|
|
346
324
|
if (count <= max) return;
|
|
347
325
|
|
|
348
326
|
const excess = count - max;
|
|
349
|
-
|
|
350
|
-
adapter.prepare(
|
|
351
|
-
`UPDATE memories SET superseded_by = 'CAP_EXCEEDED', updated_at = :now
|
|
352
|
-
WHERE id IN (
|
|
353
|
-
SELECT id FROM memories
|
|
354
|
-
WHERE superseded_by IS NULL
|
|
355
|
-
ORDER BY (confidence * (1.0 + hit_count * 0.1)) ASC
|
|
356
|
-
LIMIT :limit
|
|
357
|
-
)`,
|
|
358
|
-
).run({ ':now': new Date().toISOString(), ':limit': excess });
|
|
327
|
+
supersedeLowestRankedMemories(excess, new Date().toISOString());
|
|
359
328
|
} catch {
|
|
360
329
|
// non-fatal
|
|
361
330
|
}
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* dispatch rules, and state derivation. See gate-registry.ts.
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
-
import {
|
|
14
|
+
import { isDbAvailable, upsertQualityGate } from "./gsd-db.js";
|
|
15
15
|
import { getGatesForTurn } from "./gate-registry.js";
|
|
16
16
|
|
|
17
17
|
/**
|
|
@@ -31,24 +31,23 @@ export function insertMilestoneValidationGates(
|
|
|
31
31
|
verdict: string,
|
|
32
32
|
evaluatedAt: string,
|
|
33
33
|
): void {
|
|
34
|
-
|
|
35
|
-
if (!db) return;
|
|
34
|
+
if (!isDbAvailable()) return;
|
|
36
35
|
|
|
37
36
|
const gateVerdict = verdict === "pass" ? "pass" : "flag";
|
|
38
37
|
const milestoneGates = getGatesForTurn("validate-milestone");
|
|
39
38
|
|
|
40
39
|
for (const def of milestoneGates) {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
40
|
+
upsertQualityGate({
|
|
41
|
+
milestoneId,
|
|
42
|
+
sliceId,
|
|
43
|
+
gateId: def.id,
|
|
44
|
+
scope: "milestone",
|
|
45
|
+
taskId: "",
|
|
46
|
+
status: "complete",
|
|
47
|
+
verdict: gateVerdict,
|
|
48
|
+
rationale: `${def.promptSection} — milestone validation verdict: ${verdict}`,
|
|
49
|
+
findings: "",
|
|
50
|
+
evaluatedAt,
|
|
52
51
|
});
|
|
53
52
|
}
|
|
54
53
|
}
|
|
@@ -323,7 +323,7 @@ export function nativeIsRepo(basePath: string): boolean {
|
|
|
323
323
|
return native.gitIsRepo(basePath);
|
|
324
324
|
}
|
|
325
325
|
try {
|
|
326
|
-
|
|
326
|
+
execFileSync("git", ["rev-parse", "--git-dir"], { cwd: basePath, stdio: "pipe" });
|
|
327
327
|
return true;
|
|
328
328
|
} catch {
|
|
329
329
|
return false;
|
|
@@ -790,16 +790,15 @@ export function nativeCommit(
|
|
|
790
790
|
|
|
791
791
|
// Fallback: use git commit with stdin pipe for safe multi-line messages
|
|
792
792
|
try {
|
|
793
|
-
const
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
).trim();
|
|
793
|
+
const args = ["commit", "--no-verify", "-F", "-"];
|
|
794
|
+
if (options?.allowEmpty) args.push("--allow-empty");
|
|
795
|
+
const result = execFileSync("git", args, {
|
|
796
|
+
cwd: basePath,
|
|
797
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
798
|
+
encoding: "utf-8",
|
|
799
|
+
env: GIT_NO_PROMPT_ENV,
|
|
800
|
+
input: message,
|
|
801
|
+
}).trim();
|
|
803
802
|
return result;
|
|
804
803
|
} catch (err: unknown) {
|
|
805
804
|
const errObj = err as { stdout?: string; stderr?: string; message?: string };
|
|
@@ -940,7 +939,7 @@ export function nativeResetHard(basePath: string): void {
|
|
|
940
939
|
native.gitResetHard(basePath);
|
|
941
940
|
return;
|
|
942
941
|
}
|
|
943
|
-
|
|
942
|
+
execFileSync("git", ["reset", "--hard", "HEAD"], { cwd: basePath, stdio: "pipe" });
|
|
944
943
|
}
|
|
945
944
|
|
|
946
945
|
/**
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// GSD Extension — Notification Widget
|
|
2
2
|
// Always-on ambient widget rendered belowEditor showing unread count and
|
|
3
|
-
// the most recent notification message. Refreshes every
|
|
3
|
+
// the most recent notification message. Refreshes every 30 seconds.
|
|
4
4
|
// Widget key: "gsd-notifications", placement: "belowEditor"
|
|
5
5
|
|
|
6
6
|
import type { ExtensionContext } from "@gsd/pi-coding-agent";
|
|
@@ -19,7 +19,7 @@ export function buildNotificationWidgetLines(): string[] {
|
|
|
19
19
|
|
|
20
20
|
// ─── Widget init ────────────────────────────────────────────────────────
|
|
21
21
|
|
|
22
|
-
const REFRESH_INTERVAL_MS =
|
|
22
|
+
const REFRESH_INTERVAL_MS = 30_000;
|
|
23
23
|
|
|
24
24
|
/**
|
|
25
25
|
* Initialize the always-on notification widget (belowEditor).
|
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
10
|
+
import { homedir } from "node:os";
|
|
11
|
+
import { join } from "node:path";
|
|
10
12
|
import type { DynamicRoutingConfig } from "./model-router.js";
|
|
11
13
|
import { defaultRoutingConfig } from "./model-router.js";
|
|
12
14
|
import type { TokenProfile, InlineLevel } from "./types.js";
|
|
@@ -185,6 +187,45 @@ export function resolveDefaultSessionModel(
|
|
|
185
187
|
return undefined;
|
|
186
188
|
}
|
|
187
189
|
|
|
190
|
+
/**
|
|
191
|
+
* Returns true if `provider` is defined as a custom provider in the user's
|
|
192
|
+
* `~/.gsd/agent/models.json` (Ollama, vLLM, LM Studio, OpenAI-compatible
|
|
193
|
+
* proxies, etc.).
|
|
194
|
+
*
|
|
195
|
+
* Used by auto-mode bootstrap to decide whether the session model
|
|
196
|
+
* (set via `/gsd model`) should override `PREFERENCES.md`. Custom providers
|
|
197
|
+
* are never reachable from `PREFERENCES.md` (which only knows built-in
|
|
198
|
+
* providers), so when the user has explicitly selected one, it must take
|
|
199
|
+
* priority — otherwise auto-mode tries to start the built-in provider from
|
|
200
|
+
* PREFERENCES.md and fails with "Not logged in · Please run /login" (#4122).
|
|
201
|
+
*
|
|
202
|
+
* Reads models.json directly with a lightweight JSON parse to avoid
|
|
203
|
+
* pulling in the full model-registry at this call site. Falls back to
|
|
204
|
+
* `~/.pi/agent/models.json` for parity with `resolveModelsJsonPath()`.
|
|
205
|
+
* Any read or parse error yields `false` (treat as not-custom) so a
|
|
206
|
+
* malformed models.json never breaks the session bootstrap.
|
|
207
|
+
*/
|
|
208
|
+
export function isCustomProvider(provider: string | undefined): boolean {
|
|
209
|
+
if (!provider) return false;
|
|
210
|
+
const candidates = [
|
|
211
|
+
join(homedir(), ".gsd", "agent", "models.json"),
|
|
212
|
+
join(homedir(), ".pi", "agent", "models.json"),
|
|
213
|
+
];
|
|
214
|
+
for (const path of candidates) {
|
|
215
|
+
if (!existsSync(path)) continue;
|
|
216
|
+
try {
|
|
217
|
+
const raw = readFileSync(path, "utf-8");
|
|
218
|
+
const parsed = JSON.parse(raw) as { providers?: Record<string, unknown> };
|
|
219
|
+
if (parsed?.providers && Object.prototype.hasOwnProperty.call(parsed.providers, provider)) {
|
|
220
|
+
return true;
|
|
221
|
+
}
|
|
222
|
+
} catch {
|
|
223
|
+
// Ignore — malformed models.json must not break bootstrap.
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
return false;
|
|
227
|
+
}
|
|
228
|
+
|
|
188
229
|
/**
|
|
189
230
|
* Determines the next fallback model to try when the current model fails.
|
|
190
231
|
* If the current model is not in the configured list, returns the primary model.
|
|
@@ -113,6 +113,7 @@ export const KNOWN_PREFERENCE_KEYS = new Set<string>([
|
|
|
113
113
|
"discuss_preparation",
|
|
114
114
|
"discuss_web_research",
|
|
115
115
|
"discuss_depth",
|
|
116
|
+
"flat_rate_providers",
|
|
116
117
|
]);
|
|
117
118
|
|
|
118
119
|
/** Canonical list of all dispatch unit types. */
|
|
@@ -359,6 +360,17 @@ export interface GSDPreferences {
|
|
|
359
360
|
* Default: "standard".
|
|
360
361
|
*/
|
|
361
362
|
discuss_depth?: "quick" | "standard" | "thorough";
|
|
363
|
+
/**
|
|
364
|
+
* Extra provider IDs to treat as flat-rate (no cost benefit from dynamic
|
|
365
|
+
* routing). Dynamic routing is suppressed for any provider listed here,
|
|
366
|
+
* in addition to the built-in list (github-copilot, copilot, claude-code)
|
|
367
|
+
* and any provider auto-detected via `authMode: "externalCli"`.
|
|
368
|
+
*
|
|
369
|
+
* Intended for private subscription-backed proxies, enterprise-gated
|
|
370
|
+
* deployments, and custom CLI wrappers where every request costs the
|
|
371
|
+
* same regardless of model. Case-insensitive.
|
|
372
|
+
*/
|
|
373
|
+
flat_rate_providers?: string[];
|
|
362
374
|
}
|
|
363
375
|
|
|
364
376
|
export interface LoadedGSDPreferences {
|
|
@@ -180,6 +180,29 @@ export function validatePreferences(preferences: GSDPreferences): {
|
|
|
180
180
|
}
|
|
181
181
|
}
|
|
182
182
|
|
|
183
|
+
// ─── Flat-rate Providers ────────────────────────────────────────────
|
|
184
|
+
// User-declared flat-rate providers for dynamic routing suppression.
|
|
185
|
+
// Built-in providers (github-copilot, copilot, claude-code) and any
|
|
186
|
+
// externalCli provider are already auto-detected; this list layers on
|
|
187
|
+
// top for private subscription proxies and custom CLI wrappers.
|
|
188
|
+
if (preferences.flat_rate_providers !== undefined) {
|
|
189
|
+
if (Array.isArray(preferences.flat_rate_providers)) {
|
|
190
|
+
const allStrings = preferences.flat_rate_providers.every(
|
|
191
|
+
(item: unknown) => typeof item === "string",
|
|
192
|
+
);
|
|
193
|
+
if (allStrings) {
|
|
194
|
+
// Strip empty/whitespace-only entries to avoid false matches.
|
|
195
|
+
validated.flat_rate_providers = preferences.flat_rate_providers
|
|
196
|
+
.map((s: string) => s.trim())
|
|
197
|
+
.filter((s: string) => s.length > 0);
|
|
198
|
+
} else {
|
|
199
|
+
errors.push("flat_rate_providers must be an array of strings");
|
|
200
|
+
}
|
|
201
|
+
} else {
|
|
202
|
+
errors.push("flat_rate_providers must be an array of strings");
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
183
206
|
// ─── Phase Skip Preferences ─────────────────────────────────────────
|
|
184
207
|
if (preferences.phases !== undefined) {
|
|
185
208
|
if (typeof preferences.phases === "object" && preferences.phases !== null) {
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
You are generating tests for recently completed GSD work.
|
|
2
|
+
|
|
3
|
+
## Slice: {{sliceId}} — {{sliceTitle}}
|
|
4
|
+
|
|
5
|
+
### Summary
|
|
6
|
+
|
|
7
|
+
{{sliceSummary}}
|
|
8
|
+
|
|
9
|
+
### Existing Test Patterns
|
|
10
|
+
|
|
11
|
+
{{existingTestPatterns}}
|
|
12
|
+
|
|
13
|
+
## Working Directory
|
|
14
|
+
|
|
15
|
+
`{{workingDirectory}}`
|
|
16
|
+
|
|
17
|
+
## Instructions
|
|
18
|
+
|
|
19
|
+
1. Read the slice summary above to understand what was built
|
|
20
|
+
2. Identify the source files that were created or modified for this slice
|
|
21
|
+
3. Read the implementation code to understand behavior, edge cases, and error paths
|
|
22
|
+
4. Write comprehensive tests following the project's existing test patterns and framework
|
|
23
|
+
5. Run the tests to verify they pass
|
|
24
|
+
6. Fix any failures
|
|
25
|
+
|
|
26
|
+
### Rules
|
|
27
|
+
|
|
28
|
+
- Follow the project's existing test patterns (framework, assertions, file structure)
|
|
29
|
+
- Test behavior, not implementation details
|
|
30
|
+
- Cover: happy path, edge cases, error conditions, boundary values
|
|
31
|
+
- Do NOT modify implementation files — only create or update test files
|
|
32
|
+
- Name test files consistently with the project's conventions
|
|
33
|
+
- Keep tests focused and readable
|
|
34
|
+
|
|
35
|
+
{{skillActivation}}
|
|
@@ -345,8 +345,15 @@ function reconcileDiskToDb(basePath: string): MilestoneRow[] {
|
|
|
345
345
|
const dbSliceIds = new Set(dbSlices.map(s => s.id));
|
|
346
346
|
|
|
347
347
|
let roadmapContent: string;
|
|
348
|
-
try {
|
|
349
|
-
|
|
348
|
+
try {
|
|
349
|
+
roadmapContent = readFileSync(roadmapPath, "utf-8");
|
|
350
|
+
} catch (err) {
|
|
351
|
+
logWarning("state", "reconcileDiskToDb: roadmap read failed, skipping milestone", {
|
|
352
|
+
mid,
|
|
353
|
+
error: (err as Error).message,
|
|
354
|
+
});
|
|
355
|
+
continue;
|
|
356
|
+
}
|
|
350
357
|
|
|
351
358
|
const parsed = parseRoadmap(roadmapContent);
|
|
352
359
|
for (const s of parsed.slices) {
|
|
@@ -386,6 +393,10 @@ function buildCompletenessSet(basePath: string, milestones: MilestoneRow[]) {
|
|
|
386
393
|
const completeMilestoneIds = new Set<string>();
|
|
387
394
|
const parkedMilestoneIds = new Set<string>();
|
|
388
395
|
|
|
396
|
+
// DB-authoritative: a milestone is only "complete" when its DB row says so.
|
|
397
|
+
// SUMMARY-file presence is NOT a completion signal here — an orphan SUMMARY
|
|
398
|
+
// (crashed complete-milestone turn, partial merge, manual edit) must not
|
|
399
|
+
// flip derived state to complete and cascade into a false auto-merge (#4179).
|
|
389
400
|
for (const m of milestones) {
|
|
390
401
|
const parkedFile = resolveMilestoneFile(basePath, m.id, "PARKED");
|
|
391
402
|
if (parkedFile || m.status === 'parked') {
|
|
@@ -396,11 +407,6 @@ function buildCompletenessSet(basePath: string, milestones: MilestoneRow[]) {
|
|
|
396
407
|
completeMilestoneIds.add(m.id);
|
|
397
408
|
continue;
|
|
398
409
|
}
|
|
399
|
-
const summaryFile = resolveMilestoneFile(basePath, m.id, "SUMMARY");
|
|
400
|
-
if (summaryFile) {
|
|
401
|
-
completeMilestoneIds.add(m.id);
|
|
402
|
-
continue;
|
|
403
|
-
}
|
|
404
410
|
}
|
|
405
411
|
return { completeMilestoneIds, parkedMilestoneIds };
|
|
406
412
|
}
|
|
@@ -429,18 +435,22 @@ async function buildRegistryAndFindActive(
|
|
|
429
435
|
if (isGhostMilestone(basePath, m.id)) continue;
|
|
430
436
|
}
|
|
431
437
|
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
438
|
+
// DB-authoritative completeness (#4179): only trust completeMilestoneIds,
|
|
439
|
+
// which is itself derived from DB status. SUMMARY-file presence alone must
|
|
440
|
+
// not imply completion. The summary file may still be consulted below as a
|
|
441
|
+
// title source for legitimately-complete milestones whose DB row has no title.
|
|
442
|
+
if (completeMilestoneIds.has(m.id)) {
|
|
435
443
|
let title = stripMilestonePrefix(m.title) || m.id;
|
|
436
|
-
if (
|
|
437
|
-
const
|
|
438
|
-
if (
|
|
439
|
-
|
|
444
|
+
if (!m.title) {
|
|
445
|
+
const summaryFile = resolveMilestoneFile(basePath, m.id, "SUMMARY");
|
|
446
|
+
if (summaryFile) {
|
|
447
|
+
const summaryContent = await loadFile(summaryFile);
|
|
448
|
+
if (summaryContent) {
|
|
449
|
+
title = parseSummary(summaryContent).title || m.id;
|
|
450
|
+
}
|
|
440
451
|
}
|
|
441
452
|
}
|
|
442
453
|
registry.push({ id: m.id, title, status: 'complete' });
|
|
443
|
-
completeMilestoneIds.add(m.id);
|
|
444
454
|
continue;
|
|
445
455
|
}
|
|
446
456
|
|
|
@@ -481,7 +491,14 @@ async function buildRegistryAndFindActive(
|
|
|
481
491
|
const validationContent = validationFile ? await loadFile(validationFile) : null;
|
|
482
492
|
const validationTerminal = validationContent ? isValidationTerminal(validationContent) : false;
|
|
483
493
|
|
|
484
|
-
|
|
494
|
+
// DB-authoritative (#4179): completeness is already decided by
|
|
495
|
+
// completeMilestoneIds above. If we reached this branch, the DB says
|
|
496
|
+
// the milestone is NOT complete — so any SUMMARY file on disk is an
|
|
497
|
+
// orphan (crashed complete-milestone, partial merge, manual edit) and
|
|
498
|
+
// must not short-circuit this path. When validation is terminal, fall
|
|
499
|
+
// through to the default active-push below so `complete-milestone` can
|
|
500
|
+
// re-run idempotently.
|
|
501
|
+
if (!validationTerminal) {
|
|
485
502
|
activeMilestone = { id: m.id, title };
|
|
486
503
|
activeMilestoneSlices = slices;
|
|
487
504
|
activeMilestoneFound = true;
|
|
@@ -630,13 +647,39 @@ function resolveSliceDependencies(activeMilestoneSlices: SliceRow[]): { activeSl
|
|
|
630
647
|
}
|
|
631
648
|
}
|
|
632
649
|
|
|
650
|
+
// First pass: find a slice with ALL dependencies satisfied (strict)
|
|
651
|
+
let bestFallback: SliceRow | null = null;
|
|
652
|
+
let bestFallbackSatisfied = -1;
|
|
653
|
+
|
|
633
654
|
for (const s of activeMilestoneSlices) {
|
|
634
655
|
if (isStatusDone(s.status)) continue;
|
|
635
656
|
if (isDeferredStatus(s.status)) continue;
|
|
636
657
|
if (s.depends.every(dep => doneSliceIds.has(dep))) {
|
|
637
658
|
return { activeSlice: { id: s.id, title: s.title }, activeSliceRow: s };
|
|
638
659
|
}
|
|
660
|
+
// Track the slice with the most satisfied dependencies as fallback
|
|
661
|
+
const satisfied = s.depends.filter(dep => doneSliceIds.has(dep)).length;
|
|
662
|
+
if (satisfied > bestFallbackSatisfied || (satisfied === bestFallbackSatisfied && !bestFallback)) {
|
|
663
|
+
bestFallback = s;
|
|
664
|
+
bestFallbackSatisfied = satisfied;
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
// Fallback: if no slice has all deps met but there ARE incomplete non-deferred
|
|
669
|
+
// slices, pick the one with the most deps satisfied. This prevents hard-blocking
|
|
670
|
+
// when dependency metadata is stale (e.g. after reassessment added/removed slices)
|
|
671
|
+
// or when deps reference slices from previous milestones.
|
|
672
|
+
if (bestFallback) {
|
|
673
|
+
const unmet = bestFallback.depends.filter(dep => !doneSliceIds.has(dep));
|
|
674
|
+
logWarning("state",
|
|
675
|
+
`No slice has all deps satisfied — falling back to ${bestFallback.id} ` +
|
|
676
|
+
`(${bestFallbackSatisfied}/${bestFallback.depends.length} deps met, ` +
|
|
677
|
+
`unmet: ${unmet.join(", ")})`,
|
|
678
|
+
{ mid: activeMilestoneSlices[0]?.milestone_id, sid: bestFallback.id },
|
|
679
|
+
);
|
|
680
|
+
return { activeSlice: { id: bestFallback.id, title: bestFallback.title }, activeSliceRow: bestFallback };
|
|
639
681
|
}
|
|
682
|
+
|
|
640
683
|
return { activeSlice: null, activeSliceRow: null };
|
|
641
684
|
}
|
|
642
685
|
|
|
@@ -684,7 +727,7 @@ async function reconcileSliceTasks(
|
|
|
684
727
|
const summaryPath = resolveTaskFile(basePath, milestoneId, sliceId, t.id, "SUMMARY");
|
|
685
728
|
if (summaryPath && existsSync(summaryPath)) {
|
|
686
729
|
try {
|
|
687
|
-
updateTaskStatus(milestoneId, sliceId, t.id, "complete");
|
|
730
|
+
updateTaskStatus(milestoneId, sliceId, t.id, "complete", new Date().toISOString());
|
|
688
731
|
logWarning("reconcile", `task ${milestoneId}/${sliceId}/${t.id} status reconciled from "${t.status}" to "complete" (#2514)`, { mid: milestoneId, sid: sliceId, tid: t.id });
|
|
689
732
|
reconciled = true;
|
|
690
733
|
} catch (e) {
|
|
@@ -1431,12 +1474,32 @@ export async function _deriveStateImpl(basePath: string): Promise<GSDState> {
|
|
|
1431
1474
|
};
|
|
1432
1475
|
}
|
|
1433
1476
|
} else {
|
|
1477
|
+
let bestFallbackLegacy: { id: string; title: string; depends: string[] } | null = null;
|
|
1478
|
+
let bestFallbackLegacySatisfied = -1;
|
|
1479
|
+
|
|
1434
1480
|
for (const s of activeRoadmap.slices) {
|
|
1435
1481
|
if (s.done) continue;
|
|
1436
1482
|
if (s.depends.every(dep => doneSliceIds.has(dep))) {
|
|
1437
1483
|
activeSlice = { id: s.id, title: s.title };
|
|
1438
1484
|
break;
|
|
1439
1485
|
}
|
|
1486
|
+
// Track best fallback
|
|
1487
|
+
const satisfied = s.depends.filter(dep => doneSliceIds.has(dep)).length;
|
|
1488
|
+
if (satisfied > bestFallbackLegacySatisfied) {
|
|
1489
|
+
bestFallbackLegacy = s;
|
|
1490
|
+
bestFallbackLegacySatisfied = satisfied;
|
|
1491
|
+
}
|
|
1492
|
+
}
|
|
1493
|
+
|
|
1494
|
+
// Fallback: if no slice has all deps met, pick the one with the most deps satisfied
|
|
1495
|
+
if (!activeSlice && bestFallbackLegacy) {
|
|
1496
|
+
const unmet = bestFallbackLegacy.depends.filter(dep => !doneSliceIds.has(dep));
|
|
1497
|
+
logWarning("state",
|
|
1498
|
+
`No slice has all deps satisfied — falling back to ${bestFallbackLegacy.id} ` +
|
|
1499
|
+
`(${bestFallbackLegacySatisfied}/${bestFallbackLegacy.depends.length} deps met, ` +
|
|
1500
|
+
`unmet: ${unmet.join(", ")})`,
|
|
1501
|
+
);
|
|
1502
|
+
activeSlice = { id: bestFallbackLegacy.id, title: bestFallbackLegacy.title };
|
|
1440
1503
|
}
|
|
1441
1504
|
}
|
|
1442
1505
|
|
|
@@ -688,8 +688,8 @@ test("autoLoop exits on terminal blocked state", async (t) => {
|
|
|
688
688
|
|
|
689
689
|
assert.ok(deps.callLog.includes("deriveState"), "should have derived state");
|
|
690
690
|
assert.ok(
|
|
691
|
-
deps.callLog.includes("
|
|
692
|
-
"should have called
|
|
691
|
+
deps.callLog.includes("pauseAuto"),
|
|
692
|
+
"should have called pauseAuto for blocked state",
|
|
693
693
|
);
|
|
694
694
|
assert.ok(
|
|
695
695
|
!deps.callLog.includes("resolveDispatch"),
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// GSD-2 — Tests for step-mode completion messages in auto-post-unit
|
|
2
|
+
|
|
3
|
+
import test from "node:test";
|
|
4
|
+
import assert from "node:assert/strict";
|
|
5
|
+
|
|
6
|
+
import { buildStepCompleteMessage, STEP_COMPLETE_FALLBACK_MESSAGE } from "../auto-post-unit.ts";
|
|
7
|
+
import type { GSDState } from "../types.ts";
|
|
8
|
+
|
|
9
|
+
function makeState(overrides: Partial<GSDState>): GSDState {
|
|
10
|
+
return {
|
|
11
|
+
activeMilestone: null,
|
|
12
|
+
activeSlice: null,
|
|
13
|
+
activeTask: null,
|
|
14
|
+
phase: "executing",
|
|
15
|
+
recentDecisions: [],
|
|
16
|
+
blockers: [],
|
|
17
|
+
nextAction: "",
|
|
18
|
+
registry: [],
|
|
19
|
+
...overrides,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
test("buildStepCompleteMessage: milestone complete surfaces review guidance", () => {
|
|
24
|
+
const msg = buildStepCompleteMessage(makeState({ phase: "complete" }));
|
|
25
|
+
assert.match(msg, /milestone finished/);
|
|
26
|
+
assert.match(msg, /\/gsd status/);
|
|
27
|
+
assert.doesNotMatch(msg, /Next:/);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test("buildStepCompleteMessage: mid-flight step includes next unit label and /clear hint", () => {
|
|
31
|
+
const state = makeState({
|
|
32
|
+
phase: "executing",
|
|
33
|
+
activeSlice: { id: "S01", title: "Core" },
|
|
34
|
+
activeTask: { id: "T03", title: "Wire notify" },
|
|
35
|
+
});
|
|
36
|
+
const msg = buildStepCompleteMessage(state);
|
|
37
|
+
assert.match(msg, /Next: Execute T03: Wire notify/);
|
|
38
|
+
assert.match(msg, /\/clear/);
|
|
39
|
+
assert.match(msg, /\/gsd to continue/);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test("buildStepCompleteMessage: unknown phase falls back to generic continue label", () => {
|
|
43
|
+
// Cast to bypass Phase union so we exercise the default branch of describeNextUnit.
|
|
44
|
+
const state = makeState({ phase: "totally-unknown" as unknown as GSDState["phase"] });
|
|
45
|
+
const msg = buildStepCompleteMessage(state);
|
|
46
|
+
assert.match(msg, /Next: Continue/);
|
|
47
|
+
assert.match(msg, /\/clear/);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test("STEP_COMPLETE_FALLBACK_MESSAGE: used when deriveState throws, still points users at /clear + /gsd", () => {
|
|
51
|
+
assert.match(STEP_COMPLETE_FALLBACK_MESSAGE, /\/clear/);
|
|
52
|
+
assert.match(STEP_COMPLETE_FALLBACK_MESSAGE, /\/gsd/);
|
|
53
|
+
});
|