gsd-pi 2.80.0-dev.c5f2443b3 → 2.80.0-dev.f55d16d13
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/resources/.managed-resources-content-hash +1 -1
- package/dist/resources/GSD-WORKFLOW.md +2 -2
- package/dist/resources/extensions/github-sync/templates.js +39 -8
- package/dist/resources/extensions/gsd/auto/loop.js +16 -9
- package/dist/resources/extensions/gsd/auto/phases.js +37 -30
- 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-post-unit.js +10 -10
- package/dist/resources/extensions/gsd/auto-prompts.js +111 -1
- 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.js +9 -1
- 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/clean-root-preflight.js +42 -4
- package/dist/resources/extensions/gsd/commands-extract-learnings.js +17 -12
- package/dist/resources/extensions/gsd/custom-workflow-engine.js +22 -2
- 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/detection.js +106 -0
- package/dist/resources/extensions/gsd/graph.js +9 -3
- 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/prompts/complete-milestone.md +7 -8
- package/dist/resources/extensions/gsd/prompts/plan-milestone.md +3 -1
- package/dist/resources/extensions/gsd/safety/evidence-collector.js +10 -2
- package/dist/resources/extensions/gsd/working-output-messages.js +64 -0
- package/dist/resources/extensions/gsd/worktree-manager.js +16 -14
- package/dist/tsconfig.extensions.tsbuildinfo +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/onboarding/route.js +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 +3 -3
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
- package/dist/web/standalone/.next/server/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/package.json +10 -6
- package/packages/contracts/package.json +1 -1
- 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 +74 -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 +60 -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 +87 -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 +75 -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 +24 -3
- 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 +0 -1
- package/packages/pi-tui/dist/tui.d.ts.map +1 -1
- package/packages/pi-tui/dist/tui.js +3 -8
- 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 +29 -3
- 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 +3 -8
- 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/GSD-WORKFLOW.md +2 -2
- 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-deps.ts +1 -0
- package/src/resources/extensions/gsd/auto/loop.ts +17 -10
- package/src/resources/extensions/gsd/auto/phases.ts +42 -28
- 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-post-unit.ts +10 -10
- package/src/resources/extensions/gsd/auto-prompts.ts +116 -1
- 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.ts +12 -1
- 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/clean-root-preflight.ts +41 -3
- package/src/resources/extensions/gsd/commands-extract-learnings.ts +17 -12
- package/src/resources/extensions/gsd/custom-workflow-engine.ts +24 -1
- 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/detection.ts +128 -0
- package/src/resources/extensions/gsd/graph.ts +12 -5
- 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/prompts/complete-milestone.md +7 -8
- package/src/resources/extensions/gsd/prompts/plan-milestone.md +3 -1
- package/src/resources/extensions/gsd/safety/evidence-collector.ts +11 -2
- 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 +84 -5
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +170 -1
- package/src/resources/extensions/gsd/tests/clean-root-preflight.test.ts +88 -2
- package/src/resources/extensions/gsd/tests/commands-extract-learnings.test.ts +9 -0
- package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +112 -6
- package/src/resources/extensions/gsd/tests/custom-workflow-engine.test.ts +40 -2
- 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/detection.test.ts +140 -0
- 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/graph-operations.test.ts +10 -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/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/right-sized-workflow-prompts.test.ts +192 -0
- package/src/resources/extensions/gsd/tests/safety-harness-false-positives.test.ts +29 -0
- package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +46 -2
- package/src/resources/extensions/gsd/tests/uok-plan-v2-wiring.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/working-output-messages.test.ts +93 -0
- package/src/resources/extensions/gsd/tests/worktree-health-dispatch.test.ts +37 -6
- package/src/resources/extensions/gsd/tests/worktree-manager.test.ts +7 -0
- package/src/resources/extensions/gsd/tests/worktree-nested-git-safety.test.ts +9 -2
- 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/src/resources/extensions/gsd/worktree-manager.ts +15 -4
- package/packages/contracts/tsconfig.tsbuildinfo +0 -1
- /package/dist/web/standalone/.next/static/{bQDK5_LtkGVS64AirQgQG → mPZbi5BH9dwokaPZlrYuQ}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{bQDK5_LtkGVS64AirQgQG → mPZbi5BH9dwokaPZlrYuQ}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { execFileSync } from "node:child_process";
|
|
4
|
+
import { mkdtempSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import { tmpdir } from "node:os";
|
|
7
|
+
|
|
8
|
+
import { buildCompleteMilestonePrompt, buildPlanMilestonePrompt } from "../auto-prompts.ts";
|
|
9
|
+
|
|
10
|
+
function git(cwd: string, args: string[]): string {
|
|
11
|
+
return execFileSync("git", args, {
|
|
12
|
+
cwd,
|
|
13
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
14
|
+
encoding: "utf-8",
|
|
15
|
+
env: { ...process.env, GIT_AUTHOR_NAME: "Test User", GIT_AUTHOR_EMAIL: "test@example.com", GIT_COMMITTER_NAME: "Test User", GIT_COMMITTER_EMAIL: "test@example.com" },
|
|
16
|
+
}).trim();
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function makeRepo(files: Record<string, string>): string {
|
|
20
|
+
const base = mkdtempSync(join(tmpdir(), "gsd-right-size-"));
|
|
21
|
+
git(base, ["init", "-b", "main"]);
|
|
22
|
+
mkdirSync(join(base, ".gsd", "milestones", "M001"), { recursive: true });
|
|
23
|
+
writeFileSync(join(base, ".gsd", "milestones", "M001", "M001-CONTEXT.md"), "# Context\n\nTest milestone.");
|
|
24
|
+
for (const [path, content] of Object.entries(files)) {
|
|
25
|
+
const abs = join(base, path);
|
|
26
|
+
mkdirSync(join(abs, ".."), { recursive: true });
|
|
27
|
+
writeFileSync(abs, content);
|
|
28
|
+
}
|
|
29
|
+
git(base, ["add", "."]);
|
|
30
|
+
git(base, ["commit", "-m", "init"]);
|
|
31
|
+
return base;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function writeCompleteMilestoneFiles(base: string, validation: string): void {
|
|
35
|
+
const dir = join(base, ".gsd", "milestones", "M001");
|
|
36
|
+
mkdirSync(join(dir, "slices", "S01"), { recursive: true });
|
|
37
|
+
writeFileSync(join(dir, "M001-ROADMAP.md"), "# M001\n\n## Slices\n- [x] **S01: One** `risk:low` `depends:[]`\n > Done\n");
|
|
38
|
+
writeFileSync(join(dir, "M001-VALIDATION.md"), validation);
|
|
39
|
+
writeFileSync(join(dir, "slices", "S01", "S01-SUMMARY.md"), "# S01 Summary\n\n**Verification:** passed\n");
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function validationMetadata(): string {
|
|
43
|
+
return [
|
|
44
|
+
"validation_metadata:",
|
|
45
|
+
" covered_artifacts:",
|
|
46
|
+
" - `.gsd/milestones/M001/M001-VALIDATION.md`",
|
|
47
|
+
" - `.gsd/milestones/M001/M001-ROADMAP.md`",
|
|
48
|
+
" - `.gsd/milestones/M001/slices/S01/S01-SUMMARY.md`",
|
|
49
|
+
].join("\n");
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
test("plan-milestone prompt includes tiny untyped project classification and one-slice guidance", async () => {
|
|
53
|
+
const base = makeRepo({ "index.html": "<!doctype html>\n<title>Test</title>\n" });
|
|
54
|
+
try {
|
|
55
|
+
const prompt = await buildPlanMilestonePrompt("M001", "Polish static page", base, "minimal");
|
|
56
|
+
assert.match(prompt, /\*\*Kind:\*\* untyped-existing/);
|
|
57
|
+
assert.match(prompt, /\*\*Content files:\*\* 1/);
|
|
58
|
+
assert.match(prompt, /`index\.html`/);
|
|
59
|
+
assert.match(prompt, /Prefer exactly one slice/);
|
|
60
|
+
} finally {
|
|
61
|
+
rmSync(base, { recursive: true, force: true });
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test("plan-milestone prompt includes small untyped project 1-2 slice guidance", async () => {
|
|
66
|
+
const base = makeRepo({
|
|
67
|
+
"index.html": "html",
|
|
68
|
+
"README.md": "readme",
|
|
69
|
+
"styles.css": "body {}",
|
|
70
|
+
});
|
|
71
|
+
try {
|
|
72
|
+
const prompt = await buildPlanMilestonePrompt("M001", "Polish static files", base, "minimal");
|
|
73
|
+
assert.match(prompt, /\*\*Kind:\*\* untyped-existing/);
|
|
74
|
+
assert.match(prompt, /\*\*Content files:\*\* 3/);
|
|
75
|
+
assert.match(prompt, /Prefer 1-2 slices/);
|
|
76
|
+
} finally {
|
|
77
|
+
rmSync(base, { recursive: true, force: true });
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test("plan-milestone prompt keeps normal guidance for typed projects", async () => {
|
|
82
|
+
const base = makeRepo({
|
|
83
|
+
"package.json": "{\"scripts\":{\"test\":\"node --test\"}}\n",
|
|
84
|
+
"src/index.js": "console.log('ok');\n",
|
|
85
|
+
});
|
|
86
|
+
try {
|
|
87
|
+
const prompt = await buildPlanMilestonePrompt("M001", "Update app", base, "minimal");
|
|
88
|
+
assert.match(prompt, /\*\*Kind:\*\* typed-existing/);
|
|
89
|
+
assert.match(prompt, /Use normal ecosystem-aware planning guidance/);
|
|
90
|
+
assert.doesNotMatch(prompt, /Prefer exactly one slice/);
|
|
91
|
+
} finally {
|
|
92
|
+
rmSync(base, { recursive: true, force: true });
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
test("workflow docs no longer contain blanket 4-10 slice guidance", () => {
|
|
97
|
+
const docs = readFileSync(join(process.cwd(), "src", "resources", "GSD-WORKFLOW.md"), "utf-8");
|
|
98
|
+
assert.doesNotMatch(docs, /4-10 slices/);
|
|
99
|
+
assert.match(docs, /1-10 slices/);
|
|
100
|
+
assert.match(docs, /single-file/);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test("prompt templates carry right-sized planning and closeout mode guidance", () => {
|
|
104
|
+
const planTemplate = readFileSync(join(process.cwd(), "src", "resources", "extensions", "gsd", "prompts", "plan-milestone.md"), "utf-8");
|
|
105
|
+
const completeTemplate = readFileSync(join(process.cwd(), "src", "resources", "extensions", "gsd", "prompts", "complete-milestone.md"), "utf-8");
|
|
106
|
+
|
|
107
|
+
assert.match(planTemplate, /Use 1-10 slices, sized to the work/);
|
|
108
|
+
assert.match(planTemplate, /tiny\/single-file\/static work should usually be one slice/);
|
|
109
|
+
assert.match(planTemplate, /untyped-existing/);
|
|
110
|
+
assert.match(completeTemplate, /Closeout Review Mode/);
|
|
111
|
+
assert.match(completeTemplate, /passing validation artifact is present/);
|
|
112
|
+
assert.doesNotMatch(completeTemplate, /^### Delegate Review Work/m);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
test("complete-milestone prompt trusts passing validation artifact", async () => {
|
|
116
|
+
const base = makeRepo({ "index.html": "<!doctype html>\n<title>Test</title>\n" });
|
|
117
|
+
try {
|
|
118
|
+
writeCompleteMilestoneFiles(base, `---\nverdict: pass\nremediation_round: 0\n---\n\n# Validation\n${validationMetadata()}\n\nAll checks passed.`);
|
|
119
|
+
const prompt = await buildCompleteMilestonePrompt("M001", "Polish static page", base, "minimal");
|
|
120
|
+
assert.match(prompt, /Passing Validation Artifact/);
|
|
121
|
+
assert.match(prompt, /Treat it as authoritative/);
|
|
122
|
+
assert.match(prompt, /Do not delegate fresh reviewer\/security\/tester audits/);
|
|
123
|
+
assert.match(prompt, /All checks passed/);
|
|
124
|
+
} finally {
|
|
125
|
+
rmSync(base, { recursive: true, force: true });
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
test("complete-milestone prompt trusts centralized markdown body pass verdict", async () => {
|
|
130
|
+
const base = makeRepo({ "index.html": "<!doctype html>\n<title>Test</title>\n" });
|
|
131
|
+
try {
|
|
132
|
+
writeCompleteMilestoneFiles(base, `# Validation\n\n**Verdict:** PASS\n\n${validationMetadata()}\n\nAll checks passed.`);
|
|
133
|
+
const prompt = await buildCompleteMilestonePrompt("M001", "Polish static page", base, "minimal");
|
|
134
|
+
assert.match(prompt, /Passing Validation Artifact/);
|
|
135
|
+
assert.match(prompt, /Treat it as authoritative/);
|
|
136
|
+
assert.match(prompt, /Do not delegate fresh reviewer\/security\/tester audits/);
|
|
137
|
+
} finally {
|
|
138
|
+
rmSync(base, { recursive: true, force: true });
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
test("complete-milestone prompt does not trust stale pass validation without metadata", async () => {
|
|
143
|
+
const base = makeRepo({ "index.html": "<!doctype html>\n<title>Test</title>\n" });
|
|
144
|
+
try {
|
|
145
|
+
writeCompleteMilestoneFiles(base, "---\nverdict: pass\nremediation_round: 0\n---\n\n# Validation\nAll checks passed.");
|
|
146
|
+
const prompt = await buildCompleteMilestonePrompt("M001", "Polish static page", base, "minimal");
|
|
147
|
+
assert.match(prompt, /Validation Requires Attention/);
|
|
148
|
+
assert.match(prompt, /missing freshness metadata/);
|
|
149
|
+
assert.doesNotMatch(prompt, /Passing Validation Artifact/);
|
|
150
|
+
} finally {
|
|
151
|
+
rmSync(base, { recursive: true, force: true });
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
test("complete-milestone prompt does not trust pass validation missing current summary coverage", async () => {
|
|
156
|
+
const base = makeRepo({ "index.html": "<!doctype html>\n<title>Test</title>\n" });
|
|
157
|
+
try {
|
|
158
|
+
writeCompleteMilestoneFiles(base, [
|
|
159
|
+
"---",
|
|
160
|
+
"verdict: pass",
|
|
161
|
+
"remediation_round: 0",
|
|
162
|
+
"---",
|
|
163
|
+
"",
|
|
164
|
+
"# Validation",
|
|
165
|
+
"validation_metadata:",
|
|
166
|
+
" covered_artifacts:",
|
|
167
|
+
" - `.gsd/milestones/M001/M001-VALIDATION.md`",
|
|
168
|
+
" - `.gsd/milestones/M001/M001-ROADMAP.md`",
|
|
169
|
+
"",
|
|
170
|
+
"All checks passed.",
|
|
171
|
+
].join("\n"));
|
|
172
|
+
const prompt = await buildCompleteMilestonePrompt("M001", "Polish static page", base, "minimal");
|
|
173
|
+
assert.match(prompt, /Validation Requires Attention/);
|
|
174
|
+
assert.match(prompt, /does not cover current milestone artifacts/);
|
|
175
|
+
assert.doesNotMatch(prompt, /Passing Validation Artifact/);
|
|
176
|
+
} finally {
|
|
177
|
+
rmSync(base, { recursive: true, force: true });
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
test("complete-milestone prompt keeps deeper review path without passing validation", async () => {
|
|
182
|
+
const base = makeRepo({ "index.html": "<!doctype html>\n<title>Test</title>\n" });
|
|
183
|
+
try {
|
|
184
|
+
writeCompleteMilestoneFiles(base, "---\nverdict: needs-attention\nremediation_round: 0\n---\n\n# Validation\nFix gaps.");
|
|
185
|
+
const prompt = await buildCompleteMilestonePrompt("M001", "Polish static page", base, "minimal");
|
|
186
|
+
assert.match(prompt, /Validation Requires Attention/);
|
|
187
|
+
assert.match(prompt, /verdict `needs-attention`/);
|
|
188
|
+
assert.match(prompt, /Use `subagent` for review work needing fresh context/i);
|
|
189
|
+
} finally {
|
|
190
|
+
rmSync(base, { recursive: true, force: true });
|
|
191
|
+
}
|
|
192
|
+
});
|
|
@@ -144,6 +144,18 @@ test("safety-harness-bug2-race: bash evidence survives mid-unit reset between to
|
|
|
144
144
|
assert.ok(bash[0].outputSnippet.includes("found"), "output snippet captured");
|
|
145
145
|
});
|
|
146
146
|
|
|
147
|
+
test("safety-harness: gsd_exec counts as execution evidence", () => {
|
|
148
|
+
resetEvidence();
|
|
149
|
+
|
|
150
|
+
recordToolCall("tc-exec-1", "gsd_exec", { command: "grep -n render index.html" });
|
|
151
|
+
recordToolResult("tc-exec-1", "gsd_exec", "Command exited with code 0\n1:render\n", false);
|
|
152
|
+
|
|
153
|
+
const bash = getEvidence().filter((e): e is BashEvidence => e.kind === "bash");
|
|
154
|
+
assert.equal(bash.length, 1, "gsd_exec must be tracked as execution evidence");
|
|
155
|
+
assert.equal(bash[0].command, "grep -n render index.html");
|
|
156
|
+
assert.equal(bash[0].exitCode, 0);
|
|
157
|
+
});
|
|
158
|
+
|
|
147
159
|
// ─── Bug 3: git diff HEAD~1 scope check ─────────────────────────────────────
|
|
148
160
|
|
|
149
161
|
test("safety-harness-bug3: validateFileChanges works on initial commit (no HEAD~1)", (t) => {
|
|
@@ -237,3 +249,20 @@ test("safety-harness-bug3: validateFileChanges works on merge commit", (t) => {
|
|
|
237
249
|
// Must produce a valid result without throwing
|
|
238
250
|
assert.ok(audit !== null, "audit must be produced for merge commit repo");
|
|
239
251
|
});
|
|
252
|
+
|
|
253
|
+
test("safety-harness: planned changed file avoids unexpected-file warning", (t) => {
|
|
254
|
+
const base = mkdtempSync(join(tmpdir(), "gsd-planned-file-"));
|
|
255
|
+
t.after(() => rmSync(base, { recursive: true, force: true }));
|
|
256
|
+
|
|
257
|
+
execFileSync("git", ["init"], { cwd: base });
|
|
258
|
+
execFileSync("git", ["config", "user.email", "test@example.com"], { cwd: base });
|
|
259
|
+
execFileSync("git", ["config", "user.name", "Test User"], { cwd: base });
|
|
260
|
+
writeFileSync(join(base, "index.html"), "<main></main>\n");
|
|
261
|
+
execFileSync("git", ["add", "index.html"], { cwd: base });
|
|
262
|
+
execFileSync("git", ["commit", "-m", "add static app"], { cwd: base });
|
|
263
|
+
|
|
264
|
+
const audit = validateFileChanges(base, [], ["index.html"]);
|
|
265
|
+
assert.ok(audit !== null, "audit must be produced");
|
|
266
|
+
assert.deepEqual(audit!.unexpectedFiles, [], "planned index.html must not be unexpected");
|
|
267
|
+
assert.deepEqual(audit!.missingFiles, [], "planned index.html must not be missing");
|
|
268
|
+
});
|
|
@@ -3,6 +3,8 @@ import assert from "node:assert/strict";
|
|
|
3
3
|
import { readFileSync } from "node:fs";
|
|
4
4
|
import { resolve } from "node:path";
|
|
5
5
|
|
|
6
|
+
import { _withDetachedAutoKeepaliveForTest } from "../auto.ts";
|
|
7
|
+
|
|
6
8
|
const gsdDir = resolve(import.meta.dirname, "..");
|
|
7
9
|
|
|
8
10
|
function readGsdFile(relativePath: string): string {
|
|
@@ -102,8 +104,8 @@ test("startAutoDetached reports failures asynchronously (#3733)", () => {
|
|
|
102
104
|
"auto.ts should export startAutoDetached",
|
|
103
105
|
);
|
|
104
106
|
assert.ok(
|
|
105
|
-
autoSrc.includes("void startAuto(ctx, pi, base, verboseMode, options).catch"),
|
|
106
|
-
"startAutoDetached should launch startAuto without awaiting it",
|
|
107
|
+
autoSrc.includes("void withDetachedAutoKeepalive(startAuto(ctx, pi, base, verboseMode, options)).catch"),
|
|
108
|
+
"startAutoDetached should launch startAuto without awaiting it and keep the process alive",
|
|
107
109
|
);
|
|
108
110
|
assert.ok(
|
|
109
111
|
autoSrc.includes("ctx.ui.notify(`Auto-start failed: ${message}`, \"error\")"),
|
|
@@ -111,6 +113,48 @@ test("startAutoDetached reports failures asynchronously (#3733)", () => {
|
|
|
111
113
|
);
|
|
112
114
|
});
|
|
113
115
|
|
|
116
|
+
test("detached auto-start keeps a ref'ed handle until the run settles", async () => {
|
|
117
|
+
const originalSetInterval = globalThis.setInterval;
|
|
118
|
+
const originalClearInterval = globalThis.clearInterval;
|
|
119
|
+
let intervalCreated = false;
|
|
120
|
+
let intervalCleared = false;
|
|
121
|
+
let createdHandle: NodeJS.Timeout | undefined;
|
|
122
|
+
let resolveRun!: () => void;
|
|
123
|
+
const run = new Promise<void>((resolve) => {
|
|
124
|
+
resolveRun = resolve;
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
globalThis.setInterval = ((handler: TimerHandler, timeout?: number, ...args: unknown[]) => {
|
|
128
|
+
intervalCreated = true;
|
|
129
|
+
assert.equal(timeout, 30_000);
|
|
130
|
+
void handler;
|
|
131
|
+
void args;
|
|
132
|
+
createdHandle = originalSetInterval(() => {}, 1_000_000);
|
|
133
|
+
assert.equal(createdHandle.hasRef(), true, "detached auto keepalive must be ref'ed");
|
|
134
|
+
return createdHandle;
|
|
135
|
+
}) as unknown as typeof setInterval;
|
|
136
|
+
|
|
137
|
+
globalThis.clearInterval = ((handle?: NodeJS.Timeout | number | string) => {
|
|
138
|
+
if (handle === createdHandle) intervalCleared = true;
|
|
139
|
+
return originalClearInterval(handle);
|
|
140
|
+
}) as unknown as typeof clearInterval;
|
|
141
|
+
|
|
142
|
+
try {
|
|
143
|
+
const heldRun = _withDetachedAutoKeepaliveForTest(run);
|
|
144
|
+
assert.equal(intervalCreated, true, "keepalive interval should start immediately");
|
|
145
|
+
assert.equal(intervalCleared, false, "keepalive should remain active while auto-mode is running");
|
|
146
|
+
|
|
147
|
+
resolveRun();
|
|
148
|
+
await heldRun;
|
|
149
|
+
|
|
150
|
+
assert.equal(intervalCleared, true, "keepalive interval should clear when auto-mode settles");
|
|
151
|
+
} finally {
|
|
152
|
+
if (createdHandle) originalClearInterval(createdHandle);
|
|
153
|
+
globalThis.setInterval = originalSetInterval;
|
|
154
|
+
globalThis.clearInterval = originalClearInterval;
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
|
|
114
158
|
test("detached auto-start preserves milestone lock across pause/stop cleanup (#3733)", () => {
|
|
115
159
|
const autoSrc = readGsdFile("auto.ts");
|
|
116
160
|
const sessionSrc = readGsdFile("auto/session.ts");
|
|
@@ -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,
|
|
@@ -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
|
+
});
|
|
@@ -9,12 +9,12 @@
|
|
|
9
9
|
|
|
10
10
|
import { describe, test, beforeEach, afterEach } from "node:test";
|
|
11
11
|
import assert from "node:assert/strict";
|
|
12
|
-
import { mkdtempSync, mkdirSync, writeFileSync, rmSync, readdirSync } from "node:fs";
|
|
12
|
+
import { existsSync, mkdtempSync, mkdirSync, readFileSync, writeFileSync, rmSync, readdirSync } from "node:fs";
|
|
13
13
|
import { join } from "node:path";
|
|
14
14
|
import { tmpdir } from "node:os";
|
|
15
15
|
import { execSync } from "node:child_process";
|
|
16
16
|
|
|
17
|
-
import { PROJECT_FILES } from "../detection.js";
|
|
17
|
+
import { PROJECT_FILES, classifyProject } from "../detection.js";
|
|
18
18
|
|
|
19
19
|
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
20
20
|
|
|
@@ -30,6 +30,14 @@ function createGitRepo(): string {
|
|
|
30
30
|
return dir;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
function createEmptyGitRepo(): string {
|
|
34
|
+
const dir = mkdtempSync(join(tmpdir(), "wt-dispatch-test-empty-"));
|
|
35
|
+
execSync("git init", { cwd: dir, stdio: "ignore" });
|
|
36
|
+
execSync("git config user.email test@test.com", { cwd: dir, stdio: "ignore" });
|
|
37
|
+
execSync("git config user.name Test", { cwd: dir, stdio: "ignore" });
|
|
38
|
+
return dir;
|
|
39
|
+
}
|
|
40
|
+
|
|
33
41
|
/**
|
|
34
42
|
* Simulate the health check logic from auto/phases.ts.
|
|
35
43
|
*
|
|
@@ -64,8 +72,6 @@ function hasXcodeBundle(basePath: string): boolean {
|
|
|
64
72
|
} catch { return false; }
|
|
65
73
|
}
|
|
66
74
|
|
|
67
|
-
import { existsSync } from "node:fs";
|
|
68
|
-
|
|
69
75
|
// ─── Tests ───────────────────────────────────────────────────────────────────
|
|
70
76
|
|
|
71
77
|
test("PROJECT_FILES is exported and contains expected multi-ecosystem entries", () => {
|
|
@@ -80,6 +86,21 @@ test("PROJECT_FILES is exported and contains expected multi-ecosystem entries",
|
|
|
80
86
|
assert.ok(PROJECT_FILES.includes("Package.swift"), "includes Swift marker");
|
|
81
87
|
});
|
|
82
88
|
|
|
89
|
+
test("runUnitPhase fails closed when classification returns invalid-repo", () => {
|
|
90
|
+
const source = readFileSync(join(process.cwd(), "src/resources/extensions/gsd/auto/phases.ts"), "utf-8");
|
|
91
|
+
const invalidRepoBranch = source.slice(
|
|
92
|
+
source.indexOf('projectClassification.kind === "invalid-repo"'),
|
|
93
|
+
source.indexOf('projectClassification.kind === "greenfield"'),
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
assert.match(invalidRepoBranch, /projectClassification\.reason === "missing \.git" && hasGit/);
|
|
97
|
+
assert.match(invalidRepoBranch, /project classification could not confirm \.git/);
|
|
98
|
+
assert.match(invalidRepoBranch, /ctx\.ui\.notify\(msg,\s*"error"\)/);
|
|
99
|
+
assert.match(invalidRepoBranch, /await deps\.stopAuto\(ctx,\s*pi,\s*msg\)/);
|
|
100
|
+
assert.match(invalidRepoBranch, /return \{ action: "break", reason: "worktree-invalid" \}/);
|
|
101
|
+
assert.match(invalidRepoBranch, /classified as invalid-repo/);
|
|
102
|
+
});
|
|
103
|
+
|
|
83
104
|
describe("health check with git repo", () => {
|
|
84
105
|
let dir: string;
|
|
85
106
|
beforeEach(() => { dir = createGitRepo(); });
|
|
@@ -132,8 +153,18 @@ describe("health check with git repo", () => {
|
|
|
132
153
|
});
|
|
133
154
|
|
|
134
155
|
test("health check passes for empty git repo (greenfield project)", () => {
|
|
135
|
-
|
|
136
|
-
|
|
156
|
+
const empty = createEmptyGitRepo();
|
|
157
|
+
try {
|
|
158
|
+
assert.ok(wouldPassHealthCheck(empty, existsSync), "empty git repo should pass health check (greenfield)");
|
|
159
|
+
assert.equal(classifyProject(empty).kind, "greenfield");
|
|
160
|
+
} finally {
|
|
161
|
+
rmSync(empty, { recursive: true, force: true });
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
test("health check classifies README-only repo as untyped existing, not greenfield", () => {
|
|
166
|
+
assert.ok(wouldPassHealthCheck(dir, existsSync), "README-only repo should pass health check");
|
|
167
|
+
assert.equal(classifyProject(dir).kind, "untyped-existing");
|
|
137
168
|
});
|
|
138
169
|
});
|
|
139
170
|
|
|
@@ -235,4 +235,11 @@ describe("removeWorktree — missing worktree", () => {
|
|
|
235
235
|
"should not throw when worktree does not exist",
|
|
236
236
|
);
|
|
237
237
|
});
|
|
238
|
+
|
|
239
|
+
test("deleteBranch is quiet when the branch is already gone", () => {
|
|
240
|
+
assert.doesNotThrow(
|
|
241
|
+
() => removeWorktree(base, "nonexistent", { deleteBranch: true }),
|
|
242
|
+
"missing branch should be treated as already cleaned up",
|
|
243
|
+
);
|
|
244
|
+
});
|
|
238
245
|
});
|
|
@@ -56,11 +56,11 @@ test("#2616: findNestedGitDirs ignores .git files (worktree pointers)", (t) => {
|
|
|
56
56
|
);
|
|
57
57
|
});
|
|
58
58
|
|
|
59
|
-
test("#2616: findNestedGitDirs skips excluded directories (node_modules, .gsd, target)", (t) => {
|
|
59
|
+
test("#2616: findNestedGitDirs skips excluded directories (node_modules, .gsd, .bg-shell, target)", (t) => {
|
|
60
60
|
const root = makeRoot(t);
|
|
61
61
|
|
|
62
62
|
// All three of these contain a .git *directory*, but the scan must skip them.
|
|
63
|
-
for (const excluded of ["node_modules", ".gsd", "target"]) {
|
|
63
|
+
for (const excluded of ["node_modules", ".gsd", ".bg-shell", "target"]) {
|
|
64
64
|
const inside = join(root, excluded, "vendored-pkg");
|
|
65
65
|
mkdirSync(join(inside, ".git"), { recursive: true });
|
|
66
66
|
}
|
|
@@ -73,6 +73,13 @@ test("#2616: findNestedGitDirs skips excluded directories (node_modules, .gsd, t
|
|
|
73
73
|
);
|
|
74
74
|
});
|
|
75
75
|
|
|
76
|
+
test("#2616: findNestedGitDirs ignores normal missing child .git probes", (t) => {
|
|
77
|
+
const root = makeRoot(t);
|
|
78
|
+
mkdirSync(join(root, "plain-dir"), { recursive: true });
|
|
79
|
+
|
|
80
|
+
assert.deepEqual(findNestedGitDirs(root), []);
|
|
81
|
+
});
|
|
82
|
+
|
|
76
83
|
test("#2616: findNestedGitDirs finds deeply nested repos", (t) => {
|
|
77
84
|
const root = makeRoot(t);
|
|
78
85
|
const deep = join(root, "a", "b", "c", "scaffolded");
|