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
|
@@ -30,6 +30,10 @@ import {
|
|
|
30
30
|
insertTask,
|
|
31
31
|
getTask,
|
|
32
32
|
getSliceTasks,
|
|
33
|
+
deleteMilestone,
|
|
34
|
+
clearEngineHierarchy,
|
|
35
|
+
recordMilestoneCommitAttribution,
|
|
36
|
+
getMilestoneCommitAttributionShas,
|
|
33
37
|
checkpointDatabase,
|
|
34
38
|
refreshOpenDatabaseFromDisk,
|
|
35
39
|
tryCreateMemoriesFts,
|
|
@@ -1047,6 +1051,46 @@ describe('gsd-db', () => {
|
|
|
1047
1051
|
});
|
|
1048
1052
|
});
|
|
1049
1053
|
|
|
1054
|
+
// ─── milestone_commit_attributions teardown ───────────────────────────────
|
|
1055
|
+
|
|
1056
|
+
describe('milestone commit attribution teardown', () => {
|
|
1057
|
+
test('deleteMilestone removes persisted milestone commit attributions', () => {
|
|
1058
|
+
openDatabase(':memory:');
|
|
1059
|
+
insertMilestone({ id: 'M001', title: 'Milestone', status: 'active' });
|
|
1060
|
+
recordMilestoneCommitAttribution({
|
|
1061
|
+
commitSha: '0123456789abcdef0123456789abcdef01234567',
|
|
1062
|
+
milestoneId: 'M001',
|
|
1063
|
+
source: 'backfill',
|
|
1064
|
+
confidence: 0.8,
|
|
1065
|
+
files: ['app.js'],
|
|
1066
|
+
createdAt: '2026-05-05T00:00:00.000Z',
|
|
1067
|
+
});
|
|
1068
|
+
|
|
1069
|
+
assert.deepEqual(getMilestoneCommitAttributionShas('M001'), ['0123456789abcdef0123456789abcdef01234567']);
|
|
1070
|
+
deleteMilestone('M001');
|
|
1071
|
+
assert.deepEqual(getMilestoneCommitAttributionShas('M001'), []);
|
|
1072
|
+
closeDatabase();
|
|
1073
|
+
});
|
|
1074
|
+
|
|
1075
|
+
test('clearEngineHierarchy removes persisted milestone commit attributions', () => {
|
|
1076
|
+
openDatabase(':memory:');
|
|
1077
|
+
insertMilestone({ id: 'M001', title: 'Milestone', status: 'active' });
|
|
1078
|
+
recordMilestoneCommitAttribution({
|
|
1079
|
+
commitSha: 'fedcba9876543210fedcba9876543210fedcba98',
|
|
1080
|
+
milestoneId: 'M001',
|
|
1081
|
+
source: 'backfill',
|
|
1082
|
+
confidence: 0.8,
|
|
1083
|
+
files: ['app.js'],
|
|
1084
|
+
createdAt: '2026-05-05T00:00:00.000Z',
|
|
1085
|
+
});
|
|
1086
|
+
|
|
1087
|
+
assert.deepEqual(getMilestoneCommitAttributionShas('M001'), ['fedcba9876543210fedcba9876543210fedcba98']);
|
|
1088
|
+
clearEngineHierarchy();
|
|
1089
|
+
assert.deepEqual(getMilestoneCommitAttributionShas('M001'), []);
|
|
1090
|
+
closeDatabase();
|
|
1091
|
+
});
|
|
1092
|
+
});
|
|
1093
|
+
|
|
1050
1094
|
// ─── getDbStatus ───────────────────────────────────────────────────────────
|
|
1051
1095
|
|
|
1052
1096
|
describe('getDbStatus', () => {
|
|
@@ -15,7 +15,7 @@ import { tmpdir } from "node:os";
|
|
|
15
15
|
import { join } from "node:path";
|
|
16
16
|
import { randomUUID } from "node:crypto";
|
|
17
17
|
|
|
18
|
-
import { hasPendingDeepStage } from "../auto-dispatch.ts";
|
|
18
|
+
import { hasPendingDeepStage, shouldRunDeepProjectSetup } from "../auto-dispatch.ts";
|
|
19
19
|
import type { GSDPreferences } from "../preferences.ts";
|
|
20
20
|
import { loadEffectiveGSDPreferences } from "../preferences.ts";
|
|
21
21
|
|
|
@@ -57,6 +57,38 @@ test("hasPendingDeepStage: returns true in deep mode when nothing has been captu
|
|
|
57
57
|
}
|
|
58
58
|
});
|
|
59
59
|
|
|
60
|
+
test("shouldRunDeepProjectSetup: complete state wins over pending deep setup", async () => {
|
|
61
|
+
const base = makeBase();
|
|
62
|
+
try {
|
|
63
|
+
assert.equal(hasPendingDeepStage(deepPrefs, base), true);
|
|
64
|
+
assert.equal(
|
|
65
|
+
shouldRunDeepProjectSetup({ phase: "complete" }, deepPrefs, base),
|
|
66
|
+
false,
|
|
67
|
+
"completed projects must not restart deep setup and loop through auto-mode",
|
|
68
|
+
);
|
|
69
|
+
} finally {
|
|
70
|
+
rmSync(base, { recursive: true, force: true });
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test("shouldRunDeepProjectSetup: only setup phases can trigger pending deep setup", () => {
|
|
75
|
+
const base = makeBase();
|
|
76
|
+
try {
|
|
77
|
+
assert.equal(hasPendingDeepStage(deepPrefs, base), true);
|
|
78
|
+
assert.equal(shouldRunDeepProjectSetup({ phase: "pre-planning" }, deepPrefs, base), true);
|
|
79
|
+
assert.equal(shouldRunDeepProjectSetup({ phase: "needs-discussion" }, deepPrefs, base), true);
|
|
80
|
+
assert.equal(shouldRunDeepProjectSetup({ phase: "planning" }, deepPrefs, base), true);
|
|
81
|
+
assert.equal(shouldRunDeepProjectSetup({ phase: "executing" }, deepPrefs, base), false);
|
|
82
|
+
assert.equal(shouldRunDeepProjectSetup({ phase: "blocked" }, deepPrefs, base), false);
|
|
83
|
+
assert.equal(
|
|
84
|
+
shouldRunDeepProjectSetup({ phase: "pre-planning" }, deepPrefs, base, { hasSurvivorBranch: true }),
|
|
85
|
+
false,
|
|
86
|
+
);
|
|
87
|
+
} finally {
|
|
88
|
+
rmSync(base, { recursive: true, force: true });
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
|
|
60
92
|
test("hasPendingDeepStage: returns true in deep mode when only some gates pass", () => {
|
|
61
93
|
// workflow-preferences captured but PROJECT.md still missing.
|
|
62
94
|
const base = makeBase();
|
|
@@ -11,7 +11,7 @@ import assert from 'node:assert/strict';
|
|
|
11
11
|
import { mkdirSync, writeFileSync, rmSync } from 'node:fs';
|
|
12
12
|
import { join } from 'node:path';
|
|
13
13
|
import { tmpdir } from 'node:os';
|
|
14
|
-
import { loadAndValidateAnswerFile, AnswerInjector } from '../../../../headless
|
|
14
|
+
import { loadAndValidateAnswerFile, AnswerInjector } from '../../../../headless-answers.ts';
|
|
15
15
|
|
|
16
16
|
function makeTempDir(prefix: string): string {
|
|
17
17
|
const dir = join(tmpdir(), `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2)}`);
|
|
@@ -11,8 +11,8 @@ import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from 'node:fs'
|
|
|
11
11
|
import { join } from 'node:path'
|
|
12
12
|
import { tmpdir } from 'node:os'
|
|
13
13
|
|
|
14
|
-
import { handleQuery } from '../../../../headless
|
|
15
|
-
import type { QuerySnapshot } from '../../../../headless
|
|
14
|
+
import { handleQuery } from '../../../../headless-query.ts'
|
|
15
|
+
import type { QuerySnapshot } from '../../../../headless-query.ts'
|
|
16
16
|
import { invalidateStateCache } from '../state.ts'
|
|
17
17
|
|
|
18
18
|
// ─── Fixture Helpers ────────────────────────────────────────────────────────
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
// Project/App: GSD-2
|
|
2
|
+
// File Purpose: Golden-fixture equivalence tests pinning PR-body output for buildPrEvidence and formatSwarmLanePRBody.
|
|
3
|
+
|
|
4
|
+
import test from "node:test";
|
|
5
|
+
import assert from "node:assert/strict";
|
|
6
|
+
import { readFileSync, writeFileSync } from "node:fs";
|
|
7
|
+
import { fileURLToPath } from "node:url";
|
|
8
|
+
import { dirname, join } from "node:path";
|
|
9
|
+
|
|
10
|
+
import { buildPrEvidence, type PrEvidenceInput } from "../pr-evidence.ts";
|
|
11
|
+
import {
|
|
12
|
+
formatSwarmLanePRBody,
|
|
13
|
+
type SwarmLanePRData,
|
|
14
|
+
} from "../../github-sync/templates.ts";
|
|
15
|
+
|
|
16
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
17
|
+
const FIXTURES_DIR = join(__dirname, "fixtures", "pr-body");
|
|
18
|
+
|
|
19
|
+
const UPDATE = process.env.UPDATE_GOLDENS === "1";
|
|
20
|
+
|
|
21
|
+
function compareGolden(name: string, actual: string): void {
|
|
22
|
+
const path = join(FIXTURES_DIR, name);
|
|
23
|
+
if (UPDATE) {
|
|
24
|
+
writeFileSync(path, actual, "utf8");
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const expected = readFileSync(path, "utf8");
|
|
28
|
+
assert.equal(actual, expected, `golden mismatch for ${name}`);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const SHIP_BASIC: PrEvidenceInput = {
|
|
32
|
+
milestoneId: "M001",
|
|
33
|
+
milestoneTitle: "Authentication",
|
|
34
|
+
changeType: "feat",
|
|
35
|
+
linkedIssue: "Closes #123",
|
|
36
|
+
summaries: ["### S01\nImplemented login flow."],
|
|
37
|
+
roadmapItems: ["- [x] **S01: Login**"],
|
|
38
|
+
metrics: ["**Units executed:** 3"],
|
|
39
|
+
testsRun: ["npm test", "npm run typecheck:extensions"],
|
|
40
|
+
why: "Users need to authenticate before accessing protected resources.",
|
|
41
|
+
how: "Added password hash check and session token issuance.",
|
|
42
|
+
rollbackNotes: ["Revert the merge commit."],
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const SHIP_EMPTY_OPTIONALS: PrEvidenceInput = {
|
|
46
|
+
milestoneId: "M001",
|
|
47
|
+
milestoneTitle: "Authentication",
|
|
48
|
+
changeType: "feat",
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const SWARM_WITH_BLOCKERS: SwarmLanePRData = {
|
|
52
|
+
lane: {
|
|
53
|
+
id: "writer",
|
|
54
|
+
branch: "lane/single-writer",
|
|
55
|
+
owner: "@owner",
|
|
56
|
+
latestCommit: "abc1234",
|
|
57
|
+
changedContracts: ["WriterToken"],
|
|
58
|
+
testEvidence: ["npm run typecheck:extensions"],
|
|
59
|
+
blockers: ["Awaiting state-lane writer-sequence merge", "Parity report incomplete"],
|
|
60
|
+
},
|
|
61
|
+
impactArea: "Single-writer UOK metadata.",
|
|
62
|
+
transitionRisks: ["Writer token lifecycle regression"],
|
|
63
|
+
rollbackPlan: ["Disable writer sequence enrichment"],
|
|
64
|
+
linkedIssue: 123,
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const SWARM_NO_BLOCKERS: SwarmLanePRData = {
|
|
68
|
+
lane: {
|
|
69
|
+
id: "writer",
|
|
70
|
+
branch: "lane/single-writer",
|
|
71
|
+
owner: "@owner",
|
|
72
|
+
latestCommit: "abc1234",
|
|
73
|
+
changedContracts: ["WriterToken"],
|
|
74
|
+
testEvidence: ["npm run typecheck:extensions"],
|
|
75
|
+
},
|
|
76
|
+
impactArea: "Single-writer UOK metadata.",
|
|
77
|
+
transitionRisks: ["Writer token lifecycle regression"],
|
|
78
|
+
rollbackPlan: ["Disable writer sequence enrichment"],
|
|
79
|
+
linkedIssue: 123,
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
test("pr-evidence golden: commands-ship basic", () => {
|
|
83
|
+
compareGolden("commands-ship-basic.md", buildPrEvidence(SHIP_BASIC).body);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
test("pr-evidence golden: commands-ship empty optionals", () => {
|
|
87
|
+
compareGolden("commands-ship-empty-optionals.md", buildPrEvidence(SHIP_EMPTY_OPTIONALS).body);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
test("pr-evidence golden: swarm-lane with blockers", () => {
|
|
91
|
+
const body = formatSwarmLanePRBody(SWARM_WITH_BLOCKERS);
|
|
92
|
+
compareGolden("swarm-lane-with-blockers.md", body);
|
|
93
|
+
// Cross-check: top-level ## Blockers heading must appear (regression guard
|
|
94
|
+
// for the silent-drop fixed in this PR).
|
|
95
|
+
assert.ok(body.includes("\n## Blockers\n"), "swarm-lane body must emit a top-level ## Blockers heading");
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test("pr-evidence golden: swarm-lane no blockers (no Blockers heading)", () => {
|
|
99
|
+
const body = formatSwarmLanePRBody(SWARM_NO_BLOCKERS);
|
|
100
|
+
compareGolden("swarm-lane-no-blockers.md", body);
|
|
101
|
+
assert.ok(!body.includes("## Blockers"), "swarm-lane body without blockers must not emit ## Blockers heading");
|
|
102
|
+
});
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
// Project/App: GSD-2
|
|
2
|
+
// File Purpose: Hardening tests for buildPrEvidence — HTML-comment stripping, fake commit-trailer removal, and per-item length capping.
|
|
3
|
+
|
|
4
|
+
import test from "node:test";
|
|
5
|
+
import assert from "node:assert/strict";
|
|
6
|
+
|
|
7
|
+
import { buildPrEvidence, type PrEvidenceInput } from "../pr-evidence.ts";
|
|
8
|
+
|
|
9
|
+
test("pr-evidence hardening: strips HTML comments from summaries", () => {
|
|
10
|
+
const evidence = buildPrEvidence({
|
|
11
|
+
milestoneId: "M001",
|
|
12
|
+
summaries: ["visible<!-- hidden secret -->tail"],
|
|
13
|
+
});
|
|
14
|
+
assert.ok(!evidence.body.includes("<!--"), "raw <!-- must not appear");
|
|
15
|
+
assert.ok(!evidence.body.includes("hidden secret"), "comment contents must be stripped");
|
|
16
|
+
assert.ok(evidence.body.includes("visibletail"), "non-comment text must remain");
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test("pr-evidence hardening: removes Co-Authored-By trailer from why", () => {
|
|
20
|
+
const evidence = buildPrEvidence({
|
|
21
|
+
milestoneId: "M001",
|
|
22
|
+
why: "Real reason here.\nCo-Authored-By: Evil <e@evil.com>\nMore reason.",
|
|
23
|
+
});
|
|
24
|
+
assert.ok(!evidence.body.includes("Evil <e@evil.com>"));
|
|
25
|
+
assert.ok(!/Co-Authored-By:/i.test(evidence.body));
|
|
26
|
+
assert.ok(evidence.body.includes("Real reason here."));
|
|
27
|
+
assert.ok(evidence.body.includes("More reason."));
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test("pr-evidence hardening: removes Signed-off-by trailer from how", () => {
|
|
31
|
+
const evidence = buildPrEvidence({
|
|
32
|
+
milestoneId: "M001",
|
|
33
|
+
how: "Step one.\nSigned-off-by: Forged <f@x.com>\nStep two.",
|
|
34
|
+
});
|
|
35
|
+
assert.ok(!evidence.body.includes("Forged <f@x.com>"));
|
|
36
|
+
assert.ok(!/Signed-off-by:/i.test(evidence.body));
|
|
37
|
+
assert.ok(evidence.body.includes("Step one."));
|
|
38
|
+
assert.ok(evidence.body.includes("Step two."));
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test("pr-evidence hardening: trailer-name match is case-insensitive", () => {
|
|
42
|
+
const evidence = buildPrEvidence({
|
|
43
|
+
milestoneId: "M001",
|
|
44
|
+
why: "ok\nco-authored-by: lower <l@l.com>\nSIGNED-OFF-BY: upper <u@u.com>\nend",
|
|
45
|
+
});
|
|
46
|
+
assert.ok(!evidence.body.includes("lower <l@l.com>"));
|
|
47
|
+
assert.ok(!evidence.body.includes("upper <u@u.com>"));
|
|
48
|
+
assert.ok(evidence.body.includes("ok"));
|
|
49
|
+
assert.ok(evidence.body.includes("end"));
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test("pr-evidence hardening: caps oversize summaries item with truncation suffix", () => {
|
|
53
|
+
const big = "A".repeat(5 * 1024); // 5 KB
|
|
54
|
+
const evidence = buildPrEvidence({
|
|
55
|
+
milestoneId: "M001",
|
|
56
|
+
summaries: [big],
|
|
57
|
+
});
|
|
58
|
+
// Find the truncated A-block in the body and assert it is bounded.
|
|
59
|
+
const lines = evidence.body.split("\n");
|
|
60
|
+
const longLine = lines.find((l) => l.startsWith("AAAA"));
|
|
61
|
+
assert.ok(longLine, "expected truncated A-line in body");
|
|
62
|
+
assert.ok(longLine!.endsWith(" … [truncated]"), "must end with truncation suffix");
|
|
63
|
+
assert.ok(
|
|
64
|
+
Buffer.byteLength(longLine!, "utf8") <= 2048,
|
|
65
|
+
`truncated item must be within 2 KB cap, got ${Buffer.byteLength(longLine!, "utf8")}`,
|
|
66
|
+
);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test("pr-evidence hardening: HTML comment split across summary items is preserved literally", () => {
|
|
70
|
+
// Documented behavior: each item is sanitized independently. A comment
|
|
71
|
+
// that begins in one item and closes in the next is NOT joined, so the
|
|
72
|
+
// open/close markers remain as literal text. This is intentional — joining
|
|
73
|
+
// items before sanitizing would let an attacker straddle items to inject
|
|
74
|
+
// an aligned comment that hides the second item from rendered view.
|
|
75
|
+
const evidence = buildPrEvidence({
|
|
76
|
+
milestoneId: "M001",
|
|
77
|
+
summaries: ["first item ends <!--", "--> second item begins"],
|
|
78
|
+
});
|
|
79
|
+
// The literal markers survive because each item was sanitized alone.
|
|
80
|
+
assert.ok(evidence.body.includes("<!--"), "open marker preserved as literal");
|
|
81
|
+
assert.ok(evidence.body.includes("-->"), "close marker preserved as literal");
|
|
82
|
+
assert.ok(evidence.body.includes("first item ends"));
|
|
83
|
+
assert.ok(evidence.body.includes("second item begins"));
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
test("pr-evidence hardening: clean input is byte-identical to pre-hardening output", () => {
|
|
87
|
+
// This test is the contract that protects the golden fixtures: the
|
|
88
|
+
// sanitizer must be a true no-op for well-formed input. If this fails,
|
|
89
|
+
// there is a bug in the sanitizer (not in the goldens).
|
|
90
|
+
const cleanInput: PrEvidenceInput = {
|
|
91
|
+
milestoneId: "M001",
|
|
92
|
+
milestoneTitle: "Authentication",
|
|
93
|
+
changeType: "feat",
|
|
94
|
+
linkedIssue: "Closes #123",
|
|
95
|
+
summaries: ["### S01\nImplemented login flow."],
|
|
96
|
+
blockers: ["Awaiting design review"],
|
|
97
|
+
roadmapItems: ["- [x] **S01: Login**"],
|
|
98
|
+
metrics: ["**Units executed:** 3"],
|
|
99
|
+
testsRun: ["npm test", "npm run typecheck:extensions"],
|
|
100
|
+
why: "Users need to authenticate before accessing protected resources.",
|
|
101
|
+
how: "Added password hash check and session token issuance.",
|
|
102
|
+
rollbackNotes: ["Revert the merge commit."],
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
const expected = [
|
|
106
|
+
"## TL;DR",
|
|
107
|
+
"",
|
|
108
|
+
"**What:** Ship milestone M001 - Authentication",
|
|
109
|
+
"**Why:** Users need to authenticate before accessing protected resources.",
|
|
110
|
+
"**How:** Added password hash check and session token issuance.",
|
|
111
|
+
"",
|
|
112
|
+
"## What",
|
|
113
|
+
"",
|
|
114
|
+
"### S01\nImplemented login flow.",
|
|
115
|
+
"",
|
|
116
|
+
"## Blockers",
|
|
117
|
+
"",
|
|
118
|
+
"- Awaiting design review",
|
|
119
|
+
"",
|
|
120
|
+
"## Why",
|
|
121
|
+
"",
|
|
122
|
+
"Users need to authenticate before accessing protected resources.",
|
|
123
|
+
"",
|
|
124
|
+
"## How",
|
|
125
|
+
"",
|
|
126
|
+
"Added password hash check and session token issuance.",
|
|
127
|
+
"",
|
|
128
|
+
"## Linked Issue",
|
|
129
|
+
"",
|
|
130
|
+
"Closes #123",
|
|
131
|
+
"",
|
|
132
|
+
"## Roadmap",
|
|
133
|
+
"",
|
|
134
|
+
"- [x] **S01: Login**",
|
|
135
|
+
"",
|
|
136
|
+
"## Metrics",
|
|
137
|
+
"",
|
|
138
|
+
"- **Units executed:** 3",
|
|
139
|
+
"",
|
|
140
|
+
"## Tests Run",
|
|
141
|
+
"",
|
|
142
|
+
"- npm test",
|
|
143
|
+
"- npm run typecheck:extensions",
|
|
144
|
+
"",
|
|
145
|
+
"## Change Type",
|
|
146
|
+
"",
|
|
147
|
+
"- [x] `feat` - New feature or capability",
|
|
148
|
+
"- [ ] `fix` - Bug fix",
|
|
149
|
+
"- [ ] `refactor` - Code restructuring",
|
|
150
|
+
"- [ ] `test` - Adding or updating tests",
|
|
151
|
+
"- [ ] `docs` - Documentation only",
|
|
152
|
+
"- [ ] `chore` - Build, CI, or tooling changes",
|
|
153
|
+
"",
|
|
154
|
+
"## Rollback And Compatibility",
|
|
155
|
+
"",
|
|
156
|
+
"- Revert the merge commit.",
|
|
157
|
+
"",
|
|
158
|
+
"## AI Assistance Disclosure",
|
|
159
|
+
"",
|
|
160
|
+
"This PR was prepared with AI assistance.",
|
|
161
|
+
].join("\n");
|
|
162
|
+
|
|
163
|
+
const actual = buildPrEvidence(cleanInput).body;
|
|
164
|
+
assert.equal(actual, expected, "clean input must produce byte-identical output (sanitizer is no-op)");
|
|
165
|
+
});
|
|
@@ -24,7 +24,7 @@ describe("resource-loader import path", () => {
|
|
|
24
24
|
|
|
25
25
|
test("uses GSD_PKG_ROOT to resolve resource-loader from package root", () => {
|
|
26
26
|
// The fix uses GSD_PKG_ROOT (set by loader.ts) to construct an absolute
|
|
27
|
-
// file URL to dist/resource-
|
|
27
|
+
// file URL to dist/resource-loader.js — works in both source and deployed,
|
|
28
28
|
// and on Windows where raw paths fail with ERR_UNSUPPORTED_ESM_URL_SCHEME.
|
|
29
29
|
assert.ok(
|
|
30
30
|
autoSrc.includes('process.env.GSD_PKG_ROOT'),
|
|
@@ -118,7 +118,7 @@ test("guided flow checks pending deep setup before plan-v2 gate", () => {
|
|
|
118
118
|
const source = readFileSync(join(gsdDir, "guided-flow.ts"), "utf-8");
|
|
119
119
|
const showSmartEntryIdx = source.indexOf("export async function showSmartEntry");
|
|
120
120
|
assert.notEqual(showSmartEntryIdx, -1);
|
|
121
|
-
const deepIdx = source.indexOf("
|
|
121
|
+
const deepIdx = source.indexOf("shouldRunDeepProjectSetup(state, prefs, basePath)", showSmartEntryIdx);
|
|
122
122
|
const planIdx = source.indexOf("runPlanV2Gate(ctx, basePath, state)", showSmartEntryIdx);
|
|
123
123
|
assert.ok(
|
|
124
124
|
deepIdx > -1 && planIdx > -1 && deepIdx < planIdx,
|
|
@@ -20,7 +20,6 @@ import {
|
|
|
20
20
|
|
|
21
21
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
22
22
|
const gsdDir = join(__dirname, "..");
|
|
23
|
-
const WORKFLOW_MCP_TEST_TIMEOUT_MS = 90_000;
|
|
24
23
|
|
|
25
24
|
type ElicitPayload = {
|
|
26
25
|
message: string;
|
|
@@ -296,9 +295,9 @@ test("workflow MCP launch config reaches mutation tools over stdio", async () =>
|
|
|
296
295
|
});
|
|
297
296
|
|
|
298
297
|
try {
|
|
299
|
-
await client.connect(transport, { timeout:
|
|
298
|
+
await client.connect(transport, { timeout: 30_000 });
|
|
300
299
|
|
|
301
|
-
const tools = await client.listTools(undefined, { timeout:
|
|
300
|
+
const tools = await client.listTools(undefined, { timeout: 30_000 });
|
|
302
301
|
assert.ok(
|
|
303
302
|
(tools.tools ?? []).some((tool) => tool.name === "gsd_plan_slice"),
|
|
304
303
|
"expected workflow MCP surface to expose gsd_plan_slice",
|
|
@@ -326,7 +325,7 @@ test("workflow MCP launch config reaches mutation tools over stdio", async () =>
|
|
|
326
325
|
},
|
|
327
326
|
},
|
|
328
327
|
undefined,
|
|
329
|
-
{ timeout:
|
|
328
|
+
{ timeout: 30_000 },
|
|
330
329
|
);
|
|
331
330
|
assert.equal(askResult.isError, undefined);
|
|
332
331
|
assert.equal(
|
|
@@ -365,7 +364,7 @@ test("workflow MCP launch config reaches mutation tools over stdio", async () =>
|
|
|
365
364
|
},
|
|
366
365
|
},
|
|
367
366
|
undefined,
|
|
368
|
-
{ timeout:
|
|
367
|
+
{ timeout: 30_000 },
|
|
369
368
|
);
|
|
370
369
|
assert.equal(milestoneResult.isError, undefined);
|
|
371
370
|
assert.match(
|
|
@@ -396,7 +395,7 @@ test("workflow MCP launch config reaches mutation tools over stdio", async () =>
|
|
|
396
395
|
},
|
|
397
396
|
},
|
|
398
397
|
undefined,
|
|
399
|
-
{ timeout:
|
|
398
|
+
{ timeout: 30_000 },
|
|
400
399
|
);
|
|
401
400
|
assert.equal(sliceResult.isError, undefined);
|
|
402
401
|
assert.match(
|
|
@@ -467,7 +466,7 @@ test("workflow MCP ask_user_questions uses stdio elicitation round-trip", async
|
|
|
467
466
|
});
|
|
468
467
|
|
|
469
468
|
try {
|
|
470
|
-
await client.connect(transport, { timeout:
|
|
469
|
+
await client.connect(transport, { timeout: 30_000 });
|
|
471
470
|
|
|
472
471
|
const result = await client.callTool(
|
|
473
472
|
{
|
|
@@ -487,7 +486,7 @@ test("workflow MCP ask_user_questions uses stdio elicitation round-trip", async
|
|
|
487
486
|
},
|
|
488
487
|
},
|
|
489
488
|
undefined,
|
|
490
|
-
{ timeout:
|
|
489
|
+
{ timeout: 30_000 },
|
|
491
490
|
);
|
|
492
491
|
|
|
493
492
|
assert.ok(requestSeen, "expected stdio transport to forward an elicitation request");
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
// GSD-2 + src/resources/extensions/gsd/tests/working-output-messages.test.ts - Regression coverage for user-facing working-state message quality.
|
|
2
|
+
|
|
3
|
+
import test from "node:test";
|
|
4
|
+
import assert from "node:assert/strict";
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
evaluateWorkingOutputMessage,
|
|
8
|
+
evaluateWorkingOutputMessages,
|
|
9
|
+
formatAutoUnitWorkingMessage,
|
|
10
|
+
} from "../working-output-messages.ts";
|
|
11
|
+
|
|
12
|
+
test("auto unit loader messages name the active work", () => {
|
|
13
|
+
assert.equal(
|
|
14
|
+
formatAutoUnitWorkingMessage("research-milestone", "M001"),
|
|
15
|
+
"Researching M001: waiting for provider response",
|
|
16
|
+
);
|
|
17
|
+
assert.equal(
|
|
18
|
+
evaluateWorkingOutputMessage({
|
|
19
|
+
surface: "loader",
|
|
20
|
+
message: formatAutoUnitWorkingMessage("research-milestone", "M001"),
|
|
21
|
+
context: { unitType: "research-milestone", unitId: "M001", health: "waiting" },
|
|
22
|
+
}).length,
|
|
23
|
+
0,
|
|
24
|
+
);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test("generic working messages are flagged", () => {
|
|
28
|
+
const findings = evaluateWorkingOutputMessage({
|
|
29
|
+
surface: "loader",
|
|
30
|
+
message: "Working...",
|
|
31
|
+
context: { health: "waiting" },
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
assert.deepEqual(findings.map(f => f.code), ["generic-working-message"]);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test("dashboard messages cannot claim healthy progress while recovering", () => {
|
|
38
|
+
const findings = evaluateWorkingOutputMessage({
|
|
39
|
+
surface: "dashboard",
|
|
40
|
+
message: "GSD AUTO Progressing well",
|
|
41
|
+
context: { health: "recovering", recoveryAttempts: 1 },
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
assert.deepEqual(findings.map(f => f.code), ["misleading-healthy-message"]);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test("pre-roadmap zero-slice counters are flagged", () => {
|
|
48
|
+
const findings = evaluateWorkingOutputMessage({
|
|
49
|
+
surface: "dashboard",
|
|
50
|
+
message: "0/0 slices",
|
|
51
|
+
context: { health: "waiting" },
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
assert.deepEqual(findings.map(f => f.code), ["fake-zero-progress"]);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test("stalled/provider-error/timeout messages need an action", () => {
|
|
58
|
+
const noAction = evaluateWorkingOutputMessage({
|
|
59
|
+
surface: "notification",
|
|
60
|
+
message: "Provider stalled after 2m",
|
|
61
|
+
context: { health: "stalled" },
|
|
62
|
+
});
|
|
63
|
+
assert.deepEqual(noAction.map(f => f.code), ["missing-action"]);
|
|
64
|
+
|
|
65
|
+
const withAction = evaluateWorkingOutputMessage({
|
|
66
|
+
surface: "notification",
|
|
67
|
+
message: "Provider stalled after 2m. Type /gsd stop or wait for retry.",
|
|
68
|
+
context: { health: "stalled" },
|
|
69
|
+
});
|
|
70
|
+
assert.equal(withAction.length, 0);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test("current fixed research working surfaces pass the audit", () => {
|
|
74
|
+
const findings = evaluateWorkingOutputMessages([
|
|
75
|
+
{
|
|
76
|
+
surface: "loader",
|
|
77
|
+
message: "Researching M001: waiting for provider response",
|
|
78
|
+
context: { unitType: "research-milestone", unitId: "M001", health: "waiting" },
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
surface: "dashboard",
|
|
82
|
+
message: "Recovering\nretry 1 after idle stall",
|
|
83
|
+
context: { unitType: "research-milestone", unitId: "M001", health: "recovering", recoveryAttempts: 1 },
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
surface: "notification",
|
|
87
|
+
message: "Auto-mode stopped - User requested stop.",
|
|
88
|
+
context: { health: "stopped" },
|
|
89
|
+
},
|
|
90
|
+
]);
|
|
91
|
+
|
|
92
|
+
assert.equal(findings.length, 0);
|
|
93
|
+
});
|
|
@@ -184,7 +184,7 @@ describe("removeWorktree preserves submodule uncommitted state (#2337)", () => {
|
|
|
184
184
|
assert.ok(!existsSync(wt.path), "worktree directory should be removed");
|
|
185
185
|
|
|
186
186
|
// The code path that fires on dirty submodules emits one of these
|
|
187
|
-
// warnings.
|
|
187
|
+
// two warnings. Either indicates the detection ran — the ONLY
|
|
188
188
|
// observable that would fail if someone removed the
|
|
189
189
|
// `if (hasSubmoduleChanges)` branch and went straight to force
|
|
190
190
|
// removal. This is the assertion that #4823 demanded in place of
|