gsd-pi 2.51.0 → 2.52.0-dev.585e355
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/dist/headless-events.d.ts +18 -0
- package/dist/headless-events.js +36 -0
- package/dist/headless-types.d.ts +28 -0
- package/dist/headless-types.js +7 -0
- package/dist/headless.d.ts +8 -3
- package/dist/headless.js +47 -16
- package/dist/help-text.js +16 -5
- package/dist/onboarding.js +5 -4
- package/dist/remote-questions-config.js +1 -1
- package/dist/resources/extensions/async-jobs/async-bash-tool.js +29 -17
- package/dist/resources/extensions/async-jobs/job-manager.js +4 -1
- package/dist/resources/extensions/claude-code-cli/stream-adapter.js +18 -19
- package/dist/resources/extensions/gsd/auto/phases.js +6 -0
- package/dist/resources/extensions/gsd/auto-dispatch.js +18 -0
- package/dist/resources/extensions/gsd/auto-start.js +2 -0
- package/dist/resources/extensions/gsd/auto-timers.js +24 -2
- package/dist/resources/extensions/gsd/auto-tool-tracking.js +25 -7
- package/dist/resources/extensions/gsd/auto-worktree.js +21 -0
- package/dist/resources/extensions/gsd/auto.js +8 -4
- package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +105 -70
- package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +12 -2
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +1 -1
- package/dist/resources/extensions/gsd/claude-import.js +60 -9
- package/dist/resources/extensions/gsd/commands/handlers/auto.js +69 -6
- package/dist/resources/extensions/gsd/commands-config.js +10 -5
- package/dist/resources/extensions/gsd/commands-prefs-wizard.js +4 -4
- package/dist/resources/extensions/gsd/detection.js +6 -6
- package/dist/resources/extensions/gsd/docs/preferences-reference.md +5 -5
- package/dist/resources/extensions/gsd/error-classifier.js +105 -0
- package/dist/resources/extensions/gsd/git-service.js +4 -3
- package/dist/resources/extensions/gsd/gitignore.js +7 -7
- package/dist/resources/extensions/gsd/gsd-db.js +298 -45
- package/dist/resources/extensions/gsd/init-wizard.js +2 -2
- package/dist/resources/extensions/gsd/key-manager.js +7 -16
- package/dist/resources/extensions/gsd/markdown-renderer.js +5 -4
- package/dist/resources/extensions/gsd/memory-store.js +28 -13
- package/dist/resources/extensions/gsd/milestone-actions.js +19 -0
- package/dist/resources/extensions/gsd/preferences-models.js +1 -13
- package/dist/resources/extensions/gsd/preferences-types.js +1 -1
- package/dist/resources/extensions/gsd/preferences.js +13 -13
- package/dist/resources/extensions/gsd/prompts/system.md +1 -1
- package/dist/resources/extensions/gsd/provider-error-pause.js +0 -44
- package/dist/resources/extensions/gsd/rule-registry.js +1 -1
- package/dist/resources/extensions/gsd/service-tier.js +13 -2
- package/dist/resources/extensions/gsd/state.js +33 -19
- package/dist/resources/extensions/gsd/status-guards.js +12 -0
- package/dist/resources/extensions/gsd/tools/complete-milestone.js +7 -13
- package/dist/resources/extensions/gsd/tools/complete-slice.js +7 -20
- package/dist/resources/extensions/gsd/tools/complete-task.js +11 -21
- package/dist/resources/extensions/gsd/tools/plan-milestone.js +28 -29
- package/dist/resources/extensions/gsd/tools/plan-slice.js +27 -26
- package/dist/resources/extensions/gsd/tools/plan-task.js +23 -23
- package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +50 -41
- package/dist/resources/extensions/gsd/tools/reopen-slice.js +4 -3
- package/dist/resources/extensions/gsd/tools/reopen-task.js +5 -4
- package/dist/resources/extensions/gsd/tools/replan-slice.js +51 -41
- package/dist/resources/extensions/gsd/tools/validate-milestone.js +23 -16
- package/dist/resources/extensions/gsd/validation.js +21 -0
- package/dist/resources/extensions/gsd/workflow-logger.js +0 -1
- package/dist/resources/extensions/remote-questions/config.js +1 -1
- package/dist/resources/extensions/remote-questions/remote-command.js +1 -1
- package/dist/resources/extensions/search-the-web/native-search.js +1 -1
- package/dist/resources/extensions/search-the-web/provider.js +1 -1
- package/dist/resources/extensions/shared/rtk.js +5 -3
- package/dist/rtk.js +3 -1
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +15 -15
- package/dist/web/standalone/.next/build-manifest.json +4 -4
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
- 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 +4 -4
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +4 -4
- 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 +4 -4
- 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 +2 -2
- 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/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 +5 -5
- 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 +5 -5
- 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 +4 -4
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/dist/web/standalone/.next/server/app/page.js +2 -2
- package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +15 -15
- package/dist/web/standalone/.next/server/chunks/2229.js +3 -3
- 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-react-loadable-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/4024.21054f459af5cc78.js +9 -0
- 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-b950e4e384cc62b3.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/.next/static/chunks/{webpack-cfc9a116e6450a6b.js → webpack-024d82be84800e52.js} +1 -1
- package/dist/web/standalone/.next/static/css/a58ef8a151aa0493.css +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/dist/wizard.js +4 -1
- package/package.json +2 -2
- package/packages/mcp-server/README.md +202 -0
- package/packages/mcp-server/package.json +36 -0
- package/packages/mcp-server/src/cli.ts +68 -0
- package/packages/mcp-server/src/index.ts +14 -0
- package/packages/mcp-server/src/mcp-server.test.ts +628 -0
- package/packages/mcp-server/src/server.ts +278 -0
- package/packages/mcp-server/src/session-manager.ts +328 -0
- package/packages/mcp-server/src/types.ts +107 -0
- package/packages/mcp-server/tsconfig.json +24 -0
- package/packages/pi-ai/dist/models.d.ts +14 -3
- package/packages/pi-ai/dist/models.d.ts.map +1 -1
- package/packages/pi-ai/dist/models.js +53 -10
- package/packages/pi-ai/dist/models.js.map +1 -1
- package/packages/pi-ai/dist/models.test.js +102 -1
- package/packages/pi-ai/dist/models.test.js.map +1 -1
- package/packages/pi-ai/dist/types.d.ts +30 -0
- package/packages/pi-ai/dist/types.d.ts.map +1 -1
- package/packages/pi-ai/dist/types.js.map +1 -1
- package/packages/pi-ai/src/models.test.ts +114 -1
- package/packages/pi-ai/src/models.ts +70 -13
- package/packages/pi-ai/src/types.ts +31 -0
- package/packages/pi-coding-agent/dist/cli/args.d.ts +2 -0
- package/packages/pi-coding-agent/dist/cli/args.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/cli/args.js +3 -0
- package/packages/pi-coding-agent/dist/cli/args.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/bash-executor.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/bash-executor.js +5 -1
- package/packages/pi-coding-agent/dist/core/bash-executor.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.js +9 -4
- package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/bash-spawn-windows.test.d.ts +19 -0
- package/packages/pi-coding-agent/dist/core/tools/bash-spawn-windows.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/tools/bash-spawn-windows.test.js +83 -0
- package/packages/pi-coding-agent/dist/core/tools/bash-spawn-windows.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/tools/bash.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/bash.js +5 -1
- package/packages/pi-coding-agent/dist/core/tools/bash.js.map +1 -1
- package/packages/pi-coding-agent/dist/index.d.ts +1 -1
- package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/index.js.map +1 -1
- package/packages/pi-coding-agent/dist/main.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/main.js +5 -3
- package/packages/pi-coding-agent/dist/main.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/index.d.ts +1 -1
- package/packages/pi-coding-agent/dist/modes/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/index.js.map +1 -1
- 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 +0 -2
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.d.ts +28 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.js +49 -0
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.d.ts +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js +114 -6
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-protocol-v2.test.d.ts +9 -0
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-protocol-v2.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-protocol-v2.test.js +831 -0
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-protocol-v2.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-types.d.ts +66 -0
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-types.js.map +1 -1
- package/packages/pi-coding-agent/dist/utils/shell.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/utils/shell.js +0 -1
- package/packages/pi-coding-agent/dist/utils/shell.js.map +1 -1
- package/packages/pi-coding-agent/package.json +1 -1
- package/packages/pi-coding-agent/src/cli/args.ts +4 -0
- package/packages/pi-coding-agent/src/core/bash-executor.ts +5 -1
- package/packages/pi-coding-agent/src/core/model-registry.ts +10 -3
- package/packages/pi-coding-agent/src/core/tools/bash-spawn-windows.test.ts +101 -0
- package/packages/pi-coding-agent/src/core/tools/bash.ts +5 -1
- package/packages/pi-coding-agent/src/index.ts +3 -0
- package/packages/pi-coding-agent/src/main.ts +5 -3
- package/packages/pi-coding-agent/src/modes/index.ts +8 -1
- package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +0 -2
- package/packages/pi-coding-agent/src/modes/rpc/rpc-client.ts +54 -1
- package/packages/pi-coding-agent/src/modes/rpc/rpc-mode.ts +124 -6
- package/packages/pi-coding-agent/src/modes/rpc/rpc-protocol-v2.test.ts +971 -0
- package/packages/pi-coding-agent/src/modes/rpc/rpc-types.ts +61 -4
- package/packages/pi-coding-agent/src/utils/shell.ts +0 -1
- package/packages/rpc-client/package.json +20 -0
- package/pkg/package.json +1 -1
- package/scripts/ensure-workspace-builds.cjs +36 -8
- package/src/resources/extensions/async-jobs/async-bash-tool.ts +22 -11
- package/src/resources/extensions/async-jobs/job-manager.ts +4 -1
- package/src/resources/extensions/claude-code-cli/stream-adapter.ts +19 -20
- package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +21 -0
- package/src/resources/extensions/gsd/auto/phases.ts +6 -0
- package/src/resources/extensions/gsd/auto-dispatch.ts +19 -0
- package/src/resources/extensions/gsd/auto-start.ts +2 -0
- package/src/resources/extensions/gsd/auto-timers.ts +25 -1
- package/src/resources/extensions/gsd/auto-tool-tracking.ts +30 -6
- package/src/resources/extensions/gsd/auto-worktree.ts +21 -0
- package/src/resources/extensions/gsd/auto.ts +10 -4
- package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +125 -73
- package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +11 -2
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +1 -1
- package/src/resources/extensions/gsd/claude-import.ts +58 -9
- package/src/resources/extensions/gsd/commands/handlers/auto.ts +73 -6
- package/src/resources/extensions/gsd/commands-config.ts +11 -5
- package/src/resources/extensions/gsd/commands-prefs-wizard.ts +4 -4
- package/src/resources/extensions/gsd/detection.ts +6 -6
- package/src/resources/extensions/gsd/docs/preferences-reference.md +5 -5
- package/src/resources/extensions/gsd/error-classifier.ts +139 -0
- package/src/resources/extensions/gsd/git-service.ts +4 -3
- package/src/resources/extensions/gsd/gitignore.ts +7 -7
- package/src/resources/extensions/gsd/gsd-db.ts +355 -63
- package/src/resources/extensions/gsd/init-wizard.ts +2 -2
- package/src/resources/extensions/gsd/key-manager.ts +7 -16
- package/src/resources/extensions/gsd/markdown-renderer.ts +5 -4
- package/src/resources/extensions/gsd/memory-store.ts +29 -18
- package/src/resources/extensions/gsd/milestone-actions.ts +17 -0
- package/src/resources/extensions/gsd/preferences-models.ts +1 -13
- package/src/resources/extensions/gsd/preferences-types.ts +1 -1
- package/src/resources/extensions/gsd/preferences.ts +12 -13
- package/src/resources/extensions/gsd/prompts/system.md +1 -1
- package/src/resources/extensions/gsd/provider-error-pause.ts +0 -57
- package/src/resources/extensions/gsd/rule-registry.ts +1 -1
- package/src/resources/extensions/gsd/service-tier.ts +14 -2
- package/src/resources/extensions/gsd/state.ts +34 -20
- package/src/resources/extensions/gsd/status-guards.ts +13 -0
- package/src/resources/extensions/gsd/tests/agent-end-retry.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/auto-milestone-target.test.ts +61 -0
- package/src/resources/extensions/gsd/tests/claude-import-marketplace-discovery.test.ts +191 -0
- package/src/resources/extensions/gsd/tests/claude-import-tui.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/commands-config.test.ts +24 -0
- package/src/resources/extensions/gsd/tests/complete-slice.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/complete-task-rollback-evidence.test.ts +106 -0
- package/src/resources/extensions/gsd/tests/complete-task.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +35 -7
- package/src/resources/extensions/gsd/tests/detection.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/doctor-git.test.ts +4 -4
- package/src/resources/extensions/gsd/tests/doctor-proactive.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/doctor-providers.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/empty-db-reconciliation.test.ts +79 -0
- package/src/resources/extensions/gsd/tests/git-service.test.ts +37 -4
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/idle-watchdog-stall-override.test.ts +125 -0
- package/src/resources/extensions/gsd/tests/init-wizard.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/interactive-tool-idle-exemption.test.ts +119 -0
- package/src/resources/extensions/gsd/tests/key-manager.test.ts +16 -1
- package/src/resources/extensions/gsd/tests/md-importer.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/memory-store.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/none-mode-gates.test.ts +7 -7
- package/src/resources/extensions/gsd/tests/park-db-sync.test.ts +85 -0
- package/src/resources/extensions/gsd/tests/preferences-worktree-sync.test.ts +91 -0
- package/src/resources/extensions/gsd/tests/preferences.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/provider-errors.test.ts +77 -70
- package/src/resources/extensions/gsd/tests/remediation-completion-guard.test.ts +110 -0
- package/src/resources/extensions/gsd/tests/remote-questions.test.ts +29 -0
- package/src/resources/extensions/gsd/tests/status-guards.test.ts +30 -0
- package/src/resources/extensions/gsd/tests/terminated-transient.test.ts +42 -31
- package/src/resources/extensions/gsd/tests/token-cost-display.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/vacuous-truth-slices.test.ts +115 -0
- package/src/resources/extensions/gsd/tests/validate-milestone-write-order.test.ts +90 -0
- package/src/resources/extensions/gsd/tests/validation.test.ts +72 -0
- package/src/resources/extensions/gsd/tests/workflow-logger.test.ts +81 -1
- package/src/resources/extensions/gsd/tests/worktree-preferences-sync.test.ts +130 -0
- package/src/resources/extensions/gsd/tools/complete-milestone.ts +7 -17
- package/src/resources/extensions/gsd/tools/complete-slice.ts +7 -24
- package/src/resources/extensions/gsd/tools/complete-task.ts +13 -25
- package/src/resources/extensions/gsd/tools/plan-milestone.ts +30 -32
- package/src/resources/extensions/gsd/tools/plan-slice.ts +30 -30
- package/src/resources/extensions/gsd/tools/plan-task.ts +26 -26
- package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +57 -46
- package/src/resources/extensions/gsd/tools/reopen-slice.ts +4 -3
- package/src/resources/extensions/gsd/tools/reopen-task.ts +5 -4
- package/src/resources/extensions/gsd/tools/replan-slice.ts +55 -44
- package/src/resources/extensions/gsd/tools/validate-milestone.ts +26 -20
- package/src/resources/extensions/gsd/validation.ts +23 -0
- package/src/resources/extensions/gsd/workflow-logger.ts +0 -1
- package/src/resources/extensions/remote-questions/config.ts +1 -1
- package/src/resources/extensions/remote-questions/remote-command.ts +1 -1
- package/src/resources/extensions/search-the-web/native-search.ts +1 -1
- package/src/resources/extensions/search-the-web/provider.ts +1 -1
- package/src/resources/extensions/shared/rtk.ts +12 -3
- package/dist/web/standalone/.next/static/chunks/4024.9ad5def014d90ce4.js +0 -9
- package/dist/web/standalone/.next/static/chunks/app/page-fbecd1237e2d6d1f.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/css/de141508b083f922.css +0 -1
- /package/dist/resources/extensions/gsd/templates/{preferences.md → PREFERENCES.md} +0 -0
- /package/dist/web/standalone/.next/static/{vkr67v-utm1dgZnbrBWQh → KTe1kB5nPLQFIIFz2OcmI}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{vkr67v-utm1dgZnbrBWQh → KTe1kB5nPLQFIIFz2OcmI}/_ssgManifest.js +0 -0
- /package/src/resources/extensions/gsd/templates/{preferences.md → PREFERENCES.md} +0 -0
|
@@ -71,6 +71,9 @@ export function getActiveMemoriesRanked(limit = 30) {
|
|
|
71
71
|
/**
|
|
72
72
|
* Generate the next memory ID: MEM + zero-padded 3-digit from MAX(seq).
|
|
73
73
|
* Returns MEM001 if no memories exist.
|
|
74
|
+
*
|
|
75
|
+
* NOTE: For race-safe creation, prefer createMemory() which inserts with a
|
|
76
|
+
* placeholder ID then updates to the seq-derived ID atomically.
|
|
74
77
|
*/
|
|
75
78
|
export function nextMemoryId() {
|
|
76
79
|
if (!isDbAvailable())
|
|
@@ -94,7 +97,9 @@ export function nextMemoryId() {
|
|
|
94
97
|
}
|
|
95
98
|
// ─── Mutation Functions ─────────────────────────────────────────────────────
|
|
96
99
|
/**
|
|
97
|
-
* Insert a new memory with auto-assigned ID.
|
|
100
|
+
* Insert a new memory with a race-safe auto-assigned ID.
|
|
101
|
+
* Uses AUTOINCREMENT seq to derive the ID after insert, avoiding
|
|
102
|
+
* the read-then-write race in concurrent scenarios (e.g. worktrees).
|
|
98
103
|
* Returns the assigned ID, or null on failure.
|
|
99
104
|
*/
|
|
100
105
|
export function createMemory(fields) {
|
|
@@ -104,11 +109,12 @@ export function createMemory(fields) {
|
|
|
104
109
|
if (!adapter)
|
|
105
110
|
return null;
|
|
106
111
|
try {
|
|
107
|
-
const id = nextMemoryId();
|
|
108
112
|
const now = new Date().toISOString();
|
|
113
|
+
// Insert with a temporary placeholder ID — seq is auto-assigned
|
|
114
|
+
const placeholder = `_TMP_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
109
115
|
adapter.prepare(`INSERT INTO memories (id, category, content, confidence, source_unit_type, source_unit_id, created_at, updated_at)
|
|
110
116
|
VALUES (:id, :category, :content, :confidence, :source_unit_type, :source_unit_id, :created_at, :updated_at)`).run({
|
|
111
|
-
':id':
|
|
117
|
+
':id': placeholder,
|
|
112
118
|
':category': fields.category,
|
|
113
119
|
':content': fields.content,
|
|
114
120
|
':confidence': fields.confidence ?? 0.8,
|
|
@@ -117,7 +123,17 @@ export function createMemory(fields) {
|
|
|
117
123
|
':created_at': now,
|
|
118
124
|
':updated_at': now,
|
|
119
125
|
});
|
|
120
|
-
|
|
126
|
+
// Derive the real ID from the assigned seq
|
|
127
|
+
const row = adapter.prepare('SELECT seq FROM memories WHERE id = :id').get({ ':id': placeholder });
|
|
128
|
+
if (!row)
|
|
129
|
+
return placeholder; // fallback — should not happen
|
|
130
|
+
const seq = row['seq'];
|
|
131
|
+
const realId = `MEM${String(seq).padStart(3, '0')}`;
|
|
132
|
+
adapter.prepare('UPDATE memories SET id = :real_id WHERE id = :placeholder').run({
|
|
133
|
+
':real_id': realId,
|
|
134
|
+
':placeholder': placeholder,
|
|
135
|
+
});
|
|
136
|
+
return realId;
|
|
121
137
|
}
|
|
122
138
|
catch {
|
|
123
139
|
return null;
|
|
@@ -258,15 +274,14 @@ export function enforceMemoryCap(max = 50) {
|
|
|
258
274
|
if (count <= max)
|
|
259
275
|
return;
|
|
260
276
|
const excess = count - max;
|
|
261
|
-
//
|
|
262
|
-
|
|
263
|
-
WHERE
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
}
|
|
277
|
+
// Batch update: supersede lowest-ranked active memories in a single statement
|
|
278
|
+
adapter.prepare(`UPDATE memories SET superseded_by = 'CAP_EXCEEDED', updated_at = :now
|
|
279
|
+
WHERE id IN (
|
|
280
|
+
SELECT id FROM memories
|
|
281
|
+
WHERE superseded_by IS NULL
|
|
282
|
+
ORDER BY (confidence * (1.0 + hit_count * 0.1)) ASC
|
|
283
|
+
LIMIT :limit
|
|
284
|
+
)`).run({ ':now': new Date().toISOString(), ':limit': excess });
|
|
270
285
|
}
|
|
271
286
|
catch {
|
|
272
287
|
// non-fatal
|
|
@@ -15,6 +15,7 @@ import { join } from "node:path";
|
|
|
15
15
|
import { resolveMilestonePath, resolveMilestoneFile, buildMilestoneFileName, } from "./paths.js";
|
|
16
16
|
import { invalidateAllCaches } from "./cache.js";
|
|
17
17
|
import { loadQueueOrder, saveQueueOrder } from "./queue-order.js";
|
|
18
|
+
import { isDbAvailable, updateMilestoneStatus } from "./gsd-db.js";
|
|
18
19
|
// ─── Park ──────────────────────────────────────────────────────────────────
|
|
19
20
|
/**
|
|
20
21
|
* Park a milestone — creates a PARKED.md marker file with reason and timestamp.
|
|
@@ -44,6 +45,15 @@ export function parkMilestone(basePath, milestoneId, reason) {
|
|
|
44
45
|
"",
|
|
45
46
|
].join("\n");
|
|
46
47
|
writeFileSync(parkedPath, content, "utf-8");
|
|
48
|
+
// Sync DB status so deriveStateFromDb also skips this milestone (#2694)
|
|
49
|
+
if (isDbAvailable()) {
|
|
50
|
+
try {
|
|
51
|
+
updateMilestoneStatus(milestoneId, "parked");
|
|
52
|
+
}
|
|
53
|
+
catch (err) {
|
|
54
|
+
process.stderr.write(`gsd: parkMilestone DB sync failed for ${milestoneId}: ${err.message}\n`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
47
57
|
invalidateAllCaches();
|
|
48
58
|
return true;
|
|
49
59
|
}
|
|
@@ -60,6 +70,15 @@ export function unparkMilestone(basePath, milestoneId) {
|
|
|
60
70
|
if (!existsSync(parkedPath))
|
|
61
71
|
return false; // not parked
|
|
62
72
|
unlinkSync(parkedPath);
|
|
73
|
+
// Sync DB status so deriveStateFromDb picks up the unparked milestone (#2694)
|
|
74
|
+
if (isDbAvailable()) {
|
|
75
|
+
try {
|
|
76
|
+
updateMilestoneStatus(milestoneId, "active");
|
|
77
|
+
}
|
|
78
|
+
catch (err) {
|
|
79
|
+
process.stderr.write(`gsd: unparkMilestone DB sync failed for ${milestoneId}: ${err.message}\n`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
63
82
|
invalidateAllCaches();
|
|
64
83
|
return true;
|
|
65
84
|
}
|
|
@@ -98,18 +98,6 @@ export function getNextFallbackModel(currentModelId, modelConfig) {
|
|
|
98
98
|
return modelsToTry[0];
|
|
99
99
|
}
|
|
100
100
|
}
|
|
101
|
-
/**
|
|
102
|
-
* Detect whether an error message indicates a transient network error
|
|
103
|
-
* (worth retrying the same model) vs a permanent provider error
|
|
104
|
-
* (auth failure, quota exceeded, etc. -- should fall back immediately).
|
|
105
|
-
*/
|
|
106
|
-
export function isTransientNetworkError(errorMsg) {
|
|
107
|
-
if (!errorMsg)
|
|
108
|
-
return false;
|
|
109
|
-
const hasNetworkSignal = /network|ECONNRESET|ETIMEDOUT|ECONNREFUSED|socket hang up|fetch failed|connection.*reset|dns/i.test(errorMsg);
|
|
110
|
-
const hasPermanentSignal = /auth|unauthorized|forbidden|invalid.*key|quota|billing/i.test(errorMsg);
|
|
111
|
-
return hasNetworkSignal && !hasPermanentSignal;
|
|
112
|
-
}
|
|
113
101
|
/**
|
|
114
102
|
* Validate a model ID string.
|
|
115
103
|
* Returns true if the ID looks like a valid model identifier.
|
|
@@ -273,7 +261,7 @@ export function resolveContextSelection() {
|
|
|
273
261
|
return profile === "budget" ? "smart" : "full";
|
|
274
262
|
}
|
|
275
263
|
/**
|
|
276
|
-
* Resolve the search provider preference from
|
|
264
|
+
* Resolve the search provider preference from PREFERENCES.md.
|
|
277
265
|
* Returns undefined if not configured (caller falls back to existing behavior).
|
|
278
266
|
*/
|
|
279
267
|
export function resolveSearchProviderFromPreferences() {
|
|
@@ -24,27 +24,27 @@ export { validatePreferences } from "./preferences-validation.js";
|
|
|
24
24
|
// ─── Re-exports: skills ─────────────────────────────────────────────────────
|
|
25
25
|
export { resolveAllSkillReferences, resolveSkillDiscoveryMode, resolveSkillStalenessDays, } from "./preferences-skills.js";
|
|
26
26
|
// ─── Re-exports: models ─────────────────────────────────────────────────────
|
|
27
|
-
export { resolveModelForUnit, resolveModelWithFallbacksForUnit, getNextFallbackModel,
|
|
27
|
+
export { resolveModelForUnit, resolveModelWithFallbacksForUnit, getNextFallbackModel, validateModelId, updatePreferencesModels, resolveDynamicRoutingConfig, resolveAutoSupervisorConfig, resolveProfileDefaults, resolveEffectiveProfile, resolveInlineLevel, resolveContextSelection, resolveSearchProviderFromPreferences, } from "./preferences-models.js";
|
|
28
28
|
// ─── Path Constants & Getters ───────────────────────────────────────────────
|
|
29
29
|
function gsdHome() {
|
|
30
30
|
return process.env.GSD_HOME || join(homedir(), ".gsd");
|
|
31
31
|
}
|
|
32
32
|
function globalPreferencesPath() {
|
|
33
|
-
return join(gsdHome(), "
|
|
33
|
+
return join(gsdHome(), "PREFERENCES.md");
|
|
34
34
|
}
|
|
35
35
|
function legacyGlobalPreferencesPath() {
|
|
36
36
|
return join(homedir(), ".pi", "agent", "gsd-preferences.md");
|
|
37
37
|
}
|
|
38
38
|
function projectPreferencesPath() {
|
|
39
|
-
return join(gsdRoot(process.cwd()), "
|
|
39
|
+
return join(gsdRoot(process.cwd()), "PREFERENCES.md");
|
|
40
40
|
}
|
|
41
|
-
//
|
|
42
|
-
// Check
|
|
43
|
-
function
|
|
44
|
-
return join(gsdHome(), "
|
|
41
|
+
// Legacy: older versions used lowercase preferences.md.
|
|
42
|
+
// Check lowercase as a fallback so those files aren't silently ignored.
|
|
43
|
+
function globalPreferencesPathLegacy() {
|
|
44
|
+
return join(gsdHome(), "preferences.md");
|
|
45
45
|
}
|
|
46
|
-
function
|
|
47
|
-
return join(gsdRoot(process.cwd()), "
|
|
46
|
+
function projectPreferencesPathLegacy() {
|
|
47
|
+
return join(gsdRoot(process.cwd()), "preferences.md");
|
|
48
48
|
}
|
|
49
49
|
export function getGlobalGSDPreferencesPath() {
|
|
50
50
|
return globalPreferencesPath();
|
|
@@ -58,12 +58,12 @@ export function getProjectGSDPreferencesPath() {
|
|
|
58
58
|
// ─── Loading ────────────────────────────────────────────────────────────────
|
|
59
59
|
export function loadGlobalGSDPreferences() {
|
|
60
60
|
return loadPreferencesFile(globalPreferencesPath(), "global")
|
|
61
|
-
?? loadPreferencesFile(
|
|
61
|
+
?? loadPreferencesFile(globalPreferencesPathLegacy(), "global")
|
|
62
62
|
?? loadPreferencesFile(legacyGlobalPreferencesPath(), "global");
|
|
63
63
|
}
|
|
64
64
|
export function loadProjectGSDPreferences() {
|
|
65
65
|
return loadPreferencesFile(projectPreferencesPath(), "project")
|
|
66
|
-
?? loadPreferencesFile(
|
|
66
|
+
?? loadPreferencesFile(projectPreferencesPathLegacy(), "project");
|
|
67
67
|
}
|
|
68
68
|
export function loadEffectiveGSDPreferences() {
|
|
69
69
|
const globalPreferences = loadGlobalGSDPreferences();
|
|
@@ -149,7 +149,7 @@ export function parsePreferencesMarkdown(content) {
|
|
|
149
149
|
}
|
|
150
150
|
if (!_warnedUnrecognizedFormat) {
|
|
151
151
|
_warnedUnrecognizedFormat = true;
|
|
152
|
-
console.warn("[parsePreferencesMarkdown]
|
|
152
|
+
console.warn("[parsePreferencesMarkdown] PREFERENCES.md exists but uses an unrecognized format — skipping.");
|
|
153
153
|
}
|
|
154
154
|
return null;
|
|
155
155
|
}
|
|
@@ -398,7 +398,7 @@ export function resolvePreDispatchHooks() {
|
|
|
398
398
|
* Resolve the effective git isolation mode from preferences.
|
|
399
399
|
* Returns "none" (default), "worktree", or "branch".
|
|
400
400
|
*
|
|
401
|
-
* Default is "none" so GSD works out of the box without
|
|
401
|
+
* Default is "none" so GSD works out of the box without PREFERENCES.md.
|
|
402
402
|
* Worktree isolation requires explicit opt-in because it depends on git
|
|
403
403
|
* branch infrastructure that must be set up before use.
|
|
404
404
|
*/
|
|
@@ -92,7 +92,7 @@ Titles live inside file content (headings, frontmatter), not in file or director
|
|
|
92
92
|
|
|
93
93
|
### Isolation Model
|
|
94
94
|
|
|
95
|
-
Auto-mode supports three isolation modes (configured in `.gsd/
|
|
95
|
+
Auto-mode supports three isolation modes (configured in `.gsd/PREFERENCES.md` under `taskIsolation.mode`):
|
|
96
96
|
|
|
97
97
|
- **worktree** (default): Work happens in `.gsd/worktrees/<MID>/`, a full git worktree on the `milestone/<MID>` branch. Each worktree has its own working copy and `.gsd/` directory. Squash-merged back to the integration branch on milestone completion.
|
|
98
98
|
- **branch**: Work happens in the project root on a `milestone/<MID>` branch. No worktree directory — files are checked out in-place.
|
|
@@ -1,47 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Classify a provider error as transient (auto-resume) or permanent (manual resume).
|
|
3
|
-
*
|
|
4
|
-
* Transient: rate limits, server errors (500/502/503), overloaded, internal errors.
|
|
5
|
-
* These are expected to self-resolve and should auto-resume after a delay.
|
|
6
|
-
*
|
|
7
|
-
* Permanent: auth errors, invalid API key, billing issues.
|
|
8
|
-
* These require user intervention and should pause indefinitely.
|
|
9
|
-
*/
|
|
10
|
-
export function classifyProviderError(errorMsg) {
|
|
11
|
-
const isRateLimit = /rate.?limit|too many requests|429/i.test(errorMsg);
|
|
12
|
-
const isServerError = /internal server error|500|502|503|overloaded|server_error|api_error|service.?unavailable/i.test(errorMsg);
|
|
13
|
-
// Connection/process errors — transient, auto-resume after brief backoff (#2309).
|
|
14
|
-
// These indicate the process was killed, the connection was reset, or a network
|
|
15
|
-
// blip occurred. They are NOT permanent failures.
|
|
16
|
-
const isConnectionError = /terminated|connection.?reset|connection.?refused|other side closed|fetch failed|network.?(?:is\s+)?unavailable|ECONNREFUSED|ECONNRESET|EPIPE/i.test(errorMsg);
|
|
17
|
-
// Permanent errors — never auto-resume
|
|
18
|
-
const isPermanent = /auth|unauthorized|forbidden|invalid.*key|invalid.*api|billing|quota exceeded|account/i.test(errorMsg);
|
|
19
|
-
if (isPermanent && !isRateLimit) {
|
|
20
|
-
return { isTransient: false, isRateLimit: false, suggestedDelayMs: 0 };
|
|
21
|
-
}
|
|
22
|
-
if (isRateLimit) {
|
|
23
|
-
// Try to extract retry-after from the message
|
|
24
|
-
const resetMatch = errorMsg.match(/reset in (\d+)s/i);
|
|
25
|
-
const delayMs = resetMatch ? Number(resetMatch[1]) * 1000 : 60_000; // default 60s for rate limits
|
|
26
|
-
return { isTransient: true, isRateLimit: true, suggestedDelayMs: delayMs };
|
|
27
|
-
}
|
|
28
|
-
if (isServerError) {
|
|
29
|
-
return { isTransient: true, isRateLimit: false, suggestedDelayMs: 30_000 }; // 30s for server errors
|
|
30
|
-
}
|
|
31
|
-
if (isConnectionError) {
|
|
32
|
-
return { isTransient: true, isRateLimit: false, suggestedDelayMs: 15_000 }; // 15s for connection errors
|
|
33
|
-
}
|
|
34
|
-
// Stream-truncation JSON parse errors — transient (#2572).
|
|
35
|
-
// When the API stream is cut mid-chunk, pi tries to reassemble the partial
|
|
36
|
-
// tool-call JSON and gets a SyntaxError. This is the downstream symptom of
|
|
37
|
-
// a connection drop — same root cause as ECONNRESET, one layer up.
|
|
38
|
-
const isMalformedStream = /Unexpected end of JSON|Unexpected token.*JSON|Expected double-quoted property name|SyntaxError.*JSON/i.test(errorMsg);
|
|
39
|
-
if (isMalformedStream) {
|
|
40
|
-
return { isTransient: true, isRateLimit: false, suggestedDelayMs: 15_000 }; // 15s, same as connection errors
|
|
41
|
-
}
|
|
42
|
-
// Unknown error — treat as permanent (user reviews)
|
|
43
|
-
return { isTransient: false, isRateLimit: false, suggestedDelayMs: 0 };
|
|
44
|
-
}
|
|
45
1
|
/**
|
|
46
2
|
* Pause auto-mode due to a provider error.
|
|
47
3
|
*
|
|
@@ -426,7 +426,7 @@ export class RuleRegistry {
|
|
|
426
426
|
formatHookStatus() {
|
|
427
427
|
const entries = this.getHookStatus();
|
|
428
428
|
if (entries.length === 0) {
|
|
429
|
-
return "No hooks configured. Add post_unit_hooks or pre_dispatch_hooks to .gsd/
|
|
429
|
+
return "No hooks configured. Add post_unit_hooks or pre_dispatch_hooks to .gsd/PREFERENCES.md";
|
|
430
430
|
}
|
|
431
431
|
const lines = ["Configured Hooks:", ""];
|
|
432
432
|
const postHooks = entries.filter(e => e.type === "post");
|
|
@@ -13,16 +13,27 @@ import { getGlobalGSDPreferencesPath, loadEffectiveGSDPreferences, loadGlobalGSD
|
|
|
13
13
|
import { ensurePreferencesFile, serializePreferencesToFrontmatter } from "./commands-prefs-wizard.js";
|
|
14
14
|
const SERVICE_TIER_SCOPE_NOTE = "Only affects gpt-5.4 models, regardless of provider.";
|
|
15
15
|
// ─── Gating ──────────────────────────────────────────────────────────────────
|
|
16
|
+
/**
|
|
17
|
+
* Model ID prefixes (bare, without provider) that support OpenAI service tiers.
|
|
18
|
+
*
|
|
19
|
+
* This list is the fallback for callers that only have a model ID string.
|
|
20
|
+
* The authoritative source of truth is `model.capabilities.supportsServiceTier`
|
|
21
|
+
* (set via CAPABILITY_PATCHES in packages/pi-ai/src/models.ts). When callers
|
|
22
|
+
* have access to the full Model object, prefer reading capabilities directly.
|
|
23
|
+
*
|
|
24
|
+
* See: https://github.com/gsd-build/gsd-2/issues/2546
|
|
25
|
+
*/
|
|
26
|
+
const SERVICE_TIER_MODEL_PREFIXES = ["gpt-5.4"];
|
|
16
27
|
/**
|
|
17
28
|
* Returns true when the given model ID supports OpenAI service tiers.
|
|
18
|
-
*
|
|
29
|
+
* Reads from SERVICE_TIER_MODEL_PREFIXES — update that list, not this function.
|
|
19
30
|
*/
|
|
20
31
|
export function supportsServiceTier(modelId) {
|
|
21
32
|
if (!modelId)
|
|
22
33
|
return false;
|
|
23
34
|
// Strip provider prefix if present (e.g. "openai/gpt-5.4" → "gpt-5.4")
|
|
24
35
|
const bare = modelId.includes("/") ? modelId.split("/").pop() : modelId;
|
|
25
|
-
return bare.startsWith(
|
|
36
|
+
return SERVICE_TIER_MODEL_PREFIXES.some((prefix) => bare.startsWith(prefix));
|
|
26
37
|
}
|
|
27
38
|
// ─── Status Formatting ───────────────────────────────────────────────────────
|
|
28
39
|
/**
|
|
@@ -6,6 +6,7 @@ import { parseSummary, loadFile, parseRequirementCounts, parseContextDependsOn,
|
|
|
6
6
|
import { resolveMilestoneFile, resolveSlicePath, resolveSliceFile, resolveTaskFile, resolveTasksDir, resolveGsdRootFile, gsdRoot, } from './paths.js';
|
|
7
7
|
import { findMilestoneIds } from './milestone-ids.js';
|
|
8
8
|
import { loadQueueOrder, sortByQueueOrder } from './queue-order.js';
|
|
9
|
+
import { isClosedStatus } from './status-guards.js';
|
|
9
10
|
import { nativeBatchParseGsdFiles } from './native-parser-bridge.js';
|
|
10
11
|
import { join, resolve } from 'path';
|
|
11
12
|
import { existsSync, readdirSync } from 'node:fs';
|
|
@@ -144,7 +145,23 @@ export async function deriveState(basePath) {
|
|
|
144
145
|
let result;
|
|
145
146
|
// Dual-path: try DB-backed derivation first when hierarchy tables are populated
|
|
146
147
|
if (isDbAvailable()) {
|
|
147
|
-
|
|
148
|
+
let dbMilestones = getAllMilestones();
|
|
149
|
+
// Disk→DB reconciliation (#2631): when the milestones table is empty
|
|
150
|
+
// (e.g. failed initial migration per #2529), the reconciliation code
|
|
151
|
+
// inside deriveStateFromDb is unreachable. Populate from disk here so
|
|
152
|
+
// the DB path activates correctly.
|
|
153
|
+
if (dbMilestones.length === 0) {
|
|
154
|
+
const diskIds = findMilestoneIds(basePath);
|
|
155
|
+
let synced = false;
|
|
156
|
+
for (const diskId of diskIds) {
|
|
157
|
+
if (!isGhostMilestone(basePath, diskId)) {
|
|
158
|
+
insertMilestone({ id: diskId, status: 'active' });
|
|
159
|
+
synced = true;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
if (synced)
|
|
163
|
+
dbMilestones = getAllMilestones();
|
|
164
|
+
}
|
|
148
165
|
if (dbMilestones.length > 0) {
|
|
149
166
|
const stopDbTimer = debugTime("derive-state-db");
|
|
150
167
|
result = await deriveStateFromDb(basePath);
|
|
@@ -187,12 +204,6 @@ function extractContextTitle(content, fallback) {
|
|
|
187
204
|
return stripMilestonePrefix(h1.slice(2).trim()) || fallback;
|
|
188
205
|
}
|
|
189
206
|
// ─── DB-backed State Derivation ────────────────────────────────────────────
|
|
190
|
-
/**
|
|
191
|
-
* Helper: check if a DB status counts as "done" (handles K002 ambiguity).
|
|
192
|
-
*/
|
|
193
|
-
function isStatusDone(status) {
|
|
194
|
-
return status === 'complete' || status === 'done';
|
|
195
|
-
}
|
|
196
207
|
/**
|
|
197
208
|
* Derive GSD state from the milestones/slices/tasks DB tables.
|
|
198
209
|
* Flag files (PARKED, VALIDATION, CONTINUE, REPLAN, REPLAN-TRIGGER, CONTEXT-DRAFT)
|
|
@@ -276,7 +287,7 @@ export async function deriveStateFromDb(basePath) {
|
|
|
276
287
|
parkedMilestoneIds.add(m.id);
|
|
277
288
|
continue;
|
|
278
289
|
}
|
|
279
|
-
if (
|
|
290
|
+
if (isClosedStatus(m.status)) {
|
|
280
291
|
completeMilestoneIds.add(m.id);
|
|
281
292
|
continue;
|
|
282
293
|
}
|
|
@@ -288,7 +299,7 @@ export async function deriveStateFromDb(basePath) {
|
|
|
288
299
|
}
|
|
289
300
|
// Check roadmap: all slices done means milestone is complete
|
|
290
301
|
const slices = getMilestoneSlices(m.id);
|
|
291
|
-
if (slices.length > 0 && slices.every(s =>
|
|
302
|
+
if (slices.length > 0 && slices.every(s => isClosedStatus(s.status))) {
|
|
292
303
|
// All slices done but no summary — still counts as complete for dep resolution
|
|
293
304
|
// if a summary file exists
|
|
294
305
|
// Note: without summary file, the milestone is in validating/completing state, not complete
|
|
@@ -307,7 +318,7 @@ export async function deriveStateFromDb(basePath) {
|
|
|
307
318
|
}
|
|
308
319
|
// Ghost milestone check: no slices in DB AND no substantive files on disk
|
|
309
320
|
const slices = getMilestoneSlices(m.id);
|
|
310
|
-
if (slices.length === 0 && !
|
|
321
|
+
if (slices.length === 0 && !isClosedStatus(m.status)) {
|
|
311
322
|
// Check disk for ghost detection
|
|
312
323
|
if (isGhostMilestone(basePath, m.id))
|
|
313
324
|
continue;
|
|
@@ -328,7 +339,7 @@ export async function deriveStateFromDb(basePath) {
|
|
|
328
339
|
continue;
|
|
329
340
|
}
|
|
330
341
|
// Not complete — determine if it should be active
|
|
331
|
-
const allSlicesDone = slices.length > 0 && slices.every(s =>
|
|
342
|
+
const allSlicesDone = slices.length > 0 && slices.every(s => isClosedStatus(s.status));
|
|
332
343
|
// Get title — prefer DB, fall back to context file extraction
|
|
333
344
|
let title = stripMilestonePrefix(m.title) || m.id;
|
|
334
345
|
if (title === m.id) {
|
|
@@ -465,7 +476,10 @@ export async function deriveStateFromDb(basePath) {
|
|
|
465
476
|
};
|
|
466
477
|
}
|
|
467
478
|
// ── All slices done → validating/completing ─────────────────────────
|
|
468
|
-
|
|
479
|
+
// Guard: [].every() === true (vacuous truth). Without the length check,
|
|
480
|
+
// an empty slice array causes a premature phase transition to
|
|
481
|
+
// validating-milestone. See: https://github.com/gsd-build/gsd-2/issues/2667
|
|
482
|
+
const allSlicesDone = activeMilestoneSlices.length > 0 && activeMilestoneSlices.every(s => isClosedStatus(s.status));
|
|
469
483
|
if (allSlicesDone) {
|
|
470
484
|
const validationFile = resolveMilestoneFile(basePath, activeMilestone.id, "VALIDATION");
|
|
471
485
|
const validationContent = validationFile ? await loadFile(validationFile) : null;
|
|
@@ -495,14 +509,14 @@ export async function deriveStateFromDb(basePath) {
|
|
|
495
509
|
}
|
|
496
510
|
// ── Find active slice (first incomplete with deps satisfied) ─────────
|
|
497
511
|
const sliceProgress = {
|
|
498
|
-
done: activeMilestoneSlices.filter(s =>
|
|
512
|
+
done: activeMilestoneSlices.filter(s => isClosedStatus(s.status)).length,
|
|
499
513
|
total: activeMilestoneSlices.length,
|
|
500
514
|
};
|
|
501
|
-
const doneSliceIds = new Set(activeMilestoneSlices.filter(s =>
|
|
515
|
+
const doneSliceIds = new Set(activeMilestoneSlices.filter(s => isClosedStatus(s.status)).map(s => s.id));
|
|
502
516
|
let activeSlice = null;
|
|
503
517
|
let activeSliceRow = null;
|
|
504
518
|
for (const s of activeMilestoneSlices) {
|
|
505
|
-
if (
|
|
519
|
+
if (isClosedStatus(s.status))
|
|
506
520
|
continue;
|
|
507
521
|
if (s.depends.every(dep => doneSliceIds.has(dep))) {
|
|
508
522
|
activeSlice = { id: s.id, title: s.title };
|
|
@@ -542,7 +556,7 @@ export async function deriveStateFromDb(basePath) {
|
|
|
542
556
|
// causing the dispatcher to re-dispatch the same completed task forever.
|
|
543
557
|
let reconciled = false;
|
|
544
558
|
for (const t of tasks) {
|
|
545
|
-
if (
|
|
559
|
+
if (isClosedStatus(t.status))
|
|
546
560
|
continue;
|
|
547
561
|
const summaryPath = resolveTaskFile(basePath, activeMilestone.id, activeSlice.id, t.id, "SUMMARY");
|
|
548
562
|
if (summaryPath && existsSync(summaryPath)) {
|
|
@@ -562,10 +576,10 @@ export async function deriveStateFromDb(basePath) {
|
|
|
562
576
|
tasks = getSliceTasks(activeMilestone.id, activeSlice.id);
|
|
563
577
|
}
|
|
564
578
|
const taskProgress = {
|
|
565
|
-
done: tasks.filter(t =>
|
|
579
|
+
done: tasks.filter(t => isClosedStatus(t.status)).length,
|
|
566
580
|
total: tasks.length,
|
|
567
581
|
};
|
|
568
|
-
const activeTaskRow = tasks.find(t => !
|
|
582
|
+
const activeTaskRow = tasks.find(t => !isClosedStatus(t.status));
|
|
569
583
|
if (!activeTaskRow && tasks.length > 0) {
|
|
570
584
|
// All tasks done but slice not marked complete → summarizing
|
|
571
585
|
return {
|
|
@@ -620,7 +634,7 @@ export async function deriveStateFromDb(basePath) {
|
|
|
620
634
|
};
|
|
621
635
|
}
|
|
622
636
|
// ── Blocker detection: check completed tasks for blocker_discovered ──
|
|
623
|
-
const completedTasks = tasks.filter(t =>
|
|
637
|
+
const completedTasks = tasks.filter(t => isClosedStatus(t.status));
|
|
624
638
|
let blockerTaskId = null;
|
|
625
639
|
for (const ct of completedTasks) {
|
|
626
640
|
if (ct.blocker_discovered) {
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Status predicates for GSD state-machine guards.
|
|
3
|
+
*
|
|
4
|
+
* The DB stores status as free-form strings. Two values indicate
|
|
5
|
+
* "closed": "complete" (canonical) and "done" (legacy / alias).
|
|
6
|
+
* Every inline `status === "complete" || status === "done"` should
|
|
7
|
+
* use isClosedStatus() instead.
|
|
8
|
+
*/
|
|
9
|
+
/** Returns true when a milestone, slice, or task status indicates closure. */
|
|
10
|
+
export function isClosedStatus(status) {
|
|
11
|
+
return status === "complete" || status === "done";
|
|
12
|
+
}
|
|
@@ -7,8 +7,9 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { join } from "node:path";
|
|
9
9
|
import { mkdirSync } from "node:fs";
|
|
10
|
-
import { transaction, getMilestone, getMilestoneSlices, getSliceTasks,
|
|
10
|
+
import { transaction, getMilestone, getMilestoneSlices, getSliceTasks, updateMilestoneStatus, } from "../gsd-db.js";
|
|
11
11
|
import { resolveMilestonePath, clearPathCache } from "../paths.js";
|
|
12
|
+
import { isClosedStatus } from "../status-guards.js";
|
|
12
13
|
import { saveFile, clearParseCache } from "../files.js";
|
|
13
14
|
import { invalidateStateCache } from "../state.js";
|
|
14
15
|
import { renderAllProjections } from "../workflow-projections.js";
|
|
@@ -89,7 +90,7 @@ export async function handleCompleteMilestone(params, basePath) {
|
|
|
89
90
|
guardError = `milestone not found: ${params.milestoneId}`;
|
|
90
91
|
return;
|
|
91
92
|
}
|
|
92
|
-
if (milestone.status
|
|
93
|
+
if (isClosedStatus(milestone.status)) {
|
|
93
94
|
guardError = `milestone ${params.milestoneId} is already complete`;
|
|
94
95
|
return;
|
|
95
96
|
}
|
|
@@ -99,7 +100,7 @@ export async function handleCompleteMilestone(params, basePath) {
|
|
|
99
100
|
guardError = `no slices found for milestone ${params.milestoneId}`;
|
|
100
101
|
return;
|
|
101
102
|
}
|
|
102
|
-
const incompleteSlices = slices.filter(s => s.status
|
|
103
|
+
const incompleteSlices = slices.filter(s => !isClosedStatus(s.status));
|
|
103
104
|
if (incompleteSlices.length > 0) {
|
|
104
105
|
const incompleteIds = incompleteSlices.map(s => `${s.id} (status: ${s.status})`).join(", ");
|
|
105
106
|
guardError = `incomplete slices: ${incompleteIds}`;
|
|
@@ -108,7 +109,7 @@ export async function handleCompleteMilestone(params, basePath) {
|
|
|
108
109
|
// Deep check: verify all tasks in all slices are complete
|
|
109
110
|
for (const slice of slices) {
|
|
110
111
|
const tasks = getSliceTasks(params.milestoneId, slice.id);
|
|
111
|
-
const incompleteTasks = tasks.filter(t => t.status
|
|
112
|
+
const incompleteTasks = tasks.filter(t => !isClosedStatus(t.status));
|
|
112
113
|
if (incompleteTasks.length > 0) {
|
|
113
114
|
const ids = incompleteTasks.map(t => `${t.id} (status: ${t.status})`).join(", ");
|
|
114
115
|
guardError = `slice ${slice.id} has incomplete tasks: ${ids}`;
|
|
@@ -116,11 +117,7 @@ export async function handleCompleteMilestone(params, basePath) {
|
|
|
116
117
|
}
|
|
117
118
|
}
|
|
118
119
|
// All guards passed — perform write
|
|
119
|
-
|
|
120
|
-
adapter.prepare(`UPDATE milestones SET status = 'complete', completed_at = :completed_at WHERE id = :mid`).run({
|
|
121
|
-
":completed_at": completedAt,
|
|
122
|
-
":mid": params.milestoneId,
|
|
123
|
-
});
|
|
120
|
+
updateMilestoneStatus(params.milestoneId, 'complete', completedAt);
|
|
124
121
|
});
|
|
125
122
|
if (guardError) {
|
|
126
123
|
return { error: guardError };
|
|
@@ -144,10 +141,7 @@ export async function handleCompleteMilestone(params, basePath) {
|
|
|
144
141
|
catch (renderErr) {
|
|
145
142
|
// Disk render failed — roll back DB status so state stays consistent
|
|
146
143
|
process.stderr.write(`gsd-db: complete_milestone — disk render failed, rolling back DB status: ${renderErr.message}\n`);
|
|
147
|
-
|
|
148
|
-
if (rollbackAdapter) {
|
|
149
|
-
rollbackAdapter.prepare(`UPDATE milestones SET status = 'active', completed_at = NULL WHERE id = :mid`).run({ ":mid": params.milestoneId });
|
|
150
|
-
}
|
|
144
|
+
updateMilestoneStatus(params.milestoneId, 'active', null);
|
|
151
145
|
invalidateStateCache();
|
|
152
146
|
return { error: `disk render failed: ${renderErr.message}` };
|
|
153
147
|
}
|
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { join } from "node:path";
|
|
10
10
|
import { mkdirSync } from "node:fs";
|
|
11
|
-
import {
|
|
11
|
+
import { isClosedStatus } from "../status-guards.js";
|
|
12
|
+
import { transaction, insertMilestone, insertSlice, getSlice, getSliceTasks, getMilestone, updateSliceStatus, setSliceSummaryMd, } from "../gsd-db.js";
|
|
12
13
|
import { resolveSlicePath, clearPathCache } from "../paths.js";
|
|
13
14
|
import { checkOwnership, sliceUnitKey } from "../unit-ownership.js";
|
|
14
15
|
import { saveFile, clearParseCache } from "../files.js";
|
|
@@ -179,12 +180,12 @@ export async function handleCompleteSlice(params, basePath) {
|
|
|
179
180
|
// Milestone/slice not existing is OK — insertMilestone/insertSlice below will auto-create.
|
|
180
181
|
// Only block if they exist and are closed.
|
|
181
182
|
const milestone = getMilestone(params.milestoneId);
|
|
182
|
-
if (milestone && (milestone.status
|
|
183
|
+
if (milestone && isClosedStatus(milestone.status)) {
|
|
183
184
|
guardError = `cannot complete slice in a closed milestone: ${params.milestoneId} (status: ${milestone.status})`;
|
|
184
185
|
return;
|
|
185
186
|
}
|
|
186
187
|
const slice = getSlice(params.milestoneId, params.sliceId);
|
|
187
|
-
if (slice && (slice.status
|
|
188
|
+
if (slice && isClosedStatus(slice.status)) {
|
|
188
189
|
guardError = `slice ${params.sliceId} is already complete — use gsd_slice_reopen first if you need to redo it`;
|
|
189
190
|
return;
|
|
190
191
|
}
|
|
@@ -194,7 +195,7 @@ export async function handleCompleteSlice(params, basePath) {
|
|
|
194
195
|
guardError = `no tasks found for slice ${params.sliceId} in milestone ${params.milestoneId}`;
|
|
195
196
|
return;
|
|
196
197
|
}
|
|
197
|
-
const incompleteTasks = tasks.filter(t => t.status
|
|
198
|
+
const incompleteTasks = tasks.filter(t => !isClosedStatus(t.status));
|
|
198
199
|
if (incompleteTasks.length > 0) {
|
|
199
200
|
const incompleteIds = incompleteTasks.map(t => `${t.id} (status: ${t.status})`).join(", ");
|
|
200
201
|
guardError = `incomplete tasks: ${incompleteIds}`;
|
|
@@ -240,26 +241,12 @@ export async function handleCompleteSlice(params, basePath) {
|
|
|
240
241
|
catch (renderErr) {
|
|
241
242
|
// Disk render failed — roll back DB status so state stays consistent
|
|
242
243
|
process.stderr.write(`gsd-db: complete_slice — disk render failed, rolling back DB status: ${renderErr.message}\n`);
|
|
243
|
-
|
|
244
|
-
if (rollbackAdapter) {
|
|
245
|
-
rollbackAdapter.prepare(`UPDATE slices SET status = 'pending' WHERE milestone_id = :mid AND id = :sid`).run({
|
|
246
|
-
":mid": params.milestoneId,
|
|
247
|
-
":sid": params.sliceId,
|
|
248
|
-
});
|
|
249
|
-
}
|
|
244
|
+
updateSliceStatus(params.milestoneId, params.sliceId, 'pending');
|
|
250
245
|
invalidateStateCache();
|
|
251
246
|
return { error: `disk render failed: ${renderErr.message}` };
|
|
252
247
|
}
|
|
253
248
|
// Store rendered markdown in DB for D004 recovery
|
|
254
|
-
|
|
255
|
-
if (adapter) {
|
|
256
|
-
adapter.prepare(`UPDATE slices SET full_summary_md = :summary_md, full_uat_md = :uat_md WHERE milestone_id = :mid AND id = :sid`).run({
|
|
257
|
-
":summary_md": summaryMd,
|
|
258
|
-
":uat_md": uatMd,
|
|
259
|
-
":mid": params.milestoneId,
|
|
260
|
-
":sid": params.sliceId,
|
|
261
|
-
});
|
|
262
|
-
}
|
|
249
|
+
setSliceSummaryMd(params.milestoneId, params.sliceId, summaryMd, uatMd);
|
|
263
250
|
// Invalidate all caches
|
|
264
251
|
invalidateStateCache();
|
|
265
252
|
clearPathCache();
|