gsd-pi 2.80.0-dev.b62439b9f → 2.80.0-dev.c5c38454b
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/cli-web-branch.d.ts → cli-web-branch.d.ts} +1 -1
- package/dist/{cli/cli-web-branch.js → cli-web-branch.js} +3 -3
- package/dist/{cli/cli.js → cli.js} +25 -25
- package/dist/{extension-runtime/extension-registry.js → extension-registry.js} +1 -1
- package/dist/{headless/headless-query.d.ts → headless-query.d.ts} +1 -1
- package/dist/{headless/headless-query.js → headless-query.js} +2 -3
- package/dist/{headless/headless-recover.js → headless-recover.js} +2 -3
- package/dist/{headless/headless.js → headless.js} +3 -3
- package/dist/loader.js +10 -10
- package/dist/{providers/models-resolver.js → models-resolver.js} +1 -1
- package/dist/{onboarding/onboarding.js → onboarding.js} +8 -8
- package/dist/{providers/remote-questions-config.js → remote-questions-config.js} +1 -1
- package/dist/{resource-runtime/resource-loader.d.ts → resource-loader.d.ts} +1 -1
- package/dist/{resource-runtime/resource-loader.js → resource-loader.js} +6 -6
- package/dist/resources/.managed-resources-content-hash +1 -1
- package/dist/resources/extensions/github-sync/templates.js +39 -8
- package/dist/resources/extensions/gsd/auto/loop.js +13 -9
- package/dist/resources/extensions/gsd/auto/run-unit.js +19 -15
- package/dist/resources/extensions/gsd/auto-dashboard.js +51 -15
- package/dist/resources/extensions/gsd/auto-dispatch.js +10 -0
- package/dist/resources/extensions/gsd/auto-recovery.js +154 -8
- package/dist/resources/extensions/gsd/auto-start.js +2 -3
- package/dist/resources/extensions/gsd/auto-worktree.js +6 -4
- package/dist/resources/extensions/gsd/auto.js +3 -3
- package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +15 -1
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +11 -1
- package/dist/resources/extensions/gsd/bootstrap/write-gate.js +129 -1
- package/dist/resources/extensions/gsd/commands-extract-learnings.js +17 -12
- package/dist/resources/extensions/gsd/db-base-schema.js +14 -0
- package/dist/resources/extensions/gsd/db-migration-steps.js +16 -0
- package/dist/resources/extensions/gsd/gsd-db.js +102 -2
- package/dist/resources/extensions/gsd/guided-flow.js +2 -2
- package/dist/resources/extensions/gsd/pr-evidence.js +57 -16
- package/dist/resources/extensions/gsd/working-output-messages.js +64 -0
- package/dist/{security/security-overrides.js → security-overrides.js} +1 -1
- package/dist/{startup/startup-model-validation.js → startup-model-validation.js} +1 -1
- package/dist/tsconfig.extensions.tsbuildinfo +1 -1
- package/dist/{update/update-check.js → update-check.js} +1 -1
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +11 -11
- 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/react-loadable-manifest.json +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.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- 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 +1 -1
- 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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/preferences/route.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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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/resize/route.js +2 -2
- 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.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +3 -3
- package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js.nft.json +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.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/update/route.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.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
- 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 +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +11 -11
- package/dist/web/standalone/.next/server/chunks/6897.js +12 -0
- 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/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/{8336.6f6f30e410419aff.js → 8336.631939fb583761fa.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/{webpack-d82dbee6356c1733.js → webpack-0481f1221120a7c6.js} +1 -1
- package/dist/{cli/web-mode.js → web-mode.js} +3 -3
- package/dist/{onboarding/welcome-screen.js → welcome-screen.js} +1 -1
- package/dist/{worktrees/worktree-cli.js → worktree-cli.js} +2 -3
- package/package.json +7 -3
- package/packages/pi-ai/dist/models/fake-model.d.ts +12 -0
- package/packages/pi-ai/dist/models/fake-model.d.ts.map +1 -0
- package/packages/pi-ai/dist/models/fake-model.js +27 -0
- package/packages/pi-ai/dist/models/fake-model.js.map +1 -0
- package/packages/pi-ai/dist/models/index.d.ts.map +1 -1
- package/packages/pi-ai/dist/models/index.js +8 -0
- package/packages/pi-ai/dist/models/index.js.map +1 -1
- package/packages/pi-ai/dist/providers/fake.d.ts +42 -0
- package/packages/pi-ai/dist/providers/fake.d.ts.map +1 -0
- package/packages/pi-ai/dist/providers/fake.js +319 -0
- package/packages/pi-ai/dist/providers/fake.js.map +1 -0
- package/packages/pi-ai/dist/providers/register-builtins.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/register-builtins.js +24 -0
- package/packages/pi-ai/dist/providers/register-builtins.js.map +1 -1
- package/packages/pi-ai/src/models/fake-model.ts +30 -0
- package/packages/pi-ai/src/models/index.ts +9 -0
- package/packages/pi-ai/src/providers/fake.ts +376 -0
- package/packages/pi-ai/src/providers/register-builtins.ts +23 -0
- package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js +44 -0
- package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.js +14 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.test.js +97 -0
- package/packages/pi-coding-agent/dist/core/extensions/runner.test.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 +5 -0
- package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +4 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.js +8 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/slash-commands.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/slash-commands.js +1 -0
- package/packages/pi-coding-agent/dist/core/slash-commands.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/chat-frame-compaction-tone.test.js +6 -4
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/chat-frame-compaction-tone.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js +54 -15
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.d.ts +26 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.js +112 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.test.js +51 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.test.js.map +1 -0
- 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.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.js +10 -9
- package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts +3 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js +11 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.js +7 -6
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts +16 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +106 -17
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +58 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.test.js +40 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +3 -0
- 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 +23 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js +20 -0
- package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.test.js +79 -0
- package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-schema.d.ts +12 -0
- package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-schema.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-schema.js +13 -0
- package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-schema.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.d.ts +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js +18 -1
- package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js +36 -27
- package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.d.ts +11 -0
- package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.js +18 -0
- package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.test.js +48 -0
- package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.test.js.map +1 -0
- package/packages/pi-coding-agent/src/core/chat-controller-ordering.test.ts +51 -0
- package/packages/pi-coding-agent/src/core/extensions/runner.test.ts +108 -0
- package/packages/pi-coding-agent/src/core/extensions/runner.ts +16 -1
- package/packages/pi-coding-agent/src/core/model-registry.ts +4 -0
- package/packages/pi-coding-agent/src/core/settings-manager.ts +12 -0
- package/packages/pi-coding-agent/src/core/slash-commands.ts +1 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/chat-frame-compaction-tone.test.ts +7 -5
- package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/tool-execution.test.ts +78 -15
- package/packages/pi-coding-agent/src/modes/interactive/components/adaptive-layout.test.ts +59 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/adaptive-layout.ts +160 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/assistant-message.ts +1 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/chat-frame.ts +10 -9
- package/packages/pi-coding-agent/src/modes/interactive/components/settings-selector.ts +15 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.ts +10 -9
- package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +118 -17
- package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.test.ts +43 -1
- package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +73 -1
- package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.test.ts +1 -0
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode-state.ts +1 -1
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +25 -0
- package/packages/pi-coding-agent/src/modes/interactive/slash-command-handlers.test.ts +95 -0
- package/packages/pi-coding-agent/src/modes/interactive/slash-command-handlers.ts +24 -1
- package/packages/pi-coding-agent/src/modes/interactive/theme/theme-schema.ts +13 -0
- package/packages/pi-coding-agent/src/modes/interactive/theme/theme.ts +32 -2
- package/packages/pi-coding-agent/src/modes/interactive/theme/themes.ts +36 -27
- package/packages/pi-coding-agent/src/modes/interactive/tui-mode.test.ts +65 -0
- package/packages/pi-coding-agent/src/modes/interactive/tui-mode.ts +29 -0
- package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-tui/dist/__tests__/style.test.d.ts +2 -0
- package/packages/pi-tui/dist/__tests__/style.test.d.ts.map +1 -0
- package/packages/pi-tui/dist/__tests__/style.test.js +63 -0
- package/packages/pi-tui/dist/__tests__/style.test.js.map +1 -0
- package/packages/pi-tui/dist/__tests__/tui.test.js +1 -2
- package/packages/pi-tui/dist/__tests__/tui.test.js.map +1 -1
- package/packages/pi-tui/dist/index.d.ts +1 -0
- package/packages/pi-tui/dist/index.d.ts.map +1 -1
- package/packages/pi-tui/dist/index.js +2 -0
- package/packages/pi-tui/dist/index.js.map +1 -1
- package/packages/pi-tui/dist/style.d.ts +41 -0
- package/packages/pi-tui/dist/style.d.ts.map +1 -0
- package/packages/pi-tui/dist/style.js +158 -0
- package/packages/pi-tui/dist/style.js.map +1 -0
- package/packages/pi-tui/dist/tui.d.ts +1 -3
- package/packages/pi-tui/dist/tui.d.ts.map +1 -1
- package/packages/pi-tui/dist/tui.js +2 -3
- package/packages/pi-tui/dist/tui.js.map +1 -1
- package/packages/pi-tui/src/__tests__/style.test.ts +76 -0
- package/packages/pi-tui/src/__tests__/tui.test.ts +1 -2
- package/packages/pi-tui/src/index.ts +9 -0
- package/packages/pi-tui/src/style.ts +225 -0
- package/packages/pi-tui/src/tui.ts +2 -3
- package/packages/pi-tui/tsconfig.tsbuildinfo +1 -1
- package/pkg/dist/modes/interactive/theme/theme-schema.d.ts +12 -0
- package/pkg/dist/modes/interactive/theme/theme-schema.d.ts.map +1 -1
- package/pkg/dist/modes/interactive/theme/theme-schema.js +13 -0
- package/pkg/dist/modes/interactive/theme/theme-schema.js.map +1 -1
- package/pkg/dist/modes/interactive/theme/theme.d.ts +1 -1
- package/pkg/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/pkg/dist/modes/interactive/theme/theme.js +18 -1
- package/pkg/dist/modes/interactive/theme/theme.js.map +1 -1
- package/pkg/dist/modes/interactive/theme/themes.d.ts.map +1 -1
- package/pkg/dist/modes/interactive/theme/themes.js +36 -27
- package/pkg/dist/modes/interactive/theme/themes.js.map +1 -1
- package/src/resources/extensions/github-sync/templates.ts +38 -8
- package/src/resources/extensions/github-sync/tests/inline-code.test.ts +66 -0
- package/src/resources/extensions/gsd/auto/loop.ts +14 -10
- package/src/resources/extensions/gsd/auto/run-unit.ts +24 -14
- package/src/resources/extensions/gsd/auto-dashboard.ts +57 -8
- package/src/resources/extensions/gsd/auto-dispatch.ts +17 -0
- package/src/resources/extensions/gsd/auto-recovery.ts +153 -7
- package/src/resources/extensions/gsd/auto-start.ts +7 -6
- package/src/resources/extensions/gsd/auto-worktree.ts +6 -4
- package/src/resources/extensions/gsd/auto.ts +3 -3
- package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +16 -1
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +17 -1
- package/src/resources/extensions/gsd/bootstrap/write-gate.ts +135 -1
- package/src/resources/extensions/gsd/commands-extract-learnings.ts +17 -12
- package/src/resources/extensions/gsd/db-base-schema.ts +15 -0
- package/src/resources/extensions/gsd/db-migration-steps.ts +17 -0
- package/src/resources/extensions/gsd/gsd-db.ts +119 -1
- package/src/resources/extensions/gsd/guided-flow.ts +2 -2
- package/src/resources/extensions/gsd/pr-evidence.ts +63 -5
- package/src/resources/extensions/gsd/tests/auto-abort-pause-regression.test.ts +7 -1
- package/src/resources/extensions/gsd/tests/auto-dashboard.test.ts +33 -0
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +83 -4
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +170 -1
- package/src/resources/extensions/gsd/tests/commands-extract-learnings.test.ts +9 -0
- package/src/resources/extensions/gsd/tests/db-migration-steps.integration.test.ts +428 -0
- package/src/resources/extensions/gsd/tests/db-schema-metadata.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/fixtures/pr-body/commands-ship-basic.md +52 -0
- package/src/resources/extensions/gsd/tests/fixtures/pr-body/commands-ship-empty-optionals.md +42 -0
- package/src/resources/extensions/gsd/tests/fixtures/pr-body/swarm-lane-no-blockers.md +55 -0
- package/src/resources/extensions/gsd/tests/fixtures/pr-body/swarm-lane-with-blockers.md +60 -0
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +44 -0
- package/src/resources/extensions/gsd/tests/has-pending-deep-stage.test.ts +33 -1
- package/src/resources/extensions/gsd/tests/headless-answers.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/headless-query.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/pr-evidence-equivalence.test.ts +102 -0
- package/src/resources/extensions/gsd/tests/pr-evidence-hardening.test.ts +165 -0
- package/src/resources/extensions/gsd/tests/resource-loader-import-path.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/uok-plan-v2-wiring.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +7 -8
- package/src/resources/extensions/gsd/tests/working-output-messages.test.ts +93 -0
- package/src/resources/extensions/gsd/tests/worktree-submodule-safety.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/worktree-write-gate.test.ts +179 -0
- package/src/resources/extensions/gsd/working-output-messages.ts +120 -0
- package/dist/web/standalone/.next/server/chunks/7020.js +0 -12
- /package/dist/{app/app-paths.d.ts → app-paths.d.ts} +0 -0
- /package/dist/{app/app-paths.js → app-paths.js} +0 -0
- /package/dist/{extension-runtime/bundled-extension-paths.d.ts → bundled-extension-paths.d.ts} +0 -0
- /package/dist/{extension-runtime/bundled-extension-paths.js → bundled-extension-paths.js} +0 -0
- /package/dist/{extension-runtime/bundled-resource-path.d.ts → bundled-resource-path.d.ts} +0 -0
- /package/dist/{extension-runtime/bundled-resource-path.js → bundled-resource-path.js} +0 -0
- /package/dist/{startup/claude-cli-check.d.ts → claude-cli-check.d.ts} +0 -0
- /package/dist/{startup/claude-cli-check.js → claude-cli-check.js} +0 -0
- /package/dist/{cli/cli-auto-routing.d.ts → cli-auto-routing.d.ts} +0 -0
- /package/dist/{cli/cli-auto-routing.js → cli-auto-routing.js} +0 -0
- /package/dist/{cli/cli-policy.d.ts → cli-policy.d.ts} +0 -0
- /package/dist/{cli/cli-policy.js → cli-policy.js} +0 -0
- /package/dist/{cli/cli.d.ts → cli.d.ts} +0 -0
- /package/dist/{extension-runtime/extension-discovery.d.ts → extension-discovery.d.ts} +0 -0
- /package/dist/{extension-runtime/extension-discovery.js → extension-discovery.js} +0 -0
- /package/dist/{extension-runtime/extension-registry.d.ts → extension-registry.d.ts} +0 -0
- /package/dist/{extension-runtime/extension-sort.d.ts → extension-sort.d.ts} +0 -0
- /package/dist/{extension-runtime/extension-sort.js → extension-sort.js} +0 -0
- /package/dist/{extension-runtime/extension-validator.d.ts → extension-validator.d.ts} +0 -0
- /package/dist/{extension-runtime/extension-validator.js → extension-validator.js} +0 -0
- /package/dist/{headless/headless-answers.d.ts → headless-answers.d.ts} +0 -0
- /package/dist/{headless/headless-answers.js → headless-answers.js} +0 -0
- /package/dist/{headless/headless-context.d.ts → headless-context.d.ts} +0 -0
- /package/dist/{headless/headless-context.js → headless-context.js} +0 -0
- /package/dist/{headless/headless-events.d.ts → headless-events.d.ts} +0 -0
- /package/dist/{headless/headless-events.js → headless-events.js} +0 -0
- /package/dist/{headless/headless-recover.d.ts → headless-recover.d.ts} +0 -0
- /package/dist/{headless/headless-types.d.ts → headless-types.d.ts} +0 -0
- /package/dist/{headless/headless-types.js → headless-types.js} +0 -0
- /package/dist/{headless/headless-ui.d.ts → headless-ui.d.ts} +0 -0
- /package/dist/{headless/headless-ui.js → headless-ui.js} +0 -0
- /package/dist/{headless/headless.d.ts → headless.d.ts} +0 -0
- /package/dist/{cli/help-text.d.ts → help-text.d.ts} +0 -0
- /package/dist/{cli/help-text.js → help-text.js} +0 -0
- /package/dist/{cli/logo.d.ts → logo.d.ts} +0 -0
- /package/dist/{cli/logo.js → logo.js} +0 -0
- /package/dist/{mcp/mcp-server.d.ts → mcp-server.d.ts} +0 -0
- /package/dist/{mcp/mcp-server.js → mcp-server.js} +0 -0
- /package/dist/{providers/models-resolver.d.ts → models-resolver.d.ts} +0 -0
- /package/dist/{onboarding/onboarding.d.ts → onboarding.d.ts} +0 -0
- /package/dist/{providers/pi-migration.d.ts → pi-migration.d.ts} +0 -0
- /package/dist/{providers/pi-migration.js → pi-migration.js} +0 -0
- /package/dist/{app/project-sessions.d.ts → project-sessions.d.ts} +0 -0
- /package/dist/{app/project-sessions.js → project-sessions.js} +0 -0
- /package/dist/{providers/provider-migrations.d.ts → provider-migrations.d.ts} +0 -0
- /package/dist/{providers/provider-migrations.js → provider-migrations.js} +0 -0
- /package/dist/{providers/remote-questions-config.d.ts → remote-questions-config.d.ts} +0 -0
- /package/dist/{rtk/rtk-shared.d.ts → rtk-shared.d.ts} +0 -0
- /package/dist/{rtk/rtk-shared.js → rtk-shared.js} +0 -0
- /package/dist/{rtk/rtk.d.ts → rtk.d.ts} +0 -0
- /package/dist/{rtk/rtk.js → rtk.js} +0 -0
- /package/dist/{startup/runtime-checks.d.ts → runtime-checks.d.ts} +0 -0
- /package/dist/{startup/runtime-checks.js → runtime-checks.js} +0 -0
- /package/dist/{security/security-overrides.d.ts → security-overrides.d.ts} +0 -0
- /package/dist/{startup/startup-model-validation.d.ts → startup-model-validation.d.ts} +0 -0
- /package/dist/{startup/startup-timings.d.ts → startup-timings.d.ts} +0 -0
- /package/dist/{startup/startup-timings.js → startup-timings.js} +0 -0
- /package/dist/{app/tool-bootstrap.d.ts → tool-bootstrap.d.ts} +0 -0
- /package/dist/{app/tool-bootstrap.js → tool-bootstrap.js} +0 -0
- /package/dist/{update/update-check.d.ts → update-check.d.ts} +0 -0
- /package/dist/{update/update-cmd.d.ts → update-cmd.d.ts} +0 -0
- /package/dist/{update/update-cmd.js → update-cmd.js} +0 -0
- /package/dist/web/standalone/.next/static/{-WDXZiiJHGZDbYH75Mu6f → TCSim36ZpcPu2WgeoC45g}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{-WDXZiiJHGZDbYH75Mu6f → TCSim36ZpcPu2WgeoC45g}/_ssgManifest.js +0 -0
- /package/dist/{cli/web-mode.d.ts → web-mode.d.ts} +0 -0
- /package/dist/{onboarding/welcome-screen.d.ts → welcome-screen.d.ts} +0 -0
- /package/dist/{onboarding/wizard.d.ts → wizard.d.ts} +0 -0
- /package/dist/{onboarding/wizard.js → wizard.js} +0 -0
- /package/dist/{worktrees/worktree-cli.d.ts → worktree-cli.d.ts} +0 -0
- /package/dist/{worktrees/worktree-name-gen.d.ts → worktree-name-gen.d.ts} +0 -0
- /package/dist/{worktrees/worktree-name-gen.js → worktree-name-gen.js} +0 -0
- /package/dist/{worktrees/worktree-status-banner.d.ts → worktree-status-banner.d.ts} +0 -0
- /package/dist/{worktrees/worktree-status-banner.js → worktree-status-banner.js} +0 -0
|
@@ -7,7 +7,7 @@ import type { GSDEcosystemBeforeAgentStartHandler } from "../ecosystem/gsd-exten
|
|
|
7
7
|
import { updateSnapshot } from "../ecosystem/gsd-extension-api.js";
|
|
8
8
|
|
|
9
9
|
import { buildMilestoneFileName, resolveMilestonePath, resolveSliceFile, resolveSlicePath } from "../paths.js";
|
|
10
|
-
import { canonicalToolName, clearDiscussionFlowState, isDepthConfirmationAnswer, isQueuePhaseActive, markApprovalGateVerified, markDepthVerified, resetWriteGateState, shouldBlockContextWrite, shouldBlockPlanningUnit, shouldBlockQueueExecution, isGateQuestionId, setPendingGate, clearPendingGate, getPendingGate, shouldBlockPendingGate, shouldBlockPendingGateBash, extractDepthVerificationMilestoneId } from "./write-gate.js";
|
|
10
|
+
import { canonicalToolName, clearDiscussionFlowState, isDepthConfirmationAnswer, isQueuePhaseActive, markApprovalGateVerified, markDepthVerified, resetWriteGateState, shouldBlockContextWrite, shouldBlockPlanningUnit, shouldBlockQueueExecution, shouldBlockWorktreeWrite, isGateQuestionId, setPendingGate, clearPendingGate, getPendingGate, shouldBlockPendingGate, shouldBlockPendingGateBash, extractDepthVerificationMilestoneId } from "./write-gate.js";
|
|
11
11
|
import { resolveManifest } from "../unit-context-manifest.js";
|
|
12
12
|
import { isBlockedStateFile, isBashWriteToStateFile, BLOCKED_WRITE_ERROR } from "../write-intercept.js";
|
|
13
13
|
import { loadFile, saveFile, formatContinue } from "../files.js";
|
|
@@ -482,6 +482,22 @@ export function registerHooks(
|
|
|
482
482
|
}
|
|
483
483
|
}
|
|
484
484
|
|
|
485
|
+
// ── Worktree-isolation write gate (#5199) ────────────────────────────
|
|
486
|
+
// Block planning-write tools from landing code at the project root when
|
|
487
|
+
// git.isolation=worktree but auto-mode hasn't created the milestone
|
|
488
|
+
// worktree yet. Without this, writes silently orphan outside git history.
|
|
489
|
+
if (isToolCallEventType("write", event) || isToolCallEventType("edit", event)) {
|
|
490
|
+
const wtBasePath = resolveWorktreeProjectRoot(dash.basePath ?? discussionBasePath);
|
|
491
|
+
const wtGuard = shouldBlockWorktreeWrite(
|
|
492
|
+
event.toolName,
|
|
493
|
+
event.input.path,
|
|
494
|
+
wtBasePath,
|
|
495
|
+
isAutoActive(),
|
|
496
|
+
dash.currentUnit?.type,
|
|
497
|
+
);
|
|
498
|
+
if (wtGuard.block) return wtGuard;
|
|
499
|
+
}
|
|
500
|
+
|
|
485
501
|
// ── Single-writer engine: block direct writes to STATE.md ──────────
|
|
486
502
|
// Covers write, edit, and bash tools to prevent bypass vectors.
|
|
487
503
|
if (isToolCallEventType("write", event)) {
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import { copyFileSync, existsSync, mkdirSync, readFileSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
|
|
1
|
+
import { copyFileSync, existsSync, mkdirSync, readFileSync, realpathSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
3
3
|
|
|
4
4
|
import { minimatch } from "minimatch";
|
|
5
5
|
|
|
6
|
+
import { getIsolationMode } from "../preferences.js";
|
|
6
7
|
import type { ToolsPolicy } from "../unit-context-manifest.js";
|
|
7
8
|
import { logWarning } from "../workflow-logger.js";
|
|
9
|
+
import { isGsdWorktreePath, resolveWorktreeProjectRoot } from "../worktree-root.js";
|
|
8
10
|
|
|
9
11
|
/**
|
|
10
12
|
* Regex matching milestone CONTEXT.md file names in both legacy M001
|
|
@@ -882,3 +884,135 @@ export function shouldBlockPlanningUnit(
|
|
|
882
884
|
// avoids breaking gsd_* MCP tools or future safe additions.
|
|
883
885
|
return { block: false };
|
|
884
886
|
}
|
|
887
|
+
|
|
888
|
+
// ─── Worktree isolation write gate (#5199) ────────────────────────────────
|
|
889
|
+
//
|
|
890
|
+
// When `git.isolation: worktree` is configured, the per-unit commit pipeline
|
|
891
|
+
// only runs inside the auto-mode loop (`auto-post-unit.ts`). If the LLM
|
|
892
|
+
// authors code at the project root before auto-mode is started, those writes
|
|
893
|
+
// land in the working tree but never reach a commit — they're silently
|
|
894
|
+
// orphaned outside git history. This guard blocks those writes at the
|
|
895
|
+
// tool_call seam so the agent receives a clear error instead.
|
|
896
|
+
|
|
897
|
+
const WORKTREE_GATE_BOOTSTRAP_UNITS = new Set([
|
|
898
|
+
"discuss-milestone",
|
|
899
|
+
"plan-milestone",
|
|
900
|
+
"init",
|
|
901
|
+
]);
|
|
902
|
+
|
|
903
|
+
function realpathOrResolve(p: string): string {
|
|
904
|
+
const abs = resolve(p);
|
|
905
|
+
try {
|
|
906
|
+
return realpathSync(abs);
|
|
907
|
+
} catch {
|
|
908
|
+
// Path doesn't exist (yet) — realpath the deepest existing ancestor so
|
|
909
|
+
// platforms where /tmp -> /private/tmp don't break containment checks.
|
|
910
|
+
let dir = abs;
|
|
911
|
+
const tail: string[] = [];
|
|
912
|
+
while (dir && dir !== resolve(dir, "..")) {
|
|
913
|
+
try {
|
|
914
|
+
const real = realpathSync(dir);
|
|
915
|
+
return tail.length ? join(real, ...tail.reverse()) : real;
|
|
916
|
+
} catch {
|
|
917
|
+
const idx = dir.lastIndexOf(sep);
|
|
918
|
+
if (idx <= 0) break;
|
|
919
|
+
tail.push(dir.slice(idx + 1));
|
|
920
|
+
dir = dir.slice(0, idx) || sep;
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
return abs;
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
function isPathContained(target: string, container: string): boolean {
|
|
928
|
+
if (target === container) return true;
|
|
929
|
+
return target.startsWith(container.endsWith(sep) ? container : container + sep);
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
/**
|
|
933
|
+
* Block planning-write tool calls that would land code at the project root
|
|
934
|
+
* while `git.isolation: worktree` is in effect and auto-mode hasn't created
|
|
935
|
+
* (or flipped cwd into) the milestone worktree.
|
|
936
|
+
*
|
|
937
|
+
* Pure / unit-testable. Callers in `register-hooks.ts` supply the resolved
|
|
938
|
+
* project root and current auto liveness; this function does no I/O beyond
|
|
939
|
+
* realpath resolution.
|
|
940
|
+
*
|
|
941
|
+
* Allow rules (in order):
|
|
942
|
+
* 1. Tool isn't a planning-write (write/edit/multi_edit/notebook_edit).
|
|
943
|
+
* 2. `GSD_DISABLE_WORKTREE_WRITE_GUARD=1` self-hosting bypass.
|
|
944
|
+
* 3. Isolation mode is not "worktree".
|
|
945
|
+
* 4. Active unit is a bootstrap unit (discuss-milestone/plan-milestone/init).
|
|
946
|
+
* 5. Target is inside `<projectRoot>/.gsd/worktrees/` (a real worktree).
|
|
947
|
+
* 6. Target is inside `<projectRoot>/.gsd/` and isn't masquerading as a
|
|
948
|
+
* worktrees sibling (rejects the `.gsd/worktrees-extra/…` prefix trick).
|
|
949
|
+
* 7. Auto is live AND `effectiveBasePath` is itself a `.gsd/worktrees/…` path.
|
|
950
|
+
*
|
|
951
|
+
* Otherwise: block with a message that points the agent at `/gsd` to start
|
|
952
|
+
* auto-mode.
|
|
953
|
+
*/
|
|
954
|
+
export function shouldBlockWorktreeWrite(
|
|
955
|
+
toolName: string,
|
|
956
|
+
targetPath: string,
|
|
957
|
+
effectiveBasePath: string,
|
|
958
|
+
isAutoLive: boolean,
|
|
959
|
+
currentUnitType?: string | null,
|
|
960
|
+
): { block: boolean; reason?: string } {
|
|
961
|
+
const tool = canonicalToolName(toolName);
|
|
962
|
+
if (!PLANNING_WRITE_TOOLS.has(tool)) return { block: false };
|
|
963
|
+
if (process.env.GSD_DISABLE_WORKTREE_WRITE_GUARD === "1") return { block: false };
|
|
964
|
+
if (getIsolationMode(effectiveBasePath) !== "worktree") return { block: false };
|
|
965
|
+
if (currentUnitType && WORKTREE_GATE_BOOTSTRAP_UNITS.has(currentUnitType)) return { block: false };
|
|
966
|
+
|
|
967
|
+
if (!targetPath) {
|
|
968
|
+
return {
|
|
969
|
+
block: true,
|
|
970
|
+
reason: [
|
|
971
|
+
`HARD BLOCK: ${tool} called with empty path while \`git.isolation: worktree\` is configured`,
|
|
972
|
+
`and auto-mode is not active. Refusing to allow writes that cannot be located.`,
|
|
973
|
+
].join(" "),
|
|
974
|
+
};
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
// Resolve the target relative to the project root, then realpath to defeat
|
|
978
|
+
// symlink-based escapes and prefix tricks (e.g. .gsd/worktrees-extra/).
|
|
979
|
+
const projectRoot = resolveWorktreeProjectRoot(effectiveBasePath);
|
|
980
|
+
const absTarget = isAbsolute(targetPath) ? targetPath : resolve(projectRoot, targetPath);
|
|
981
|
+
const realTarget = realpathOrResolve(absTarget);
|
|
982
|
+
const realRoot = realpathOrResolve(projectRoot);
|
|
983
|
+
const realGsd = realpathOrResolve(join(projectRoot, ".gsd"));
|
|
984
|
+
const realWorktreesDir = realpathOrResolve(join(projectRoot, ".gsd", "worktrees"));
|
|
985
|
+
|
|
986
|
+
// Allow writes inside the legitimate worktrees subtree.
|
|
987
|
+
if (isPathContained(realTarget, realWorktreesDir)) return { block: false };
|
|
988
|
+
|
|
989
|
+
// Allow writes to .gsd/ planning artifacts, but reject siblings whose name
|
|
990
|
+
// starts with "worktrees" (the worktrees-extra prefix trick — case 4).
|
|
991
|
+
if (isPathContained(realTarget, realGsd)) {
|
|
992
|
+
const rel = relative(realGsd, realTarget);
|
|
993
|
+
const firstSeg = rel.split(/[\/\\]/)[0] ?? "";
|
|
994
|
+
if (!firstSeg.startsWith("worktrees")) return { block: false };
|
|
995
|
+
// fall through: looks like worktrees<something> sibling — block
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
// Auto is live and the caller is operating inside a worktree path —
|
|
999
|
+
// host tool's write happens in worktree context; let it through.
|
|
1000
|
+
if (isAutoLive && isGsdWorktreePath(effectiveBasePath)) return { block: false };
|
|
1001
|
+
|
|
1002
|
+
// Block. Provide enough context that the agent can self-correct.
|
|
1003
|
+
const displayTarget = isPathContained(realTarget, realRoot)
|
|
1004
|
+
? relative(realRoot, realTarget) || "."
|
|
1005
|
+
: realTarget;
|
|
1006
|
+
return {
|
|
1007
|
+
block: true,
|
|
1008
|
+
reason: [
|
|
1009
|
+
`HARD BLOCK: Worktree isolation is configured (\`git.isolation: worktree\`) but auto-mode is`,
|
|
1010
|
+
`not running and the target "${displayTarget}" is not inside \`.gsd/worktrees/<MID>/\`.`,
|
|
1011
|
+
`Code edits at the project root would be lost — only the auto-mode commit pipeline`,
|
|
1012
|
+
`(auto-post-unit) commits work, and it never runs outside the loop.`,
|
|
1013
|
+
`Required action: start auto-mode with \`/gsd\` so the milestone worktree is created,`,
|
|
1014
|
+
`then write inside it. To disable this guard for self-hosting development, set`,
|
|
1015
|
+
`GSD_DISABLE_WORKTREE_WRITE_GUARD=1.`,
|
|
1016
|
+
].join(" "),
|
|
1017
|
+
};
|
|
1018
|
+
}
|
|
@@ -222,14 +222,19 @@ Using the \`write\` tool, persist the full structured report to
|
|
|
222
222
|
LEARNINGS.md is the full, cited audit trail. Write it first — subsequent steps
|
|
223
223
|
feed from its content.
|
|
224
224
|
|
|
225
|
-
### Step 3 —
|
|
225
|
+
### Step 3 — Run one bounded duplicate check
|
|
226
226
|
|
|
227
|
-
Before persisting
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
227
|
+
Before persisting extracted items in Steps 4–6, do at most one duplicate-check
|
|
228
|
+
pass for the durable Decisions, Lessons, and Patterns from this milestone. Use
|
|
229
|
+
the already-written LEARNINGS.md content and call \`memory_query\` once with a
|
|
230
|
+
compact keyword summary for the whole batch. If that result clearly shows a
|
|
231
|
+
semantically equivalent high-confidence memory for an item, mark only that item
|
|
232
|
+
as already captured and skip it in its respective persistence step.
|
|
233
|
+
|
|
234
|
+
Do not re-read milestone artefacts or repeat memory queries category-by-category
|
|
235
|
+
after this point. The memory store is the single source of truth for
|
|
236
|
+
cross-session durable knowledge — no other persistence call is part of this
|
|
237
|
+
flow.
|
|
233
238
|
|
|
234
239
|
### Step 4 — Persist Patterns via \`capture_thought\`
|
|
235
240
|
|
|
@@ -268,11 +273,11 @@ later projection back to a human-visible decisions register stays lossless
|
|
|
268
273
|
|
|
269
274
|
### Step 7 — Deduplication rule (applies to Steps 4, 5, 6)
|
|
270
275
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
over creating a second slightly-different row
|
|
275
|
-
signal.
|
|
276
|
+
Use only the duplicate-check result from Step 3. If that bounded check returned
|
|
277
|
+
a semantically equivalent memory at high confidence for an extracted item, skip
|
|
278
|
+
the capture entirely. Otherwise, persist the item once via \`capture_thought\`.
|
|
279
|
+
Prefer skipping a near-duplicate over creating a second slightly-different row
|
|
280
|
+
— redundancy degrades the signal.
|
|
276
281
|
|
|
277
282
|
### Step 8 — Surprises stay only in LEARNINGS.md
|
|
278
283
|
|
|
@@ -323,6 +323,20 @@ export function createBaseSchemaObjects(db: DbAdapter, hooks: BaseSchemaHooks):
|
|
|
323
323
|
)
|
|
324
324
|
`);
|
|
325
325
|
|
|
326
|
+
db.exec(`
|
|
327
|
+
CREATE TABLE IF NOT EXISTS milestone_commit_attributions (
|
|
328
|
+
commit_sha TEXT NOT NULL,
|
|
329
|
+
milestone_id TEXT NOT NULL,
|
|
330
|
+
slice_id TEXT DEFAULT NULL,
|
|
331
|
+
task_id TEXT DEFAULT NULL,
|
|
332
|
+
source TEXT NOT NULL DEFAULT 'recorded',
|
|
333
|
+
confidence REAL NOT NULL DEFAULT 1.0,
|
|
334
|
+
files_json TEXT NOT NULL DEFAULT '[]',
|
|
335
|
+
created_at TEXT NOT NULL DEFAULT '',
|
|
336
|
+
PRIMARY KEY (commit_sha, milestone_id)
|
|
337
|
+
)
|
|
338
|
+
`);
|
|
339
|
+
|
|
326
340
|
db.exec(`
|
|
327
341
|
CREATE TABLE IF NOT EXISTS audit_events (
|
|
328
342
|
event_id TEXT PRIMARY KEY,
|
|
@@ -359,6 +373,7 @@ export function createBaseSchemaObjects(db: DbAdapter, hooks: BaseSchemaHooks):
|
|
|
359
373
|
db.exec("CREATE INDEX IF NOT EXISTS idx_gate_runs_turn ON gate_runs(trace_id, turn_id)");
|
|
360
374
|
db.exec("CREATE INDEX IF NOT EXISTS idx_gate_runs_lookup ON gate_runs(milestone_id, slice_id, task_id, gate_id)");
|
|
361
375
|
db.exec("CREATE INDEX IF NOT EXISTS idx_turn_git_tx_turn ON turn_git_transactions(trace_id, turn_id)");
|
|
376
|
+
db.exec("CREATE INDEX IF NOT EXISTS idx_milestone_commit_attr_milestone ON milestone_commit_attributions(milestone_id)");
|
|
362
377
|
db.exec("CREATE INDEX IF NOT EXISTS idx_audit_events_trace ON audit_events(trace_id, ts)");
|
|
363
378
|
db.exec("CREATE INDEX IF NOT EXISTS idx_audit_events_turn ON audit_events(trace_id, turn_id, ts)");
|
|
364
379
|
|
|
@@ -399,6 +399,23 @@ export function applyMigrationV23MilestoneQueue(db: DbAdapter): void {
|
|
|
399
399
|
ensureColumn(db, "milestones", "sequence", "ALTER TABLE milestones ADD COLUMN sequence INTEGER DEFAULT 0");
|
|
400
400
|
}
|
|
401
401
|
|
|
402
|
+
export function applyMigrationV26MilestoneCommitAttributions(db: DbAdapter): void {
|
|
403
|
+
db.exec(`
|
|
404
|
+
CREATE TABLE IF NOT EXISTS milestone_commit_attributions (
|
|
405
|
+
commit_sha TEXT NOT NULL,
|
|
406
|
+
milestone_id TEXT NOT NULL,
|
|
407
|
+
slice_id TEXT DEFAULT NULL,
|
|
408
|
+
task_id TEXT DEFAULT NULL,
|
|
409
|
+
source TEXT NOT NULL DEFAULT 'recorded',
|
|
410
|
+
confidence REAL NOT NULL DEFAULT 1.0,
|
|
411
|
+
files_json TEXT NOT NULL DEFAULT '[]',
|
|
412
|
+
created_at TEXT NOT NULL DEFAULT '',
|
|
413
|
+
PRIMARY KEY (commit_sha, milestone_id)
|
|
414
|
+
)
|
|
415
|
+
`);
|
|
416
|
+
db.exec("CREATE INDEX IF NOT EXISTS idx_milestone_commit_attr_milestone ON milestone_commit_attributions(milestone_id)");
|
|
417
|
+
}
|
|
418
|
+
|
|
402
419
|
export interface MigrationV22Hooks {
|
|
403
420
|
copyQualityGateRowsToRepairedTable(db: DbAdapter): void;
|
|
404
421
|
}
|
|
@@ -77,6 +77,7 @@ import {
|
|
|
77
77
|
applyMigrationV21StructuredMemories,
|
|
78
78
|
applyMigrationV22QualityGateRepair,
|
|
79
79
|
applyMigrationV23MilestoneQueue,
|
|
80
|
+
applyMigrationV26MilestoneCommitAttributions,
|
|
80
81
|
} from "./db-migration-steps.js";
|
|
81
82
|
import { isMemoriesFtsAvailableSchema, tryCreateMemoriesFtsSchema } from "./db-memory-fts-schema.js";
|
|
82
83
|
import { createDbOpenState, type DbOpenPhase } from "./db-open-state.js";
|
|
@@ -105,7 +106,7 @@ const providerLoader = createSqliteProviderLoader({
|
|
|
105
106
|
writeStderr: (message: string) => process.stderr.write(message),
|
|
106
107
|
});
|
|
107
108
|
|
|
108
|
-
export const SCHEMA_VERSION =
|
|
109
|
+
export const SCHEMA_VERSION = 26;
|
|
109
110
|
|
|
110
111
|
function initSchema(db: DbAdapter, fileBacked: boolean): void {
|
|
111
112
|
if (fileBacked) db.exec("PRAGMA journal_mode=WAL");
|
|
@@ -329,6 +330,11 @@ function migrateSchema(db: DbAdapter): void {
|
|
|
329
330
|
recordSchemaVersion(db, 25);
|
|
330
331
|
}
|
|
331
332
|
|
|
333
|
+
if (currentVersion < 26) {
|
|
334
|
+
applyMigrationV26MilestoneCommitAttributions(db);
|
|
335
|
+
recordSchemaVersion(db, 26);
|
|
336
|
+
}
|
|
337
|
+
|
|
332
338
|
db.exec("COMMIT");
|
|
333
339
|
} catch (err) {
|
|
334
340
|
db.exec("ROLLBACK");
|
|
@@ -1349,6 +1355,45 @@ export function getSliceTasks(milestoneId: string, sliceId: string): TaskRow[] {
|
|
|
1349
1355
|
return rows.map(rowToTask);
|
|
1350
1356
|
}
|
|
1351
1357
|
|
|
1358
|
+
export function getCompletedMilestoneTaskFileHints(milestoneId: string): string[] {
|
|
1359
|
+
if (!currentDb) return [];
|
|
1360
|
+
const rows = currentDb.prepare(
|
|
1361
|
+
`SELECT files, key_files
|
|
1362
|
+
FROM tasks
|
|
1363
|
+
WHERE milestone_id = :mid AND status IN ('complete', 'done')`,
|
|
1364
|
+
).all({ ":mid": milestoneId }) as Array<Record<string, unknown>>;
|
|
1365
|
+
|
|
1366
|
+
const hints = new Set<string>();
|
|
1367
|
+
for (const row of rows) {
|
|
1368
|
+
for (const raw of [row["files"], row["key_files"]]) {
|
|
1369
|
+
for (const file of parseStringArrayColumn(raw)) {
|
|
1370
|
+
const normalized = normalizeRepoPath(file);
|
|
1371
|
+
if (normalized) hints.add(normalized);
|
|
1372
|
+
}
|
|
1373
|
+
}
|
|
1374
|
+
}
|
|
1375
|
+
return [...hints];
|
|
1376
|
+
}
|
|
1377
|
+
|
|
1378
|
+
function parseStringArrayColumn(raw: unknown): string[] {
|
|
1379
|
+
if (Array.isArray(raw)) return raw.filter((entry): entry is string => typeof entry === "string");
|
|
1380
|
+
if (typeof raw !== "string") return [];
|
|
1381
|
+
const trimmed = raw.trim();
|
|
1382
|
+
if (!trimmed) return [];
|
|
1383
|
+
try {
|
|
1384
|
+
const parsed = JSON.parse(trimmed);
|
|
1385
|
+
if (Array.isArray(parsed)) return parsed.filter((entry): entry is string => typeof entry === "string");
|
|
1386
|
+
if (typeof parsed === "string") return [parsed];
|
|
1387
|
+
} catch {
|
|
1388
|
+
return trimmed.split(",");
|
|
1389
|
+
}
|
|
1390
|
+
return [];
|
|
1391
|
+
}
|
|
1392
|
+
|
|
1393
|
+
function normalizeRepoPath(file: string): string {
|
|
1394
|
+
return file.trim().replace(/\\/g, "/").replace(/^\.\/+/, "");
|
|
1395
|
+
}
|
|
1396
|
+
|
|
1352
1397
|
// ─── ADR-011 Phase 2 escalation helpers ──────────────────────────────────
|
|
1353
1398
|
|
|
1354
1399
|
/** Set pause-on-escalation state on a completed task. Mutually exclusive with awaiting_review. */
|
|
@@ -2073,6 +2118,9 @@ export function deleteMilestone(milestoneId: string): void {
|
|
|
2073
2118
|
currentDb!.prepare(
|
|
2074
2119
|
`DELETE FROM artifacts WHERE milestone_id = :mid`,
|
|
2075
2120
|
).run({ ":mid": milestoneId });
|
|
2121
|
+
currentDb!.prepare(
|
|
2122
|
+
`DELETE FROM milestone_commit_attributions WHERE milestone_id = :mid`,
|
|
2123
|
+
).run({ ":mid": milestoneId });
|
|
2076
2124
|
currentDb!.prepare(
|
|
2077
2125
|
`DELETE FROM milestone_leases WHERE milestone_id = :mid`,
|
|
2078
2126
|
).run({ ":mid": milestoneId });
|
|
@@ -2391,6 +2439,75 @@ export function upsertTurnGitTransaction(entry: {
|
|
|
2391
2439
|
});
|
|
2392
2440
|
}
|
|
2393
2441
|
|
|
2442
|
+
export function getMilestoneCommitAttributionShas(milestoneId: string): string[] {
|
|
2443
|
+
if (!currentDb) return [];
|
|
2444
|
+
const rows = currentDb.prepare(
|
|
2445
|
+
`SELECT commit_sha
|
|
2446
|
+
FROM milestone_commit_attributions
|
|
2447
|
+
WHERE milestone_id = :mid
|
|
2448
|
+
ORDER BY created_at, commit_sha`,
|
|
2449
|
+
).all({ ":mid": milestoneId }) as Array<Record<string, unknown>>;
|
|
2450
|
+
return rows
|
|
2451
|
+
.map((row) => typeof row["commit_sha"] === "string" ? row["commit_sha"] : "")
|
|
2452
|
+
.filter(Boolean);
|
|
2453
|
+
}
|
|
2454
|
+
|
|
2455
|
+
export function recordMilestoneCommitAttribution(entry: {
|
|
2456
|
+
commitSha: string;
|
|
2457
|
+
milestoneId: string;
|
|
2458
|
+
sliceId?: string;
|
|
2459
|
+
taskId?: string;
|
|
2460
|
+
source: "recorded" | "backfill";
|
|
2461
|
+
confidence: number;
|
|
2462
|
+
files: string[];
|
|
2463
|
+
createdAt: string;
|
|
2464
|
+
}): void {
|
|
2465
|
+
if (!currentDb) return;
|
|
2466
|
+
transaction(() => {
|
|
2467
|
+
currentDb!.prepare(
|
|
2468
|
+
`INSERT OR REPLACE INTO milestone_commit_attributions (
|
|
2469
|
+
commit_sha, milestone_id, slice_id, task_id, source, confidence, files_json, created_at
|
|
2470
|
+
) VALUES (
|
|
2471
|
+
:commit_sha, :milestone_id, :slice_id, :task_id, :source, :confidence, :files_json, :created_at
|
|
2472
|
+
)`,
|
|
2473
|
+
).run({
|
|
2474
|
+
":commit_sha": entry.commitSha,
|
|
2475
|
+
":milestone_id": entry.milestoneId,
|
|
2476
|
+
":slice_id": entry.sliceId ?? null,
|
|
2477
|
+
":task_id": entry.taskId ?? null,
|
|
2478
|
+
":source": entry.source,
|
|
2479
|
+
":confidence": entry.confidence,
|
|
2480
|
+
":files_json": JSON.stringify(entry.files),
|
|
2481
|
+
":created_at": entry.createdAt,
|
|
2482
|
+
});
|
|
2483
|
+
|
|
2484
|
+
currentDb!.prepare(
|
|
2485
|
+
`INSERT OR IGNORE INTO audit_events (
|
|
2486
|
+
event_id, trace_id, turn_id, caused_by, category, type, ts, payload_json
|
|
2487
|
+
) VALUES (
|
|
2488
|
+
:event_id, :trace_id, :turn_id, :caused_by, :category, :type, :ts, :payload_json
|
|
2489
|
+
)`,
|
|
2490
|
+
).run({
|
|
2491
|
+
":event_id": `milestone-commit-attribution:${entry.milestoneId}:${entry.commitSha}`,
|
|
2492
|
+
":trace_id": "milestone-commit-attribution",
|
|
2493
|
+
":turn_id": null,
|
|
2494
|
+
":caused_by": null,
|
|
2495
|
+
":category": "git",
|
|
2496
|
+
":type": "milestone-commit-attribution-recorded",
|
|
2497
|
+
":ts": entry.createdAt,
|
|
2498
|
+
":payload_json": JSON.stringify({
|
|
2499
|
+
commitSha: entry.commitSha,
|
|
2500
|
+
milestoneId: entry.milestoneId,
|
|
2501
|
+
sliceId: entry.sliceId ?? null,
|
|
2502
|
+
taskId: entry.taskId ?? null,
|
|
2503
|
+
source: entry.source,
|
|
2504
|
+
confidence: entry.confidence,
|
|
2505
|
+
files: entry.files,
|
|
2506
|
+
}),
|
|
2507
|
+
});
|
|
2508
|
+
});
|
|
2509
|
+
}
|
|
2510
|
+
|
|
2394
2511
|
export function insertAuditEvent(entry: {
|
|
2395
2512
|
eventId: string;
|
|
2396
2513
|
traceId: string;
|
|
@@ -2494,6 +2611,7 @@ export function clearEngineHierarchy(): void {
|
|
|
2494
2611
|
currentDb!.exec("DELETE FROM slice_dependencies");
|
|
2495
2612
|
currentDb!.exec("DELETE FROM assessments");
|
|
2496
2613
|
currentDb!.exec("DELETE FROM replan_history");
|
|
2614
|
+
currentDb!.exec("DELETE FROM milestone_commit_attributions");
|
|
2497
2615
|
currentDb!.exec("DELETE FROM tasks");
|
|
2498
2616
|
currentDb!.exec("DELETE FROM slices");
|
|
2499
2617
|
currentDb!.exec("DELETE FROM milestone_leases");
|
|
@@ -2064,8 +2064,8 @@ export async function showSmartEntry(
|
|
|
2064
2064
|
// standard wizard below.
|
|
2065
2065
|
{
|
|
2066
2066
|
const prefs = loadEffectiveGSDPreferences(basePath)?.preferences;
|
|
2067
|
-
const {
|
|
2068
|
-
if (
|
|
2067
|
+
const { shouldRunDeepProjectSetup } = await import("./auto-dispatch.js");
|
|
2068
|
+
if (shouldRunDeepProjectSetup(state, prefs, basePath)) {
|
|
2069
2069
|
await startDeepProjectSetupForeground(ctx, pi, basePath, stepMode);
|
|
2070
2070
|
return;
|
|
2071
2071
|
}
|
|
@@ -11,6 +11,7 @@ export interface PrEvidenceInput {
|
|
|
11
11
|
changeType?: PrChangeType;
|
|
12
12
|
linkedIssue?: string;
|
|
13
13
|
summaries?: string[];
|
|
14
|
+
blockers?: string[];
|
|
14
15
|
roadmapItems?: string[];
|
|
15
16
|
metrics?: string[];
|
|
16
17
|
testsRun?: string[];
|
|
@@ -34,8 +35,54 @@ const CHANGE_TYPE_LABELS: Record<PrChangeType, string> = {
|
|
|
34
35
|
chore: "Build, CI, or tooling changes",
|
|
35
36
|
};
|
|
36
37
|
|
|
38
|
+
// Per-item cap for user-supplied content. 2 KB gives slice titles plus
|
|
39
|
+
// descriptions room while still bounding malicious DoS-via-PR-body input.
|
|
40
|
+
const USER_CONTENT_CAP_BYTES = 2048;
|
|
41
|
+
const TRUNCATION_SUFFIX = " … [truncated]";
|
|
42
|
+
|
|
43
|
+
// Strips HTML comments, fake commit trailers (Co-Authored-By, Signed-off-by —
|
|
44
|
+
// case-insensitive on the trailer name), and caps total length. Designed to
|
|
45
|
+
// be a no-op for well-formed input; golden fixtures must remain byte-stable.
|
|
46
|
+
// Trailer lines are removed (not rejected) so that a single bad line in an
|
|
47
|
+
// otherwise legitimate description does not block the entire PR.
|
|
48
|
+
function sanitizeUserContent(s: string): string {
|
|
49
|
+
if (!s) return s;
|
|
50
|
+
// Strip HTML comments greedily across newlines.
|
|
51
|
+
let out = s.replace(/<!--[\s\S]*?-->/g, "");
|
|
52
|
+
// Drop lines that look like commit trailers we do not want forged.
|
|
53
|
+
out = out
|
|
54
|
+
.split("\n")
|
|
55
|
+
.filter((line) => !/^\s*(co-authored-by|signed-off-by)\s*:/i.test(line))
|
|
56
|
+
.join("\n");
|
|
57
|
+
if (Buffer.byteLength(out, "utf8") > USER_CONTENT_CAP_BYTES) {
|
|
58
|
+
const budget = USER_CONTENT_CAP_BYTES - Buffer.byteLength(TRUNCATION_SUFFIX, "utf8");
|
|
59
|
+
// Truncate by code units to stay safely under the byte budget for ASCII;
|
|
60
|
+
// for multibyte content we conservatively walk back until under budget.
|
|
61
|
+
let sliced = out.slice(0, Math.max(0, budget));
|
|
62
|
+
while (Buffer.byteLength(sliced, "utf8") > budget && sliced.length > 0) {
|
|
63
|
+
sliced = sliced.slice(0, -1);
|
|
64
|
+
}
|
|
65
|
+
out = sliced + TRUNCATION_SUFFIX;
|
|
66
|
+
}
|
|
67
|
+
return out;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Strips HTML comments and fake trailers without applying the length cap.
|
|
71
|
+
// Used for short fields like linkedIssue where truncation would be confusing.
|
|
72
|
+
function sanitizeIssueRef(s: string): string {
|
|
73
|
+
if (!s) return s;
|
|
74
|
+
let out = s.replace(/<!--[\s\S]*?-->/g, "");
|
|
75
|
+
out = out
|
|
76
|
+
.split("\n")
|
|
77
|
+
.filter((line) => !/^\s*(co-authored-by|signed-off-by)\s*:/i.test(line))
|
|
78
|
+
.join("\n");
|
|
79
|
+
return out;
|
|
80
|
+
}
|
|
81
|
+
|
|
37
82
|
function normalizeList(values: readonly string[] | undefined): string[] {
|
|
38
|
-
return (values ?? [])
|
|
83
|
+
return (values ?? [])
|
|
84
|
+
.map((value) => sanitizeUserContent(value).trim())
|
|
85
|
+
.filter(Boolean);
|
|
39
86
|
}
|
|
40
87
|
|
|
41
88
|
function changeTypeChecklist(selected: PrChangeType): string[] {
|
|
@@ -56,13 +103,17 @@ export function buildPrEvidence(input: PrEvidenceInput): PrEvidence {
|
|
|
56
103
|
const subjectTitle = input.milestoneTitle?.trim() || subjectId;
|
|
57
104
|
const changeType = input.changeType ?? "feat";
|
|
58
105
|
const summaries = normalizeList(input.summaries);
|
|
106
|
+
const blockers = normalizeList(input.blockers);
|
|
59
107
|
const roadmapItems = normalizeList(input.roadmapItems);
|
|
60
108
|
const metrics = normalizeList(input.metrics);
|
|
61
109
|
const testsRun = normalizeList(input.testsRun);
|
|
62
110
|
const rollbackNotes = normalizeList(input.rollbackNotes);
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
const
|
|
111
|
+
// linkedIssue is sanitized but not length-capped: legitimate issue refs
|
|
112
|
+
// are short by nature, and truncating "Closes #123" would be unhelpful.
|
|
113
|
+
const linkedIssueRaw = input.linkedIssue ? sanitizeIssueRef(input.linkedIssue).trim() : "";
|
|
114
|
+
const linkedIssue = linkedIssueRaw || "Not specified. Add an issue link before marking this PR ready if CONTRIBUTING.md requires one.";
|
|
115
|
+
const why = (input.why ? sanitizeUserContent(input.why).trim() : "") || `${capitalize(subjectKind)} work is complete and ready for review.`;
|
|
116
|
+
const how = (input.how ? sanitizeUserContent(input.how).trim() : "") || "Generated from GSD evidence and local workflow artifacts.";
|
|
66
117
|
const title = `${changeType}: ${subjectTitle}`;
|
|
67
118
|
|
|
68
119
|
const sections: string[] = [
|
|
@@ -75,6 +126,13 @@ export function buildPrEvidence(input: PrEvidenceInput): PrEvidence {
|
|
|
75
126
|
"## What",
|
|
76
127
|
"",
|
|
77
128
|
summaries.length > 0 ? summaries.join("\n\n") : `${capitalize(subjectKind)} ${subjectId} completed.`,
|
|
129
|
+
];
|
|
130
|
+
|
|
131
|
+
if (blockers.length > 0) {
|
|
132
|
+
sections.push("", "## Blockers", "", blockers.map((blocker) => `- ${blocker}`).join("\n"));
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
sections.push(
|
|
78
136
|
"",
|
|
79
137
|
"## Why",
|
|
80
138
|
"",
|
|
@@ -87,7 +145,7 @@ export function buildPrEvidence(input: PrEvidenceInput): PrEvidence {
|
|
|
87
145
|
"## Linked Issue",
|
|
88
146
|
"",
|
|
89
147
|
linkedIssue,
|
|
90
|
-
|
|
148
|
+
);
|
|
91
149
|
|
|
92
150
|
if (roadmapItems.length > 0) {
|
|
93
151
|
sections.push("", "## Roadmap", "", roadmapItems.join("\n"));
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import test from "node:test";
|
|
2
2
|
import assert from "node:assert/strict";
|
|
3
3
|
|
|
4
|
-
import { _buildAbortedPauseContext } from "../bootstrap/agent-end-recovery.js";
|
|
4
|
+
import { _buildAbortedPauseContext, isUserInitiatedAbortMessage } from "../bootstrap/agent-end-recovery.js";
|
|
5
5
|
import { _buildCancelledUnitStopReason } from "../auto/phases.js";
|
|
6
6
|
|
|
7
7
|
test("aborted agent_end maps errorMessage into structured aborted pause context", () => {
|
|
@@ -30,3 +30,9 @@ test("cancelled non-session failures are labeled as unit aborts (not session-cre
|
|
|
30
30
|
assert.equal(cancelled.stopReason, "Unit aborted: tool invocation cancelled");
|
|
31
31
|
assert.equal(cancelled.loopReason, "unit-aborted");
|
|
32
32
|
});
|
|
33
|
+
|
|
34
|
+
test("provider user-abort errors are recognized as cancellations, not provider outages", () => {
|
|
35
|
+
assert.equal(isUserInitiatedAbortMessage("Claude Code process aborted by user"), true);
|
|
36
|
+
assert.equal(isUserInitiatedAbortMessage("Request aborted by user"), true);
|
|
37
|
+
assert.equal(isUserInitiatedAbortMessage("HTTP 503 Service Unavailable"), false);
|
|
38
|
+
});
|
|
@@ -23,6 +23,8 @@ import {
|
|
|
23
23
|
_refreshLastCommitForTests,
|
|
24
24
|
_getLastCommitForTests,
|
|
25
25
|
_getLastCommitFetchedAtForTests,
|
|
26
|
+
formatRuntimeHealthSignal,
|
|
27
|
+
shouldRenderRoadmapProgress,
|
|
26
28
|
} from "../auto-dashboard.ts";
|
|
27
29
|
import {
|
|
28
30
|
openDatabase,
|
|
@@ -193,6 +195,37 @@ test("formatWidgetTokens formats millions with M", () => {
|
|
|
193
195
|
assert.equal(formatWidgetTokens(25_000_000), "25M");
|
|
194
196
|
});
|
|
195
197
|
|
|
198
|
+
test("formatRuntimeHealthSignal surfaces idle recovery instead of generic progress", () => {
|
|
199
|
+
const signal = formatRuntimeHealthSignal({
|
|
200
|
+
version: 1,
|
|
201
|
+
unitType: "research-milestone",
|
|
202
|
+
unitId: "M001",
|
|
203
|
+
startedAt: 1_000,
|
|
204
|
+
updatedAt: 600_000,
|
|
205
|
+
phase: "recovered",
|
|
206
|
+
wrapupWarningSent: false,
|
|
207
|
+
continueHereFired: false,
|
|
208
|
+
timeoutAt: null,
|
|
209
|
+
lastProgressAt: 1_000,
|
|
210
|
+
progressCount: 1,
|
|
211
|
+
lastProgressKind: "idle-recovery-retry",
|
|
212
|
+
recoveryAttempts: 1,
|
|
213
|
+
lastRecoveryReason: "idle",
|
|
214
|
+
}, 600_000);
|
|
215
|
+
|
|
216
|
+
assert.deepEqual(signal, {
|
|
217
|
+
level: "yellow",
|
|
218
|
+
summary: "Recovering",
|
|
219
|
+
detail: "retry 1 after idle stall",
|
|
220
|
+
});
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
test("shouldRenderRoadmapProgress hides pre-roadmap zero-slice progress", () => {
|
|
224
|
+
assert.equal(shouldRenderRoadmapProgress(null), false);
|
|
225
|
+
assert.equal(shouldRenderRoadmapProgress({ done: 0, total: 0, activeSliceTasks: null } as any), false);
|
|
226
|
+
assert.equal(shouldRenderRoadmapProgress({ done: 0, total: 1, activeSliceTasks: null } as any), true);
|
|
227
|
+
});
|
|
228
|
+
|
|
196
229
|
// ─── estimateTimeRemaining ──────────────────────────────────────────────
|
|
197
230
|
|
|
198
231
|
test("estimateTimeRemaining returns null when no ledger data", () => {
|