gsd-pi 2.64.0-dev.f8aad9b → 2.65.0-dev.16e10d7
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/headless.js +3 -1
- package/dist/mcp-server.js +6 -2
- package/dist/resources/extensions/bg-shell/bg-shell-lifecycle.js +22 -7
- package/dist/resources/extensions/bg-shell/process-manager.js +6 -1
- package/dist/resources/extensions/browser-tools/capture.js +20 -1
- package/dist/resources/extensions/browser-tools/tests/capture-sharp-optional.test.cjs +93 -0
- package/dist/resources/extensions/gsd/auto/run-unit.js +13 -2
- package/dist/resources/extensions/gsd/auto-dispatch.js +94 -8
- package/dist/resources/extensions/gsd/auto-model-selection.js +7 -5
- package/dist/resources/extensions/gsd/auto-post-unit.js +115 -7
- package/dist/resources/extensions/gsd/auto-prompts.js +24 -0
- package/dist/resources/extensions/gsd/auto-recovery.js +12 -8
- package/dist/resources/extensions/gsd/auto-start.js +35 -1
- package/dist/resources/extensions/gsd/auto-tool-tracking.js +10 -0
- package/dist/resources/extensions/gsd/auto-verification.js +138 -1
- package/dist/resources/extensions/gsd/auto-worktree.js +29 -7
- package/dist/resources/extensions/gsd/auto.js +2 -2
- package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +17 -4
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +34 -13
- package/dist/resources/extensions/gsd/bootstrap/notify-interceptor.js +28 -0
- package/dist/resources/extensions/gsd/bootstrap/query-tools.js +6 -4
- package/dist/resources/extensions/gsd/bootstrap/register-extension.js +5 -1
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +12 -1
- package/dist/resources/extensions/gsd/bootstrap/register-shortcuts.js +16 -0
- package/dist/resources/extensions/gsd/bootstrap/system-context.js +20 -0
- package/dist/resources/extensions/gsd/commands/catalog.js +7 -1
- package/dist/resources/extensions/gsd/commands/context.js +8 -1
- package/dist/resources/extensions/gsd/commands/handlers/core.js +21 -0
- package/dist/resources/extensions/gsd/commands/handlers/notifications-handler.js +104 -0
- package/dist/resources/extensions/gsd/commands/handlers/ops.js +5 -0
- package/dist/resources/extensions/gsd/commands-extensions.js +1 -1
- package/dist/resources/extensions/gsd/config-overlay.js +312 -0
- package/dist/resources/extensions/gsd/detection.js +1 -1
- package/dist/resources/extensions/gsd/dispatch-guard.js +2 -1
- package/dist/resources/extensions/gsd/docs/preferences-reference.md +1 -0
- package/dist/resources/extensions/gsd/doctor.js +2 -1
- package/dist/resources/extensions/gsd/gitignore.js +1 -0
- package/dist/resources/extensions/gsd/gsd-db.js +11 -2
- package/dist/resources/extensions/gsd/guided-flow.js +220 -29
- package/dist/resources/extensions/gsd/md-importer.js +14 -7
- package/dist/resources/extensions/gsd/notification-overlay.js +256 -0
- package/dist/resources/extensions/gsd/notification-store.js +273 -0
- package/dist/resources/extensions/gsd/notification-widget.js +56 -0
- package/dist/resources/extensions/gsd/parallel-orchestrator.js +17 -11
- package/dist/resources/extensions/gsd/post-execution-checks.js +407 -0
- package/dist/resources/extensions/gsd/pre-execution-checks.js +471 -0
- package/dist/resources/extensions/gsd/preferences-types.js +7 -0
- package/dist/resources/extensions/gsd/preferences-validation.js +78 -1
- package/dist/resources/extensions/gsd/preferences.js +13 -2
- package/dist/resources/extensions/gsd/preparation.js +1092 -0
- package/dist/resources/extensions/gsd/prompt-validation.js +67 -0
- package/dist/resources/extensions/gsd/prompts/complete-milestone.md +3 -3
- package/dist/resources/extensions/gsd/prompts/complete-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/discuss-prepared.md +424 -0
- package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +4 -1
- package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +5 -4
- package/dist/resources/extensions/gsd/prompts/parallel-research-slices.md +23 -0
- package/dist/resources/extensions/gsd/prompts/rethink.md +2 -1
- package/dist/resources/extensions/gsd/prompts/validate-milestone.md +56 -23
- package/dist/resources/extensions/gsd/quick.js +19 -15
- package/dist/resources/extensions/gsd/reactive-graph.js +12 -0
- package/dist/resources/extensions/gsd/roadmap-slices.js +24 -5
- package/dist/resources/extensions/gsd/safety/content-validator.js +3 -3
- package/dist/resources/extensions/gsd/session-lock.js +23 -1
- package/dist/resources/extensions/gsd/state.js +100 -12
- package/dist/resources/extensions/gsd/templates/context-enhanced.md +138 -0
- package/dist/resources/extensions/gsd/tools/complete-slice.js +12 -3
- package/dist/resources/extensions/gsd/tools/complete-task.js +16 -4
- package/dist/resources/extensions/gsd/tools/plan-milestone.js +7 -5
- package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +5 -2
- package/dist/resources/extensions/gsd/tools/reopen-milestone.js +119 -0
- package/dist/resources/extensions/gsd/tools/reopen-slice.js +30 -0
- package/dist/resources/extensions/gsd/tools/reopen-task.js +18 -0
- package/dist/resources/extensions/gsd/triage-resolution.js +25 -9
- package/dist/resources/extensions/gsd/verification-evidence.js +18 -0
- package/dist/resources/extensions/gsd/workflow-logger.js +8 -0
- package/dist/resources/extensions/gsd/workflow-projections.js +4 -7
- package/dist/resources/extensions/gsd/workflow-reconcile.js +2 -4
- package/dist/resources/extensions/gsd/workflow-templates.js +11 -2
- package/dist/resources/extensions/gsd/worktree-manager.js +5 -2
- package/dist/resources/extensions/gsd/worktree.js +9 -0
- package/dist/resources/extensions/shared/interview-ui.js +1 -1
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +18 -17
- 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/routes-manifest.json +6 -0
- package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.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/notifications/route.js +3 -0
- package/dist/web/standalone/.next/server/app/api/notifications/route.js.nft.json +1 -0
- package/dist/web/standalone/.next/server/app/api/notifications/route_client-reference-manifest.js +1 -0
- 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 +18 -17
- package/dist/web/standalone/.next/server/functions-config-manifest.json +1 -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 +2 -2
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/dist/web/standalone/.next/static/Z3TgDP0c7kG9j8CVQVGcl/_buildManifest.js +1 -0
- package/dist/web/standalone/.next/static/chunks/6502.8874bcae249c02e1.js +9 -0
- package/dist/web/standalone/.next/static/chunks/app/_global-error/page-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/boot/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/bridge-terminal/input/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/bridge-terminal/resize/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/bridge-terminal/stream/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/browse-directories/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/captures/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/cleanup/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/dev-mode/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/doctor/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/experimental/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/export-data/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/files/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/forensics/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/git/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/history/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/hooks/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/inspect/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/knowledge/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/live-state/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/notifications/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/onboarding/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/preferences/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/projects/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/recovery/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/remote-questions/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/session/browser/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/session/command/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/session/events/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/session/manage/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/settings-data/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/shutdown/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/skill-health/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/steer/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/switch-root/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/terminal/input/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/terminal/resize/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/terminal/sessions/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/terminal/stream/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/terminal/upload/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/undo/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/update/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/visualizer/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/app-error-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/forbidden-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/not-found-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/unauthorized-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/{webpack-a1c1e452c6b32d04.js → webpack-9fed74684e1c5bb1.js} +1 -1
- package/package.json +1 -1
- package/packages/pi-agent-core/dist/agent-loop.js +26 -9
- package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
- package/packages/pi-agent-core/src/agent-loop.test.ts +100 -4
- package/packages/pi-agent-core/src/agent-loop.ts +43 -12
- package/packages/pi-coding-agent/dist/core/agent-session-tool-refresh.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/agent-session-tool-refresh.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/agent-session-tool-refresh.test.js +38 -0
- package/packages/pi-coding-agent/dist/core/agent-session-tool-refresh.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.js +11 -0
- package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/resource-loader-cache-reset.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/resource-loader-cache-reset.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/resource-loader-cache-reset.test.js +24 -0
- package/packages/pi-coding-agent/dist/core/resource-loader-cache-reset.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/resource-loader.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/resource-loader.js +4 -1
- package/packages/pi-coding-agent/dist/core/resource-loader.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/retry-handler.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/retry-handler.js +30 -19
- package/packages/pi-coding-agent/dist/core/retry-handler.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/retry-handler.test.js +51 -0
- package/packages/pi-coding-agent/dist/core/retry-handler.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/sdk.js +9 -9
- package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.d.ts +2 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.js +10 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts +2 -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 +28 -5
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js +15 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js +18 -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.d.ts +6 -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 +40 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/package.json +1 -1
- package/packages/pi-coding-agent/src/core/agent-session-tool-refresh.test.ts +64 -0
- package/packages/pi-coding-agent/src/core/agent-session.ts +10 -0
- package/packages/pi-coding-agent/src/core/resource-loader-cache-reset.test.ts +42 -0
- package/packages/pi-coding-agent/src/core/resource-loader.ts +5 -1
- package/packages/pi-coding-agent/src/core/retry-handler.test.ts +80 -0
- package/packages/pi-coding-agent/src/core/retry-handler.ts +37 -25
- package/packages/pi-coding-agent/src/core/sdk.ts +9 -9
- package/packages/pi-coding-agent/src/modes/interactive/components/provider-manager.ts +10 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +29 -4
- package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.test.ts +27 -0
- package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.ts +16 -1
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +38 -0
- package/packages/pi-tui/dist/__tests__/overlay-layout.test.d.ts +2 -0
- package/packages/pi-tui/dist/__tests__/overlay-layout.test.d.ts.map +1 -0
- package/packages/pi-tui/dist/__tests__/overlay-layout.test.js +66 -0
- package/packages/pi-tui/dist/__tests__/overlay-layout.test.js.map +1 -0
- package/packages/pi-tui/dist/components/image.d.ts +2 -0
- package/packages/pi-tui/dist/components/image.d.ts.map +1 -1
- package/packages/pi-tui/dist/components/image.js +4 -0
- package/packages/pi-tui/dist/components/image.js.map +1 -1
- package/packages/pi-tui/dist/components/image.test.d.ts +6 -0
- package/packages/pi-tui/dist/components/image.test.d.ts.map +1 -0
- package/packages/pi-tui/dist/components/image.test.js +32 -0
- package/packages/pi-tui/dist/components/image.test.js.map +1 -0
- package/packages/pi-tui/dist/components/loader.d.ts +4 -2
- package/packages/pi-tui/dist/components/loader.d.ts.map +1 -1
- package/packages/pi-tui/dist/components/loader.js +27 -9
- package/packages/pi-tui/dist/components/loader.js.map +1 -1
- package/packages/pi-tui/dist/components/text.d.ts.map +1 -1
- package/packages/pi-tui/dist/components/text.js +2 -0
- package/packages/pi-tui/dist/components/text.js.map +1 -1
- package/packages/pi-tui/dist/overlay-layout.d.ts.map +1 -1
- package/packages/pi-tui/dist/overlay-layout.js +12 -1
- package/packages/pi-tui/dist/overlay-layout.js.map +1 -1
- package/packages/pi-tui/dist/tui.d.ts +4 -0
- package/packages/pi-tui/dist/tui.d.ts.map +1 -1
- package/packages/pi-tui/dist/tui.js +35 -0
- package/packages/pi-tui/dist/tui.js.map +1 -1
- package/packages/pi-tui/src/__tests__/overlay-layout.test.ts +82 -0
- package/packages/pi-tui/src/components/image.test.ts +36 -0
- package/packages/pi-tui/src/components/image.ts +5 -0
- package/packages/pi-tui/src/components/loader.ts +27 -10
- package/packages/pi-tui/src/components/text.ts +1 -0
- package/packages/pi-tui/src/overlay-layout.ts +13 -1
- package/packages/pi-tui/src/tui.ts +34 -0
- package/pkg/package.json +1 -1
- package/src/resources/extensions/bg-shell/bg-shell-lifecycle.ts +19 -7
- package/src/resources/extensions/bg-shell/process-manager.ts +8 -2
- package/src/resources/extensions/browser-tools/capture.ts +19 -1
- package/src/resources/extensions/browser-tools/tests/capture-sharp-optional.test.cjs +93 -0
- package/src/resources/extensions/gsd/auto/run-unit.ts +12 -2
- package/src/resources/extensions/gsd/auto-dispatch.ts +105 -8
- package/src/resources/extensions/gsd/auto-model-selection.ts +7 -5
- package/src/resources/extensions/gsd/auto-post-unit.ts +138 -6
- package/src/resources/extensions/gsd/auto-prompts.ts +31 -0
- package/src/resources/extensions/gsd/auto-recovery.ts +10 -8
- package/src/resources/extensions/gsd/auto-start.ts +38 -0
- package/src/resources/extensions/gsd/auto-tool-tracking.ts +10 -0
- package/src/resources/extensions/gsd/auto-verification.ts +190 -2
- package/src/resources/extensions/gsd/auto-worktree.ts +28 -7
- package/src/resources/extensions/gsd/auto.ts +2 -1
- package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +16 -4
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +35 -13
- package/src/resources/extensions/gsd/bootstrap/notify-interceptor.ts +34 -0
- package/src/resources/extensions/gsd/bootstrap/query-tools.ts +5 -4
- package/src/resources/extensions/gsd/bootstrap/register-extension.ts +4 -1
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +12 -1
- package/src/resources/extensions/gsd/bootstrap/register-shortcuts.ts +20 -0
- package/src/resources/extensions/gsd/bootstrap/system-context.ts +28 -0
- package/src/resources/extensions/gsd/commands/catalog.ts +7 -1
- package/src/resources/extensions/gsd/commands/context.ts +7 -1
- package/src/resources/extensions/gsd/commands/handlers/core.ts +24 -0
- package/src/resources/extensions/gsd/commands/handlers/notifications-handler.ts +140 -0
- package/src/resources/extensions/gsd/commands/handlers/ops.ts +5 -0
- package/src/resources/extensions/gsd/commands-extensions.ts +1 -1
- package/src/resources/extensions/gsd/config-overlay.ts +331 -0
- package/src/resources/extensions/gsd/detection.ts +1 -1
- package/src/resources/extensions/gsd/dispatch-guard.ts +2 -1
- package/src/resources/extensions/gsd/docs/preferences-reference.md +1 -0
- package/src/resources/extensions/gsd/doctor.ts +2 -1
- package/src/resources/extensions/gsd/gitignore.ts +1 -0
- package/src/resources/extensions/gsd/gsd-db.ts +13 -2
- package/src/resources/extensions/gsd/guided-flow.ts +254 -30
- package/src/resources/extensions/gsd/md-importer.ts +13 -6
- package/src/resources/extensions/gsd/notification-overlay.ts +295 -0
- package/src/resources/extensions/gsd/notification-store.ts +293 -0
- package/src/resources/extensions/gsd/notification-widget.ts +68 -0
- package/src/resources/extensions/gsd/parallel-orchestrator.ts +19 -11
- package/src/resources/extensions/gsd/post-execution-checks.ts +539 -0
- package/src/resources/extensions/gsd/pre-execution-checks.ts +581 -0
- package/src/resources/extensions/gsd/preferences-types.ts +53 -0
- package/src/resources/extensions/gsd/preferences-validation.ts +78 -1
- package/src/resources/extensions/gsd/preferences.ts +13 -2
- package/src/resources/extensions/gsd/preparation.ts +1419 -0
- package/src/resources/extensions/gsd/prompt-validation.ts +88 -0
- package/src/resources/extensions/gsd/prompts/complete-milestone.md +3 -3
- package/src/resources/extensions/gsd/prompts/complete-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/discuss-prepared.md +424 -0
- package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +4 -1
- package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +5 -4
- package/src/resources/extensions/gsd/prompts/parallel-research-slices.md +23 -0
- package/src/resources/extensions/gsd/prompts/rethink.md +2 -1
- package/src/resources/extensions/gsd/prompts/validate-milestone.md +56 -23
- package/src/resources/extensions/gsd/quick.ts +20 -15
- package/src/resources/extensions/gsd/reactive-graph.ts +18 -0
- package/src/resources/extensions/gsd/roadmap-slices.ts +21 -5
- package/src/resources/extensions/gsd/safety/content-validator.ts +3 -3
- package/src/resources/extensions/gsd/session-lock.ts +17 -1
- package/src/resources/extensions/gsd/state.ts +101 -11
- package/src/resources/extensions/gsd/templates/context-enhanced.md +138 -0
- package/src/resources/extensions/gsd/tests/adversarial-review-fixes.test.ts +223 -0
- package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +33 -2
- package/src/resources/extensions/gsd/tests/auto-remediate-slice-status.test.ts +56 -0
- package/src/resources/extensions/gsd/tests/clear-stale-autostart.test.ts +41 -0
- package/src/resources/extensions/gsd/tests/complete-slice-string-coercion.test.ts +36 -0
- package/src/resources/extensions/gsd/tests/complete-slice-verification-gate.test.ts +72 -0
- package/src/resources/extensions/gsd/tests/complete-task-normalize-lists.test.ts +54 -0
- package/src/resources/extensions/gsd/tests/defer-milestone-stamp.test.ts +30 -0
- package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +4 -3
- package/src/resources/extensions/gsd/tests/discuss-incremental-persistence.test.ts +36 -0
- package/src/resources/extensions/gsd/tests/discuss-slice-structured-questions.test.ts +46 -0
- package/src/resources/extensions/gsd/tests/discuss-tool-scope-leak.test.ts +76 -0
- package/src/resources/extensions/gsd/tests/dispatch-guard-closed-status.test.ts +33 -0
- package/src/resources/extensions/gsd/tests/dispatcher-stuck-planning.test.ts +37 -0
- package/src/resources/extensions/gsd/tests/enhanced-verification-integration.test.ts +526 -0
- package/src/resources/extensions/gsd/tests/error-success-mask.test.ts +37 -0
- package/src/resources/extensions/gsd/tests/find-missing-summaries-closed.test.ts +48 -0
- package/src/resources/extensions/gsd/tests/frontmatter-parse-noise.test.ts +42 -0
- package/src/resources/extensions/gsd/tests/gitignore-bg-shell.test.ts +38 -0
- package/src/resources/extensions/gsd/tests/guided-flow-state-rebuild.test.ts +103 -0
- package/src/resources/extensions/gsd/tests/import-done-milestones.test.ts +42 -0
- package/src/resources/extensions/gsd/tests/integration/auto-recovery.test.ts +5 -3
- package/src/resources/extensions/gsd/tests/integration/state-machine-edge-cases.test.ts +4 -2
- package/src/resources/extensions/gsd/tests/integration/state-machine-live-validation.test.ts +28 -30
- package/src/resources/extensions/gsd/tests/integration/test-isolation.ts +53 -0
- package/src/resources/extensions/gsd/tests/integration-prepared-discussion.test.ts +525 -0
- package/src/resources/extensions/gsd/tests/isolation-none-branch-guard.test.ts +62 -0
- package/src/resources/extensions/gsd/tests/needs-remediation-revalidation.test.ts +48 -0
- package/src/resources/extensions/gsd/tests/note-captures-executed.test.ts +46 -0
- package/src/resources/extensions/gsd/tests/notification-overlay.test.ts +73 -0
- package/src/resources/extensions/gsd/tests/notification-store.test.ts +282 -0
- package/src/resources/extensions/gsd/tests/parallel-research-dispatch.test.ts +77 -0
- package/src/resources/extensions/gsd/tests/phantom-ghost-detection.test.ts +55 -0
- package/src/resources/extensions/gsd/tests/phantom-milestone-default-queued.test.ts +39 -0
- package/src/resources/extensions/gsd/tests/post-exec-retry-bypass.test.ts +312 -0
- package/src/resources/extensions/gsd/tests/post-execution-checks.test.ts +813 -0
- package/src/resources/extensions/gsd/tests/pre-exec-backtick-strip.test.ts +68 -0
- package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +1197 -0
- package/src/resources/extensions/gsd/tests/pre-execution-fail-closed.test.ts +266 -0
- package/src/resources/extensions/gsd/tests/pre-execution-pause-wiring.test.ts +457 -0
- package/src/resources/extensions/gsd/tests/preparation.test.ts +1211 -0
- package/src/resources/extensions/gsd/tests/project-root-cwd-crash.test.ts +53 -0
- package/src/resources/extensions/gsd/tests/projection-no-plan-overwrite.test.ts +83 -0
- package/src/resources/extensions/gsd/tests/prompt-builder.test.ts +669 -0
- package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +7 -4
- package/src/resources/extensions/gsd/tests/prompt-step-ordering.test.ts +85 -0
- package/src/resources/extensions/gsd/tests/provider-errors.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/query-tools-db-open.test.ts +47 -0
- package/src/resources/extensions/gsd/tests/queued-discuss-fast-path.test.ts +107 -0
- package/src/resources/extensions/gsd/tests/reactive-graph.test.ts +45 -0
- package/src/resources/extensions/gsd/tests/restore-tools-after-discuss.test.ts +63 -0
- package/src/resources/extensions/gsd/tests/rogue-file-detection.test.ts +4 -5
- package/src/resources/extensions/gsd/tests/run-uat-replay-cap.test.ts +51 -0
- package/src/resources/extensions/gsd/tests/show-config-command.test.ts +56 -0
- package/src/resources/extensions/gsd/tests/skip-slice-state-rebuild.test.ts +31 -0
- package/src/resources/extensions/gsd/tests/skipped-validation-completion.test.ts +39 -0
- package/src/resources/extensions/gsd/tests/slice-sequence-insert.test.ts +51 -0
- package/src/resources/extensions/gsd/tests/smart-entry-complete.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/stale-lockfile-recovery.test.ts +36 -0
- package/src/resources/extensions/gsd/tests/stale-queued-milestone.test.ts +147 -0
- package/src/resources/extensions/gsd/tests/stale-worktree-cwd.test.ts +13 -0
- package/src/resources/extensions/gsd/tests/stash-pop-gsd-conflict.test.ts +21 -0
- package/src/resources/extensions/gsd/tests/stash-queued-context-files.test.ts +21 -0
- package/src/resources/extensions/gsd/tests/state-machine-full-walkthrough.test.ts +6 -7
- package/src/resources/extensions/gsd/tests/status-db-open.test.ts +47 -0
- package/src/resources/extensions/gsd/tests/stuck-detection-coverage.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/symlink-extension-discovery.test.ts +125 -0
- package/src/resources/extensions/gsd/tests/sync-worktree-skip-current.test.ts +65 -0
- package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +29 -1
- package/src/resources/extensions/gsd/tests/triage-resolution.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/unstructured-continue-context-injection.test.ts +163 -0
- package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +3 -4
- package/src/resources/extensions/gsd/tests/verification-operational-gate.test.ts +15 -0
- package/src/resources/extensions/gsd/tests/verify-artifact-tightened.test.ts +89 -0
- package/src/resources/extensions/gsd/tests/worker-model-override.test.ts +48 -0
- package/src/resources/extensions/gsd/tests/worktree-expected-warnings.test.ts +38 -0
- package/src/resources/extensions/gsd/tests/worktree-integration.test.ts +16 -0
- package/src/resources/extensions/gsd/tests/worktree-main-branch.test.ts +20 -0
- package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +16 -17
- package/src/resources/extensions/gsd/tests/worktree-sync-tasks.test.ts +13 -9
- package/src/resources/extensions/gsd/tests/worktree.test.ts +26 -9
- package/src/resources/extensions/gsd/tests/zero-slice-roadmap-guided.test.ts +19 -0
- package/src/resources/extensions/gsd/tools/complete-slice.ts +13 -3
- package/src/resources/extensions/gsd/tools/complete-task.ts +16 -4
- package/src/resources/extensions/gsd/tools/plan-milestone.ts +11 -9
- package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +5 -2
- package/src/resources/extensions/gsd/tools/reopen-milestone.ts +152 -0
- package/src/resources/extensions/gsd/tools/reopen-slice.ts +27 -0
- package/src/resources/extensions/gsd/tools/reopen-task.ts +17 -0
- package/src/resources/extensions/gsd/triage-resolution.ts +29 -10
- package/src/resources/extensions/gsd/types.ts +4 -0
- package/src/resources/extensions/gsd/verification-evidence.ts +68 -0
- package/src/resources/extensions/gsd/workflow-logger.ts +13 -0
- package/src/resources/extensions/gsd/workflow-projections.ts +4 -6
- package/src/resources/extensions/gsd/workflow-reconcile.ts +2 -3
- package/src/resources/extensions/gsd/workflow-templates.ts +11 -2
- package/src/resources/extensions/gsd/worktree-manager.ts +4 -2
- package/src/resources/extensions/gsd/worktree.ts +10 -0
- package/src/resources/extensions/shared/interview-ui.ts +1 -1
- package/src/resources/extensions/shared/tests/interview-notes-loop.test.ts +8 -10
- package/dist/web/standalone/.next/static/F1mOwzgCW9R8N3Pt1Et87/_buildManifest.js +0 -1
- package/dist/web/standalone/.next/static/chunks/6502.7593d7797a4b3999.js +0 -9
- package/dist/web/standalone/.next/static/chunks/app/_global-error/page-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/boot/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/bridge-terminal/input/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/bridge-terminal/resize/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/bridge-terminal/stream/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/browse-directories/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/captures/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/cleanup/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/dev-mode/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/doctor/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/experimental/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/export-data/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/files/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/forensics/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/git/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/history/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/hooks/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/inspect/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/knowledge/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/live-state/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/onboarding/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/preferences/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/projects/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/recovery/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/remote-questions/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/session/browser/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/session/command/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/session/events/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/session/manage/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/settings-data/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/shutdown/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/skill-health/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/steer/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/switch-root/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/terminal/input/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/terminal/resize/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/terminal/sessions/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/terminal/stream/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/terminal/upload/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/undo/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/update/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/visualizer/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/app-error-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/forbidden-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/not-found-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/unauthorized-c4cc189e7b117ea2.js +0 -1
- /package/dist/web/standalone/.next/static/{F1mOwzgCW9R8N3Pt1Et87 → Z3TgDP0c7kG9j8CVQVGcl}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,1197 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* pre-execution-checks.test.ts — Unit tests for pre-execution validation checks.
|
|
3
|
+
*
|
|
4
|
+
* Tests all 4 check types:
|
|
5
|
+
* 1. Package existence — npm view mocking, timeout handling
|
|
6
|
+
* 2. File path consistency — files exist vs prior expected_output
|
|
7
|
+
* 3. Task ordering — detect impossible read-before-create
|
|
8
|
+
* 4. Interface contracts — contradictory function signatures
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { describe, test, mock } from "node:test";
|
|
12
|
+
import assert from "node:assert/strict";
|
|
13
|
+
import { tmpdir } from "node:os";
|
|
14
|
+
import { mkdirSync, writeFileSync, rmSync } from "node:fs";
|
|
15
|
+
import { join } from "node:path";
|
|
16
|
+
|
|
17
|
+
import {
|
|
18
|
+
extractPackageReferences,
|
|
19
|
+
checkFilePathConsistency,
|
|
20
|
+
checkTaskOrdering,
|
|
21
|
+
checkInterfaceContracts,
|
|
22
|
+
runPreExecutionChecks,
|
|
23
|
+
normalizeFilePath,
|
|
24
|
+
type PreExecutionResult,
|
|
25
|
+
} from "../pre-execution-checks.ts";
|
|
26
|
+
import type { TaskRow } from "../gsd-db.ts";
|
|
27
|
+
|
|
28
|
+
// ─── Test Fixtures ───────────────────────────────────────────────────────────
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Create a minimal TaskRow for testing.
|
|
32
|
+
*/
|
|
33
|
+
function createTask(overrides: Partial<TaskRow> = {}): TaskRow {
|
|
34
|
+
return {
|
|
35
|
+
milestone_id: "M001",
|
|
36
|
+
slice_id: "S01",
|
|
37
|
+
id: overrides.id ?? "T01",
|
|
38
|
+
title: "Test Task",
|
|
39
|
+
status: "pending",
|
|
40
|
+
one_liner: "",
|
|
41
|
+
narrative: "",
|
|
42
|
+
verification_result: "",
|
|
43
|
+
duration: "",
|
|
44
|
+
completed_at: null,
|
|
45
|
+
blocker_discovered: false,
|
|
46
|
+
deviations: "",
|
|
47
|
+
known_issues: "",
|
|
48
|
+
key_files: [],
|
|
49
|
+
key_decisions: [],
|
|
50
|
+
full_summary_md: "",
|
|
51
|
+
description: overrides.description ?? "",
|
|
52
|
+
estimate: "",
|
|
53
|
+
files: overrides.files ?? [],
|
|
54
|
+
verify: "",
|
|
55
|
+
inputs: overrides.inputs ?? [],
|
|
56
|
+
expected_output: overrides.expected_output ?? [],
|
|
57
|
+
observability_impact: "",
|
|
58
|
+
full_plan_md: "",
|
|
59
|
+
sequence: overrides.sequence ?? 0,
|
|
60
|
+
...overrides,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// ─── Package Reference Extraction Tests ──────────────────────────────────────
|
|
65
|
+
|
|
66
|
+
describe("extractPackageReferences", () => {
|
|
67
|
+
test("extracts npm install patterns", () => {
|
|
68
|
+
const desc = "Run npm install lodash then npm i axios";
|
|
69
|
+
const packages = extractPackageReferences(desc);
|
|
70
|
+
assert.deepEqual(packages.sort(), ["axios", "lodash"]);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test("extracts yarn add patterns", () => {
|
|
74
|
+
const desc = "yarn add react-dom";
|
|
75
|
+
const packages = extractPackageReferences(desc);
|
|
76
|
+
assert.deepEqual(packages, ["react-dom"]);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test("extracts scoped packages", () => {
|
|
80
|
+
const desc = "npm install @types/node @babel/core";
|
|
81
|
+
const packages = extractPackageReferences(desc);
|
|
82
|
+
assert.ok(packages.includes("@types/node"));
|
|
83
|
+
assert.ok(packages.includes("@babel/core"));
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
test("extracts require statements from code blocks", () => {
|
|
87
|
+
const desc = `
|
|
88
|
+
\`\`\`javascript
|
|
89
|
+
const fs = require('fs-extra');
|
|
90
|
+
const path = require('path');
|
|
91
|
+
\`\`\`
|
|
92
|
+
`;
|
|
93
|
+
const packages = extractPackageReferences(desc);
|
|
94
|
+
assert.ok(packages.includes("fs-extra"));
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test("extracts import statements from code blocks", () => {
|
|
98
|
+
const desc = `
|
|
99
|
+
\`\`\`typescript
|
|
100
|
+
import express from 'express';
|
|
101
|
+
import { Router } from 'express';
|
|
102
|
+
import type { Request } from 'express';
|
|
103
|
+
\`\`\`
|
|
104
|
+
`;
|
|
105
|
+
const packages = extractPackageReferences(desc);
|
|
106
|
+
assert.ok(packages.includes("express"));
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
test("ignores relative imports", () => {
|
|
110
|
+
const desc = `import { foo } from './local-file';`;
|
|
111
|
+
const packages = extractPackageReferences(desc);
|
|
112
|
+
assert.deepEqual(packages, []);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
test("ignores node builtins", () => {
|
|
116
|
+
const desc = `import fs from 'node:fs';`;
|
|
117
|
+
const packages = extractPackageReferences(desc);
|
|
118
|
+
assert.deepEqual(packages, []);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
test("normalizes package subpaths", () => {
|
|
122
|
+
const desc = "npm install lodash/get";
|
|
123
|
+
const packages = extractPackageReferences(desc);
|
|
124
|
+
assert.deepEqual(packages, ["lodash"]);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
test("handles empty description", () => {
|
|
128
|
+
const packages = extractPackageReferences("");
|
|
129
|
+
assert.deepEqual(packages, []);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
test("ignores flags in npm install", () => {
|
|
133
|
+
const desc = "npm install -D typescript";
|
|
134
|
+
const packages = extractPackageReferences(desc);
|
|
135
|
+
assert.ok(packages.includes("typescript"));
|
|
136
|
+
assert.ok(!packages.includes("-D"));
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
// ─── File Path Consistency Tests ─────────────────────────────────────────────
|
|
141
|
+
|
|
142
|
+
describe("checkFilePathConsistency", () => {
|
|
143
|
+
let tempDir: string;
|
|
144
|
+
|
|
145
|
+
test("passes when files exist on disk", () => {
|
|
146
|
+
tempDir = join(tmpdir(), `pre-exec-test-${Date.now()}`);
|
|
147
|
+
mkdirSync(tempDir, { recursive: true });
|
|
148
|
+
writeFileSync(join(tempDir, "existing.ts"), "// content");
|
|
149
|
+
|
|
150
|
+
try {
|
|
151
|
+
const tasks = [
|
|
152
|
+
createTask({
|
|
153
|
+
id: "T01",
|
|
154
|
+
files: ["existing.ts"],
|
|
155
|
+
inputs: [],
|
|
156
|
+
expected_output: [],
|
|
157
|
+
}),
|
|
158
|
+
];
|
|
159
|
+
|
|
160
|
+
const results = checkFilePathConsistency(tasks, tempDir);
|
|
161
|
+
assert.deepEqual(results, []);
|
|
162
|
+
} finally {
|
|
163
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
test("passes when files are in prior expected_output", () => {
|
|
168
|
+
tempDir = join(tmpdir(), `pre-exec-test-${Date.now()}`);
|
|
169
|
+
mkdirSync(tempDir, { recursive: true });
|
|
170
|
+
|
|
171
|
+
try {
|
|
172
|
+
const tasks = [
|
|
173
|
+
createTask({
|
|
174
|
+
id: "T01",
|
|
175
|
+
sequence: 0,
|
|
176
|
+
files: [],
|
|
177
|
+
inputs: [],
|
|
178
|
+
expected_output: ["generated.ts"],
|
|
179
|
+
}),
|
|
180
|
+
createTask({
|
|
181
|
+
id: "T02",
|
|
182
|
+
sequence: 1,
|
|
183
|
+
files: ["generated.ts"],
|
|
184
|
+
inputs: [],
|
|
185
|
+
expected_output: [],
|
|
186
|
+
}),
|
|
187
|
+
];
|
|
188
|
+
|
|
189
|
+
const results = checkFilePathConsistency(tasks, tempDir);
|
|
190
|
+
assert.deepEqual(results, []);
|
|
191
|
+
} finally {
|
|
192
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
test("fails when inputs don't exist and not in prior outputs", () => {
|
|
197
|
+
tempDir = join(tmpdir(), `pre-exec-test-${Date.now()}`);
|
|
198
|
+
mkdirSync(tempDir, { recursive: true });
|
|
199
|
+
|
|
200
|
+
try {
|
|
201
|
+
const tasks = [
|
|
202
|
+
createTask({
|
|
203
|
+
id: "T01",
|
|
204
|
+
files: [],
|
|
205
|
+
inputs: ["nonexistent.ts"],
|
|
206
|
+
expected_output: [],
|
|
207
|
+
}),
|
|
208
|
+
];
|
|
209
|
+
|
|
210
|
+
const results = checkFilePathConsistency(tasks, tempDir);
|
|
211
|
+
assert.equal(results.length, 1);
|
|
212
|
+
assert.equal(results[0].category, "file");
|
|
213
|
+
assert.equal(results[0].passed, false);
|
|
214
|
+
assert.equal(results[0].blocking, true);
|
|
215
|
+
assert.ok(results[0].message.includes("nonexistent.ts"));
|
|
216
|
+
} finally {
|
|
217
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
test("checks only inputs array, not files array", () => {
|
|
222
|
+
tempDir = join(tmpdir(), `pre-exec-test-${Date.now()}`);
|
|
223
|
+
mkdirSync(tempDir, { recursive: true });
|
|
224
|
+
|
|
225
|
+
try {
|
|
226
|
+
const tasks = [
|
|
227
|
+
createTask({
|
|
228
|
+
id: "T01",
|
|
229
|
+
files: ["missing-file.ts"],
|
|
230
|
+
inputs: ["missing-input.ts"],
|
|
231
|
+
expected_output: [],
|
|
232
|
+
}),
|
|
233
|
+
];
|
|
234
|
+
|
|
235
|
+
// Only inputs are checked — files ("files likely touched") are excluded
|
|
236
|
+
// because they may include files the task will create (#3626)
|
|
237
|
+
const results = checkFilePathConsistency(tasks, tempDir);
|
|
238
|
+
assert.equal(results.length, 1);
|
|
239
|
+
assert.ok(results.some((r) => r.target === "missing-input.ts"));
|
|
240
|
+
// missing-file.ts should NOT produce a failure
|
|
241
|
+
assert.ok(!results.some((r) => r.target === "missing-file.ts"));
|
|
242
|
+
} finally {
|
|
243
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
244
|
+
}
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
test("skips empty file strings", () => {
|
|
248
|
+
tempDir = join(tmpdir(), `pre-exec-test-${Date.now()}`);
|
|
249
|
+
mkdirSync(tempDir, { recursive: true });
|
|
250
|
+
|
|
251
|
+
try {
|
|
252
|
+
const tasks = [
|
|
253
|
+
createTask({
|
|
254
|
+
id: "T01",
|
|
255
|
+
files: ["", " "],
|
|
256
|
+
inputs: [],
|
|
257
|
+
expected_output: [],
|
|
258
|
+
}),
|
|
259
|
+
];
|
|
260
|
+
|
|
261
|
+
const results = checkFilePathConsistency(tasks, tempDir);
|
|
262
|
+
assert.deepEqual(results, []);
|
|
263
|
+
} finally {
|
|
264
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
265
|
+
}
|
|
266
|
+
});
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
// ─── Path Normalization Tests ────────────────────────────────────────────────
|
|
270
|
+
|
|
271
|
+
describe("normalizeFilePath", () => {
|
|
272
|
+
test("strips leading ./", () => {
|
|
273
|
+
assert.equal(normalizeFilePath("./src/a.ts"), "src/a.ts");
|
|
274
|
+
assert.equal(normalizeFilePath("././foo.ts"), "foo.ts");
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
test("normalizes backslashes to forward slashes", () => {
|
|
278
|
+
assert.equal(normalizeFilePath("src\\a.ts"), "src/a.ts");
|
|
279
|
+
assert.equal(normalizeFilePath("src\\sub\\file.ts"), "src/sub/file.ts");
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
test("removes duplicate slashes", () => {
|
|
283
|
+
assert.equal(normalizeFilePath("src//a.ts"), "src/a.ts");
|
|
284
|
+
assert.equal(normalizeFilePath("src///sub//file.ts"), "src/sub/file.ts");
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
test("handles empty string", () => {
|
|
288
|
+
assert.equal(normalizeFilePath(""), "");
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
test("removes trailing slash", () => {
|
|
292
|
+
assert.equal(normalizeFilePath("src/"), "src");
|
|
293
|
+
assert.equal(normalizeFilePath("src/sub/"), "src/sub");
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
test("handles paths without any normalization needed", () => {
|
|
297
|
+
assert.equal(normalizeFilePath("src/a.ts"), "src/a.ts");
|
|
298
|
+
assert.equal(normalizeFilePath("index.ts"), "index.ts");
|
|
299
|
+
});
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
describe("checkFilePathConsistency with path normalization", () => {
|
|
303
|
+
let tempDir: string;
|
|
304
|
+
|
|
305
|
+
test("./path matches path in prior expected_output", () => {
|
|
306
|
+
tempDir = join(tmpdir(), `pre-exec-test-${Date.now()}`);
|
|
307
|
+
mkdirSync(tempDir, { recursive: true });
|
|
308
|
+
|
|
309
|
+
try {
|
|
310
|
+
const tasks = [
|
|
311
|
+
createTask({
|
|
312
|
+
id: "T01",
|
|
313
|
+
sequence: 0,
|
|
314
|
+
files: [],
|
|
315
|
+
inputs: [],
|
|
316
|
+
expected_output: ["src/generated.ts"], // Output without ./
|
|
317
|
+
}),
|
|
318
|
+
createTask({
|
|
319
|
+
id: "T02",
|
|
320
|
+
sequence: 1,
|
|
321
|
+
files: ["./src/generated.ts"], // Input with ./
|
|
322
|
+
inputs: [],
|
|
323
|
+
expected_output: [],
|
|
324
|
+
}),
|
|
325
|
+
];
|
|
326
|
+
|
|
327
|
+
const results = checkFilePathConsistency(tasks, tempDir);
|
|
328
|
+
assert.deepEqual(results, [], "Should pass because ./src/generated.ts matches src/generated.ts");
|
|
329
|
+
} finally {
|
|
330
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
331
|
+
}
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
test("path matches ./path in prior expected_output", () => {
|
|
335
|
+
tempDir = join(tmpdir(), `pre-exec-test-${Date.now()}`);
|
|
336
|
+
mkdirSync(tempDir, { recursive: true });
|
|
337
|
+
|
|
338
|
+
try {
|
|
339
|
+
const tasks = [
|
|
340
|
+
createTask({
|
|
341
|
+
id: "T01",
|
|
342
|
+
sequence: 0,
|
|
343
|
+
files: [],
|
|
344
|
+
inputs: [],
|
|
345
|
+
expected_output: ["./src/generated.ts"], // Output with ./
|
|
346
|
+
}),
|
|
347
|
+
createTask({
|
|
348
|
+
id: "T02",
|
|
349
|
+
sequence: 1,
|
|
350
|
+
files: ["src/generated.ts"], // Input without ./
|
|
351
|
+
inputs: [],
|
|
352
|
+
expected_output: [],
|
|
353
|
+
}),
|
|
354
|
+
];
|
|
355
|
+
|
|
356
|
+
const results = checkFilePathConsistency(tasks, tempDir);
|
|
357
|
+
assert.deepEqual(results, [], "Should pass because src/generated.ts matches ./src/generated.ts");
|
|
358
|
+
} finally {
|
|
359
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
360
|
+
}
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
test("paths with mixed separators match", () => {
|
|
364
|
+
tempDir = join(tmpdir(), `pre-exec-test-${Date.now()}`);
|
|
365
|
+
mkdirSync(tempDir, { recursive: true });
|
|
366
|
+
|
|
367
|
+
try {
|
|
368
|
+
const tasks = [
|
|
369
|
+
createTask({
|
|
370
|
+
id: "T01",
|
|
371
|
+
sequence: 0,
|
|
372
|
+
files: [],
|
|
373
|
+
inputs: [],
|
|
374
|
+
expected_output: ["src/sub/file.ts"],
|
|
375
|
+
}),
|
|
376
|
+
createTask({
|
|
377
|
+
id: "T02",
|
|
378
|
+
sequence: 1,
|
|
379
|
+
files: ["src\\sub\\file.ts"], // Backslash separators
|
|
380
|
+
inputs: [],
|
|
381
|
+
expected_output: [],
|
|
382
|
+
}),
|
|
383
|
+
];
|
|
384
|
+
|
|
385
|
+
const results = checkFilePathConsistency(tasks, tempDir);
|
|
386
|
+
assert.deepEqual(results, [], "Should pass because backslash paths normalize to forward slash");
|
|
387
|
+
} finally {
|
|
388
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
389
|
+
}
|
|
390
|
+
});
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
describe("checkTaskOrdering with path normalization", () => {
|
|
394
|
+
test("./path in inputs triggers ordering check for path in expected_output", () => {
|
|
395
|
+
const tasks = [
|
|
396
|
+
createTask({
|
|
397
|
+
id: "T01",
|
|
398
|
+
sequence: 0,
|
|
399
|
+
files: [],
|
|
400
|
+
inputs: ["./generated.ts"], // Reads with ./
|
|
401
|
+
expected_output: [],
|
|
402
|
+
}),
|
|
403
|
+
createTask({
|
|
404
|
+
id: "T02",
|
|
405
|
+
sequence: 1,
|
|
406
|
+
files: [],
|
|
407
|
+
inputs: [],
|
|
408
|
+
expected_output: ["generated.ts"], // Creates without ./
|
|
409
|
+
}),
|
|
410
|
+
];
|
|
411
|
+
|
|
412
|
+
const results = checkTaskOrdering(tasks, "/tmp");
|
|
413
|
+
assert.equal(results.length, 1, "Should detect ordering violation despite ./");
|
|
414
|
+
assert.ok(results[0].message.includes("T01"));
|
|
415
|
+
assert.ok(results[0].message.includes("T02"));
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
test("path in inputs triggers ordering check for ./path in expected_output", () => {
|
|
419
|
+
const tasks = [
|
|
420
|
+
createTask({
|
|
421
|
+
id: "T01",
|
|
422
|
+
sequence: 0,
|
|
423
|
+
files: [],
|
|
424
|
+
inputs: ["generated.ts"], // Reads without ./
|
|
425
|
+
expected_output: [],
|
|
426
|
+
}),
|
|
427
|
+
createTask({
|
|
428
|
+
id: "T02",
|
|
429
|
+
sequence: 1,
|
|
430
|
+
files: [],
|
|
431
|
+
inputs: [],
|
|
432
|
+
expected_output: ["./generated.ts"], // Creates with ./
|
|
433
|
+
}),
|
|
434
|
+
];
|
|
435
|
+
|
|
436
|
+
const results = checkTaskOrdering(tasks, "/tmp");
|
|
437
|
+
assert.equal(results.length, 1, "Should detect ordering violation despite ./ on creator");
|
|
438
|
+
assert.ok(results[0].message.includes("sequence violation"));
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
test("no false positive when correctly ordered with mixed paths", () => {
|
|
442
|
+
const tasks = [
|
|
443
|
+
createTask({
|
|
444
|
+
id: "T01",
|
|
445
|
+
sequence: 0,
|
|
446
|
+
files: [],
|
|
447
|
+
inputs: [],
|
|
448
|
+
expected_output: ["./src/api.ts"],
|
|
449
|
+
}),
|
|
450
|
+
createTask({
|
|
451
|
+
id: "T02",
|
|
452
|
+
sequence: 1,
|
|
453
|
+
files: ["src/api.ts"], // Same file, different notation
|
|
454
|
+
inputs: [],
|
|
455
|
+
expected_output: [],
|
|
456
|
+
}),
|
|
457
|
+
];
|
|
458
|
+
|
|
459
|
+
const results = checkTaskOrdering(tasks, "/tmp");
|
|
460
|
+
assert.deepEqual(results, [], "Should pass - T02 reads file that T01 already created");
|
|
461
|
+
});
|
|
462
|
+
});
|
|
463
|
+
|
|
464
|
+
// ─── Task Ordering Tests ─────────────────────────────────────────────────────
|
|
465
|
+
|
|
466
|
+
describe("checkTaskOrdering", () => {
|
|
467
|
+
test("passes when tasks are correctly ordered", () => {
|
|
468
|
+
const tasks = [
|
|
469
|
+
createTask({
|
|
470
|
+
id: "T01",
|
|
471
|
+
sequence: 0,
|
|
472
|
+
files: [],
|
|
473
|
+
inputs: [],
|
|
474
|
+
expected_output: ["api.ts"],
|
|
475
|
+
}),
|
|
476
|
+
createTask({
|
|
477
|
+
id: "T02",
|
|
478
|
+
sequence: 1,
|
|
479
|
+
files: ["api.ts"],
|
|
480
|
+
inputs: [],
|
|
481
|
+
expected_output: [],
|
|
482
|
+
}),
|
|
483
|
+
];
|
|
484
|
+
|
|
485
|
+
const results = checkTaskOrdering(tasks, "/tmp");
|
|
486
|
+
assert.deepEqual(results, []);
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
test("fails when task inputs reference file created by later task", () => {
|
|
490
|
+
const tasks = [
|
|
491
|
+
createTask({
|
|
492
|
+
id: "T01",
|
|
493
|
+
sequence: 0,
|
|
494
|
+
files: [],
|
|
495
|
+
inputs: ["generated.ts"], // Reads file that doesn't exist yet
|
|
496
|
+
expected_output: [],
|
|
497
|
+
}),
|
|
498
|
+
createTask({
|
|
499
|
+
id: "T02",
|
|
500
|
+
sequence: 1,
|
|
501
|
+
files: [],
|
|
502
|
+
inputs: [],
|
|
503
|
+
expected_output: ["generated.ts"], // Creates the file
|
|
504
|
+
}),
|
|
505
|
+
];
|
|
506
|
+
|
|
507
|
+
const results = checkTaskOrdering(tasks, "/tmp");
|
|
508
|
+
assert.equal(results.length, 1);
|
|
509
|
+
assert.equal(results[0].category, "file");
|
|
510
|
+
assert.equal(results[0].passed, false);
|
|
511
|
+
assert.equal(results[0].blocking, true);
|
|
512
|
+
assert.ok(results[0].message.includes("T01"));
|
|
513
|
+
assert.ok(results[0].message.includes("T02"));
|
|
514
|
+
assert.ok(results[0].message.includes("sequence violation"));
|
|
515
|
+
});
|
|
516
|
+
|
|
517
|
+
test("detects ordering violation in inputs array", () => {
|
|
518
|
+
const tasks = [
|
|
519
|
+
createTask({
|
|
520
|
+
id: "T01",
|
|
521
|
+
sequence: 0,
|
|
522
|
+
files: [],
|
|
523
|
+
inputs: ["schema.json"],
|
|
524
|
+
expected_output: [],
|
|
525
|
+
}),
|
|
526
|
+
createTask({
|
|
527
|
+
id: "T02",
|
|
528
|
+
sequence: 1,
|
|
529
|
+
files: [],
|
|
530
|
+
inputs: [],
|
|
531
|
+
expected_output: ["schema.json"],
|
|
532
|
+
}),
|
|
533
|
+
];
|
|
534
|
+
|
|
535
|
+
const results = checkTaskOrdering(tasks, "/tmp");
|
|
536
|
+
assert.equal(results.length, 1);
|
|
537
|
+
assert.ok(results[0].message.includes("schema.json"));
|
|
538
|
+
});
|
|
539
|
+
|
|
540
|
+
test("handles multiple ordering violations via inputs", () => {
|
|
541
|
+
const tasks = [
|
|
542
|
+
createTask({
|
|
543
|
+
id: "T01",
|
|
544
|
+
sequence: 0,
|
|
545
|
+
files: [],
|
|
546
|
+
inputs: ["a.ts", "b.ts"],
|
|
547
|
+
expected_output: [],
|
|
548
|
+
}),
|
|
549
|
+
createTask({
|
|
550
|
+
id: "T02",
|
|
551
|
+
sequence: 1,
|
|
552
|
+
files: [],
|
|
553
|
+
inputs: [],
|
|
554
|
+
expected_output: ["a.ts"],
|
|
555
|
+
}),
|
|
556
|
+
createTask({
|
|
557
|
+
id: "T03",
|
|
558
|
+
sequence: 2,
|
|
559
|
+
files: [],
|
|
560
|
+
inputs: [],
|
|
561
|
+
expected_output: ["b.ts"],
|
|
562
|
+
}),
|
|
563
|
+
];
|
|
564
|
+
|
|
565
|
+
const results = checkTaskOrdering(tasks, "/tmp");
|
|
566
|
+
assert.equal(results.length, 2);
|
|
567
|
+
});
|
|
568
|
+
|
|
569
|
+
test("passes when no dependencies between tasks", () => {
|
|
570
|
+
const tasks = [
|
|
571
|
+
createTask({
|
|
572
|
+
id: "T01",
|
|
573
|
+
sequence: 0,
|
|
574
|
+
files: [],
|
|
575
|
+
inputs: [],
|
|
576
|
+
expected_output: ["a.ts"],
|
|
577
|
+
}),
|
|
578
|
+
createTask({
|
|
579
|
+
id: "T02",
|
|
580
|
+
sequence: 1,
|
|
581
|
+
files: [],
|
|
582
|
+
inputs: [],
|
|
583
|
+
expected_output: ["b.ts"],
|
|
584
|
+
}),
|
|
585
|
+
];
|
|
586
|
+
|
|
587
|
+
const results = checkTaskOrdering(tasks, "/tmp");
|
|
588
|
+
assert.deepEqual(results, []);
|
|
589
|
+
});
|
|
590
|
+
});
|
|
591
|
+
|
|
592
|
+
// ─── Interface Contract Tests ────────────────────────────────────────────────
|
|
593
|
+
|
|
594
|
+
describe("checkInterfaceContracts", () => {
|
|
595
|
+
test("passes when function signatures match", () => {
|
|
596
|
+
const tasks = [
|
|
597
|
+
createTask({
|
|
598
|
+
id: "T01",
|
|
599
|
+
description: `
|
|
600
|
+
\`\`\`typescript
|
|
601
|
+
function processData(input: string): boolean
|
|
602
|
+
\`\`\`
|
|
603
|
+
`,
|
|
604
|
+
}),
|
|
605
|
+
createTask({
|
|
606
|
+
id: "T02",
|
|
607
|
+
description: `
|
|
608
|
+
\`\`\`typescript
|
|
609
|
+
function processData(input: string): boolean
|
|
610
|
+
\`\`\`
|
|
611
|
+
`,
|
|
612
|
+
}),
|
|
613
|
+
];
|
|
614
|
+
|
|
615
|
+
const results = checkInterfaceContracts(tasks, "/tmp");
|
|
616
|
+
assert.deepEqual(results, []);
|
|
617
|
+
});
|
|
618
|
+
|
|
619
|
+
test("warns on parameter mismatch (non-blocking)", () => {
|
|
620
|
+
const tasks = [
|
|
621
|
+
createTask({
|
|
622
|
+
id: "T01",
|
|
623
|
+
description: `
|
|
624
|
+
\`\`\`typescript
|
|
625
|
+
function saveUser(name: string): void
|
|
626
|
+
\`\`\`
|
|
627
|
+
`,
|
|
628
|
+
}),
|
|
629
|
+
createTask({
|
|
630
|
+
id: "T02",
|
|
631
|
+
description: `
|
|
632
|
+
\`\`\`typescript
|
|
633
|
+
function saveUser(name: string, email: string): void
|
|
634
|
+
\`\`\`
|
|
635
|
+
`,
|
|
636
|
+
}),
|
|
637
|
+
];
|
|
638
|
+
|
|
639
|
+
const results = checkInterfaceContracts(tasks, "/tmp");
|
|
640
|
+
assert.equal(results.length, 1);
|
|
641
|
+
assert.equal(results[0].category, "schema");
|
|
642
|
+
assert.equal(results[0].target, "saveUser");
|
|
643
|
+
assert.equal(results[0].passed, true); // Warning, not failure
|
|
644
|
+
assert.equal(results[0].blocking, false);
|
|
645
|
+
assert.ok(results[0].message.includes("different parameters"));
|
|
646
|
+
});
|
|
647
|
+
|
|
648
|
+
test("warns on return type mismatch (non-blocking)", () => {
|
|
649
|
+
const tasks = [
|
|
650
|
+
createTask({
|
|
651
|
+
id: "T01",
|
|
652
|
+
description: `
|
|
653
|
+
\`\`\`typescript
|
|
654
|
+
function getData(): string
|
|
655
|
+
\`\`\`
|
|
656
|
+
`,
|
|
657
|
+
}),
|
|
658
|
+
createTask({
|
|
659
|
+
id: "T02",
|
|
660
|
+
description: `
|
|
661
|
+
\`\`\`typescript
|
|
662
|
+
function getData(): number
|
|
663
|
+
\`\`\`
|
|
664
|
+
`,
|
|
665
|
+
}),
|
|
666
|
+
];
|
|
667
|
+
|
|
668
|
+
const results = checkInterfaceContracts(tasks, "/tmp");
|
|
669
|
+
assert.equal(results.length, 1);
|
|
670
|
+
assert.ok(results[0].message.includes("different return types"));
|
|
671
|
+
});
|
|
672
|
+
|
|
673
|
+
test("handles export function syntax", () => {
|
|
674
|
+
const tasks = [
|
|
675
|
+
createTask({
|
|
676
|
+
id: "T01",
|
|
677
|
+
description: `
|
|
678
|
+
\`\`\`typescript
|
|
679
|
+
export function validate(data: object): boolean
|
|
680
|
+
\`\`\`
|
|
681
|
+
`,
|
|
682
|
+
}),
|
|
683
|
+
createTask({
|
|
684
|
+
id: "T02",
|
|
685
|
+
description: `
|
|
686
|
+
\`\`\`typescript
|
|
687
|
+
export function validate(data: string): boolean
|
|
688
|
+
\`\`\`
|
|
689
|
+
`,
|
|
690
|
+
}),
|
|
691
|
+
];
|
|
692
|
+
|
|
693
|
+
const results = checkInterfaceContracts(tasks, "/tmp");
|
|
694
|
+
assert.equal(results.length, 1);
|
|
695
|
+
assert.ok(results[0].message.includes("validate"));
|
|
696
|
+
});
|
|
697
|
+
|
|
698
|
+
test("handles async function syntax", () => {
|
|
699
|
+
const tasks = [
|
|
700
|
+
createTask({
|
|
701
|
+
id: "T01",
|
|
702
|
+
description: `
|
|
703
|
+
\`\`\`typescript
|
|
704
|
+
export async function fetchData(): Promise<string>
|
|
705
|
+
\`\`\`
|
|
706
|
+
`,
|
|
707
|
+
}),
|
|
708
|
+
createTask({
|
|
709
|
+
id: "T02",
|
|
710
|
+
description: `
|
|
711
|
+
\`\`\`typescript
|
|
712
|
+
export async function fetchData(): Promise<number>
|
|
713
|
+
\`\`\`
|
|
714
|
+
`,
|
|
715
|
+
}),
|
|
716
|
+
];
|
|
717
|
+
|
|
718
|
+
const results = checkInterfaceContracts(tasks, "/tmp");
|
|
719
|
+
assert.equal(results.length, 1);
|
|
720
|
+
});
|
|
721
|
+
|
|
722
|
+
test("handles const arrow function syntax", () => {
|
|
723
|
+
const tasks = [
|
|
724
|
+
createTask({
|
|
725
|
+
id: "T01",
|
|
726
|
+
description: `
|
|
727
|
+
\`\`\`typescript
|
|
728
|
+
const handler = (req: Request): Response =>
|
|
729
|
+
\`\`\`
|
|
730
|
+
`,
|
|
731
|
+
}),
|
|
732
|
+
createTask({
|
|
733
|
+
id: "T02",
|
|
734
|
+
description: `
|
|
735
|
+
\`\`\`typescript
|
|
736
|
+
const handler = (req: Request, res: Response): void =>
|
|
737
|
+
\`\`\`
|
|
738
|
+
`,
|
|
739
|
+
}),
|
|
740
|
+
];
|
|
741
|
+
|
|
742
|
+
const results = checkInterfaceContracts(tasks, "/tmp");
|
|
743
|
+
// Should have 2 results: parameter mismatch AND return type mismatch
|
|
744
|
+
assert.equal(results.length, 2);
|
|
745
|
+
assert.ok(results.some((r) => r.message.includes("handler")));
|
|
746
|
+
assert.ok(results.some((r) => r.message.includes("parameters")));
|
|
747
|
+
assert.ok(results.some((r) => r.message.includes("return types")));
|
|
748
|
+
});
|
|
749
|
+
|
|
750
|
+
test("passes when no code blocks present", () => {
|
|
751
|
+
const tasks = [
|
|
752
|
+
createTask({
|
|
753
|
+
id: "T01",
|
|
754
|
+
description: "Just some text without code blocks",
|
|
755
|
+
}),
|
|
756
|
+
];
|
|
757
|
+
|
|
758
|
+
const results = checkInterfaceContracts(tasks, "/tmp");
|
|
759
|
+
assert.deepEqual(results, []);
|
|
760
|
+
});
|
|
761
|
+
|
|
762
|
+
test("handles multiple mismatches for same function", () => {
|
|
763
|
+
const tasks = [
|
|
764
|
+
createTask({
|
|
765
|
+
id: "T01",
|
|
766
|
+
description: `
|
|
767
|
+
\`\`\`typescript
|
|
768
|
+
function process(a: string): string
|
|
769
|
+
\`\`\`
|
|
770
|
+
`,
|
|
771
|
+
}),
|
|
772
|
+
createTask({
|
|
773
|
+
id: "T02",
|
|
774
|
+
description: `
|
|
775
|
+
\`\`\`typescript
|
|
776
|
+
function process(a: number): number
|
|
777
|
+
\`\`\`
|
|
778
|
+
`,
|
|
779
|
+
}),
|
|
780
|
+
];
|
|
781
|
+
|
|
782
|
+
const results = checkInterfaceContracts(tasks, "/tmp");
|
|
783
|
+
// Should have both parameter and return type mismatches
|
|
784
|
+
assert.equal(results.length, 2);
|
|
785
|
+
});
|
|
786
|
+
});
|
|
787
|
+
|
|
788
|
+
// ─── runPreExecutionChecks Integration Tests ─────────────────────────────────
|
|
789
|
+
|
|
790
|
+
describe("runPreExecutionChecks", () => {
|
|
791
|
+
let tempDir: string;
|
|
792
|
+
|
|
793
|
+
test("returns pass status when all checks pass", async () => {
|
|
794
|
+
tempDir = join(tmpdir(), `pre-exec-test-${Date.now()}`);
|
|
795
|
+
mkdirSync(tempDir, { recursive: true });
|
|
796
|
+
writeFileSync(join(tempDir, "existing.ts"), "// content");
|
|
797
|
+
|
|
798
|
+
try {
|
|
799
|
+
const tasks = [
|
|
800
|
+
createTask({
|
|
801
|
+
id: "T01",
|
|
802
|
+
files: ["existing.ts"],
|
|
803
|
+
inputs: [],
|
|
804
|
+
expected_output: ["output.ts"],
|
|
805
|
+
}),
|
|
806
|
+
createTask({
|
|
807
|
+
id: "T02",
|
|
808
|
+
files: ["output.ts"],
|
|
809
|
+
inputs: [],
|
|
810
|
+
expected_output: [],
|
|
811
|
+
}),
|
|
812
|
+
];
|
|
813
|
+
|
|
814
|
+
const result = await runPreExecutionChecks(tasks, tempDir);
|
|
815
|
+
assert.equal(result.status, "pass");
|
|
816
|
+
assert.equal(result.checks.length, 0);
|
|
817
|
+
assert.ok(result.durationMs >= 0);
|
|
818
|
+
} finally {
|
|
819
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
820
|
+
}
|
|
821
|
+
});
|
|
822
|
+
|
|
823
|
+
test("returns fail status when blocking failure exists", async () => {
|
|
824
|
+
tempDir = join(tmpdir(), `pre-exec-test-${Date.now()}`);
|
|
825
|
+
mkdirSync(tempDir, { recursive: true });
|
|
826
|
+
|
|
827
|
+
try {
|
|
828
|
+
const tasks = [
|
|
829
|
+
createTask({
|
|
830
|
+
id: "T01",
|
|
831
|
+
files: [],
|
|
832
|
+
inputs: ["nonexistent.ts"],
|
|
833
|
+
expected_output: [],
|
|
834
|
+
}),
|
|
835
|
+
];
|
|
836
|
+
|
|
837
|
+
const result = await runPreExecutionChecks(tasks, tempDir);
|
|
838
|
+
assert.equal(result.status, "fail");
|
|
839
|
+
assert.ok(result.checks.length > 0);
|
|
840
|
+
assert.ok(result.checks.some((c) => c.blocking === true));
|
|
841
|
+
} finally {
|
|
842
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
843
|
+
}
|
|
844
|
+
});
|
|
845
|
+
|
|
846
|
+
test("returns warn status for non-blocking issues", async () => {
|
|
847
|
+
tempDir = join(tmpdir(), `pre-exec-test-${Date.now()}`);
|
|
848
|
+
mkdirSync(tempDir, { recursive: true });
|
|
849
|
+
|
|
850
|
+
try {
|
|
851
|
+
// Create tasks with only interface contract warnings
|
|
852
|
+
const tasks = [
|
|
853
|
+
createTask({
|
|
854
|
+
id: "T01",
|
|
855
|
+
files: [],
|
|
856
|
+
inputs: [],
|
|
857
|
+
expected_output: [],
|
|
858
|
+
description: `
|
|
859
|
+
\`\`\`typescript
|
|
860
|
+
function foo(a: string): void
|
|
861
|
+
\`\`\`
|
|
862
|
+
`,
|
|
863
|
+
}),
|
|
864
|
+
createTask({
|
|
865
|
+
id: "T02",
|
|
866
|
+
files: [],
|
|
867
|
+
inputs: [],
|
|
868
|
+
expected_output: [],
|
|
869
|
+
description: `
|
|
870
|
+
\`\`\`typescript
|
|
871
|
+
function foo(a: number): void
|
|
872
|
+
\`\`\`
|
|
873
|
+
`,
|
|
874
|
+
}),
|
|
875
|
+
];
|
|
876
|
+
|
|
877
|
+
const result = await runPreExecutionChecks(tasks, tempDir);
|
|
878
|
+
assert.equal(result.status, "warn");
|
|
879
|
+
assert.ok(result.checks.some((c) => c.blocking === false));
|
|
880
|
+
} finally {
|
|
881
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
882
|
+
}
|
|
883
|
+
});
|
|
884
|
+
|
|
885
|
+
test("combines results from all check types", async () => {
|
|
886
|
+
tempDir = join(tmpdir(), `pre-exec-test-${Date.now()}`);
|
|
887
|
+
mkdirSync(tempDir, { recursive: true });
|
|
888
|
+
|
|
889
|
+
try {
|
|
890
|
+
const tasks = [
|
|
891
|
+
createTask({
|
|
892
|
+
id: "T01",
|
|
893
|
+
sequence: 0,
|
|
894
|
+
files: ["will-be-created.ts"], // Ordering violation
|
|
895
|
+
inputs: ["missing.ts"], // Missing file
|
|
896
|
+
expected_output: [],
|
|
897
|
+
description: `
|
|
898
|
+
\`\`\`typescript
|
|
899
|
+
function check(a: string): void
|
|
900
|
+
\`\`\`
|
|
901
|
+
`,
|
|
902
|
+
}),
|
|
903
|
+
createTask({
|
|
904
|
+
id: "T02",
|
|
905
|
+
sequence: 1,
|
|
906
|
+
files: [],
|
|
907
|
+
inputs: [],
|
|
908
|
+
expected_output: ["will-be-created.ts"],
|
|
909
|
+
description: `
|
|
910
|
+
\`\`\`typescript
|
|
911
|
+
function check(a: number): void
|
|
912
|
+
\`\`\`
|
|
913
|
+
`,
|
|
914
|
+
}),
|
|
915
|
+
];
|
|
916
|
+
|
|
917
|
+
const result = await runPreExecutionChecks(tasks, tempDir);
|
|
918
|
+
assert.equal(result.status, "fail");
|
|
919
|
+
|
|
920
|
+
// Should have multiple types of issues
|
|
921
|
+
const categories = new Set(result.checks.map((c) => c.category));
|
|
922
|
+
assert.ok(categories.has("file")); // From consistency and ordering
|
|
923
|
+
assert.ok(categories.has("schema")); // From interface check
|
|
924
|
+
} finally {
|
|
925
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
926
|
+
}
|
|
927
|
+
});
|
|
928
|
+
|
|
929
|
+
test("reports duration in milliseconds", async () => {
|
|
930
|
+
tempDir = join(tmpdir(), `pre-exec-test-${Date.now()}`);
|
|
931
|
+
mkdirSync(tempDir, { recursive: true });
|
|
932
|
+
|
|
933
|
+
try {
|
|
934
|
+
const tasks = [createTask({ id: "T01" })];
|
|
935
|
+
const result = await runPreExecutionChecks(tasks, tempDir);
|
|
936
|
+
|
|
937
|
+
assert.ok(typeof result.durationMs === "number");
|
|
938
|
+
assert.ok(result.durationMs >= 0);
|
|
939
|
+
} finally {
|
|
940
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
941
|
+
}
|
|
942
|
+
});
|
|
943
|
+
|
|
944
|
+
test("handles empty task array", async () => {
|
|
945
|
+
tempDir = join(tmpdir(), `pre-exec-test-${Date.now()}`);
|
|
946
|
+
mkdirSync(tempDir, { recursive: true });
|
|
947
|
+
|
|
948
|
+
try {
|
|
949
|
+
const result = await runPreExecutionChecks([], tempDir);
|
|
950
|
+
assert.equal(result.status, "pass");
|
|
951
|
+
assert.deepEqual(result.checks, []);
|
|
952
|
+
} finally {
|
|
953
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
954
|
+
}
|
|
955
|
+
});
|
|
956
|
+
});
|
|
957
|
+
|
|
958
|
+
// ─── Regression Tests: checkTaskOrdering false positive (#3677) ──────────────
|
|
959
|
+
|
|
960
|
+
describe("checkTaskOrdering false positive regression (#3677)", () => {
|
|
961
|
+
test("task.files should not trigger ordering violation when file is in later expected_output", () => {
|
|
962
|
+
// T01 has files: ["component.tsx"] — this is a file the task will CREATE,
|
|
963
|
+
// not read. Including task.files in the ordering check causes a false positive.
|
|
964
|
+
// After fix (check only task.inputs), this should return 0 results.
|
|
965
|
+
const tasks = [
|
|
966
|
+
createTask({
|
|
967
|
+
id: "T01",
|
|
968
|
+
sequence: 0,
|
|
969
|
+
files: ["component.tsx"],
|
|
970
|
+
inputs: [],
|
|
971
|
+
expected_output: [],
|
|
972
|
+
}),
|
|
973
|
+
createTask({
|
|
974
|
+
id: "T02",
|
|
975
|
+
sequence: 1,
|
|
976
|
+
files: [],
|
|
977
|
+
inputs: [],
|
|
978
|
+
expected_output: ["component.tsx"],
|
|
979
|
+
}),
|
|
980
|
+
];
|
|
981
|
+
|
|
982
|
+
const results = checkTaskOrdering(tasks, "/tmp");
|
|
983
|
+
assert.equal(results.length, 0, "task.files should not be checked for ordering violations");
|
|
984
|
+
});
|
|
985
|
+
|
|
986
|
+
test("task.files with multiple files should not trigger false positives", () => {
|
|
987
|
+
// T01 lists several files it will touch/create — none should trigger ordering
|
|
988
|
+
// violations just because T02 declares one of them as expected_output.
|
|
989
|
+
const tasks = [
|
|
990
|
+
createTask({
|
|
991
|
+
id: "T01",
|
|
992
|
+
sequence: 0,
|
|
993
|
+
files: ["a.ts", "b.ts", "c.ts"],
|
|
994
|
+
inputs: [],
|
|
995
|
+
expected_output: [],
|
|
996
|
+
}),
|
|
997
|
+
createTask({
|
|
998
|
+
id: "T02",
|
|
999
|
+
sequence: 1,
|
|
1000
|
+
files: [],
|
|
1001
|
+
inputs: [],
|
|
1002
|
+
expected_output: ["b.ts"],
|
|
1003
|
+
}),
|
|
1004
|
+
];
|
|
1005
|
+
|
|
1006
|
+
const results = checkTaskOrdering(tasks, "/tmp");
|
|
1007
|
+
assert.equal(results.length, 0, "Multiple task.files should not generate false positive violations");
|
|
1008
|
+
});
|
|
1009
|
+
|
|
1010
|
+
test("task.inputs SHOULD still trigger ordering violation", () => {
|
|
1011
|
+
// task.inputs represents files a task genuinely needs to READ, so a sequence
|
|
1012
|
+
// violation here is a real error and must still be detected.
|
|
1013
|
+
const tasks = [
|
|
1014
|
+
createTask({
|
|
1015
|
+
id: "T01",
|
|
1016
|
+
sequence: 0,
|
|
1017
|
+
files: [],
|
|
1018
|
+
inputs: ["config.json"],
|
|
1019
|
+
expected_output: [],
|
|
1020
|
+
}),
|
|
1021
|
+
createTask({
|
|
1022
|
+
id: "T02",
|
|
1023
|
+
sequence: 1,
|
|
1024
|
+
files: [],
|
|
1025
|
+
inputs: [],
|
|
1026
|
+
expected_output: ["config.json"],
|
|
1027
|
+
}),
|
|
1028
|
+
];
|
|
1029
|
+
|
|
1030
|
+
const results = checkTaskOrdering(tasks, "/tmp");
|
|
1031
|
+
assert.equal(results.length, 1, "task.inputs ordering violation must still be detected");
|
|
1032
|
+
assert.equal(results[0].blocking, true);
|
|
1033
|
+
assert.ok(results[0].message.includes("T01"));
|
|
1034
|
+
assert.ok(results[0].message.includes("T02"));
|
|
1035
|
+
assert.ok(results[0].message.includes("sequence violation"));
|
|
1036
|
+
});
|
|
1037
|
+
|
|
1038
|
+
test("mixed files and inputs — only inputs trigger ordering violation", () => {
|
|
1039
|
+
// T01 will create "created.ts" (files) and also needs to READ "needed.json" (inputs).
|
|
1040
|
+
// T02 creates both. Only the inputs dependency is a real violation.
|
|
1041
|
+
const tasks = [
|
|
1042
|
+
createTask({
|
|
1043
|
+
id: "T01",
|
|
1044
|
+
sequence: 0,
|
|
1045
|
+
files: ["created.ts"],
|
|
1046
|
+
inputs: ["needed.json"],
|
|
1047
|
+
expected_output: [],
|
|
1048
|
+
}),
|
|
1049
|
+
createTask({
|
|
1050
|
+
id: "T02",
|
|
1051
|
+
sequence: 1,
|
|
1052
|
+
files: [],
|
|
1053
|
+
inputs: [],
|
|
1054
|
+
expected_output: ["created.ts", "needed.json"],
|
|
1055
|
+
}),
|
|
1056
|
+
];
|
|
1057
|
+
|
|
1058
|
+
const results = checkTaskOrdering(tasks, "/tmp");
|
|
1059
|
+
assert.equal(results.length, 1, "Only the inputs entry should produce a violation, not files");
|
|
1060
|
+
assert.ok(results[0].target === "needed.json", "Violation target should be the input, not the file");
|
|
1061
|
+
});
|
|
1062
|
+
|
|
1063
|
+
test("task.files with normalized paths should not false-positive", () => {
|
|
1064
|
+
// Path normalization (./src/new-file.ts → src/new-file.ts) should not cause
|
|
1065
|
+
// task.files to match against expected_output and produce a false positive.
|
|
1066
|
+
const tasks = [
|
|
1067
|
+
createTask({
|
|
1068
|
+
id: "T01",
|
|
1069
|
+
sequence: 0,
|
|
1070
|
+
files: ["./src/new-file.ts"],
|
|
1071
|
+
inputs: [],
|
|
1072
|
+
expected_output: [],
|
|
1073
|
+
}),
|
|
1074
|
+
createTask({
|
|
1075
|
+
id: "T02",
|
|
1076
|
+
sequence: 1,
|
|
1077
|
+
files: [],
|
|
1078
|
+
inputs: [],
|
|
1079
|
+
expected_output: ["src/new-file.ts"],
|
|
1080
|
+
}),
|
|
1081
|
+
];
|
|
1082
|
+
|
|
1083
|
+
const results = checkTaskOrdering(tasks, "/tmp");
|
|
1084
|
+
assert.equal(results.length, 0, "Normalized task.files path should not trigger a false positive");
|
|
1085
|
+
});
|
|
1086
|
+
});
|
|
1087
|
+
|
|
1088
|
+
// ─── checkFilePathConsistency additional edge cases ──────────────────────────
|
|
1089
|
+
|
|
1090
|
+
describe("checkFilePathConsistency additional edge cases", () => {
|
|
1091
|
+
test("inputs referencing glob-like patterns should not crash", () => {
|
|
1092
|
+
// A glob pattern in inputs is unusual but should be handled gracefully.
|
|
1093
|
+
// The file won't exist on disk, so it should produce a blocking result.
|
|
1094
|
+
const tasks = [
|
|
1095
|
+
createTask({
|
|
1096
|
+
id: "T01",
|
|
1097
|
+
files: [],
|
|
1098
|
+
inputs: ["src/**/*.ts"],
|
|
1099
|
+
expected_output: [],
|
|
1100
|
+
}),
|
|
1101
|
+
];
|
|
1102
|
+
|
|
1103
|
+
// Should not throw
|
|
1104
|
+
let results: ReturnType<typeof checkFilePathConsistency>;
|
|
1105
|
+
assert.doesNotThrow(() => {
|
|
1106
|
+
results = checkFilePathConsistency(tasks, "/tmp");
|
|
1107
|
+
});
|
|
1108
|
+
assert.equal(results!.length, 1, "Glob-pattern input that doesn't exist should produce a blocking result");
|
|
1109
|
+
assert.equal(results![0].blocking, true);
|
|
1110
|
+
});
|
|
1111
|
+
|
|
1112
|
+
test("empty inputs array produces no results", () => {
|
|
1113
|
+
// A task with no inputs and only files should produce zero results from
|
|
1114
|
+
// consistency check — files are not checked (#3626).
|
|
1115
|
+
const tasks = [
|
|
1116
|
+
createTask({
|
|
1117
|
+
id: "T01",
|
|
1118
|
+
files: ["anything.ts"],
|
|
1119
|
+
inputs: [],
|
|
1120
|
+
expected_output: [],
|
|
1121
|
+
}),
|
|
1122
|
+
];
|
|
1123
|
+
|
|
1124
|
+
const results = checkFilePathConsistency(tasks, "/tmp");
|
|
1125
|
+
assert.equal(results.length, 0, "Empty inputs should produce no consistency check results");
|
|
1126
|
+
});
|
|
1127
|
+
|
|
1128
|
+
test("inputs with absolute paths are checked correctly", () => {
|
|
1129
|
+
// An absolute path in inputs should resolve to itself and pass when the file exists.
|
|
1130
|
+
const tempDir = join(tmpdir(), `pre-exec-test-abs-${Date.now()}`);
|
|
1131
|
+
mkdirSync(tempDir, { recursive: true });
|
|
1132
|
+
const absFilePath = join(tempDir, "real-file.ts");
|
|
1133
|
+
writeFileSync(absFilePath, "// content");
|
|
1134
|
+
|
|
1135
|
+
try {
|
|
1136
|
+
const tasks = [
|
|
1137
|
+
createTask({
|
|
1138
|
+
id: "T01",
|
|
1139
|
+
files: [],
|
|
1140
|
+
inputs: [absFilePath],
|
|
1141
|
+
expected_output: [],
|
|
1142
|
+
}),
|
|
1143
|
+
];
|
|
1144
|
+
|
|
1145
|
+
const results = checkFilePathConsistency(tasks, tempDir);
|
|
1146
|
+
assert.equal(results.length, 0, "Absolute path to an existing file should pass consistency check");
|
|
1147
|
+
} finally {
|
|
1148
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
1149
|
+
}
|
|
1150
|
+
});
|
|
1151
|
+
});
|
|
1152
|
+
|
|
1153
|
+
// ─── PreExecutionResult Type Tests ───────────────────────────────────────────
|
|
1154
|
+
|
|
1155
|
+
describe("PreExecutionResult type", () => {
|
|
1156
|
+
test("status is one of pass, warn, fail", async () => {
|
|
1157
|
+
const tempDir = join(tmpdir(), `pre-exec-test-${Date.now()}`);
|
|
1158
|
+
mkdirSync(tempDir, { recursive: true });
|
|
1159
|
+
|
|
1160
|
+
try {
|
|
1161
|
+
const tasks = [createTask({ id: "T01" })];
|
|
1162
|
+
const result = await runPreExecutionChecks(tasks, tempDir);
|
|
1163
|
+
|
|
1164
|
+
assert.ok(["pass", "warn", "fail"].includes(result.status));
|
|
1165
|
+
} finally {
|
|
1166
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
1167
|
+
}
|
|
1168
|
+
});
|
|
1169
|
+
|
|
1170
|
+
test("checks array matches PreExecutionCheckJSON schema", async () => {
|
|
1171
|
+
const tempDir = join(tmpdir(), `pre-exec-test-${Date.now()}`);
|
|
1172
|
+
mkdirSync(tempDir, { recursive: true });
|
|
1173
|
+
|
|
1174
|
+
try {
|
|
1175
|
+
const tasks = [
|
|
1176
|
+
createTask({
|
|
1177
|
+
id: "T01",
|
|
1178
|
+
files: ["missing.ts"],
|
|
1179
|
+
}),
|
|
1180
|
+
];
|
|
1181
|
+
|
|
1182
|
+
const result = await runPreExecutionChecks(tasks, tempDir);
|
|
1183
|
+
|
|
1184
|
+
for (const check of result.checks) {
|
|
1185
|
+
assert.ok(["package", "file", "tool", "endpoint", "schema"].includes(check.category));
|
|
1186
|
+
assert.ok(typeof check.target === "string");
|
|
1187
|
+
assert.ok(typeof check.passed === "boolean");
|
|
1188
|
+
assert.ok(typeof check.message === "string");
|
|
1189
|
+
if (check.blocking !== undefined) {
|
|
1190
|
+
assert.ok(typeof check.blocking === "boolean");
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
} finally {
|
|
1194
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
1195
|
+
}
|
|
1196
|
+
});
|
|
1197
|
+
});
|