gsd-pi 2.44.0-dev.848dd4c → 2.44.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -30
- package/dist/resources/extensions/gsd/auto-start.js +0 -10
- package/dist/resources/extensions/gsd/commands/handlers/workflow.js +0 -5
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +14 -14
- 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/required-server-files.json +3 -3
- package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
- package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
- package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +3 -3
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
- 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 +3 -3
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +5 -5
- package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +4 -4
- package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +4 -4
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -4
- 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 +3 -3
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/page.js +2 -2
- package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +14 -14
- package/dist/web/standalone/.next/server/chunks/229.js +1 -1
- package/dist/web/standalone/.next/server/chunks/471.js +3 -3
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware.js +2 -2
- package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
- package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +2 -2
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/dist/web/standalone/.next/static/chunks/app/_not-found/{page-f2a7482d42a5614b.js → page-2f24283c162b6ab3.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/app/{layout-a16c7a7ecdf0c2cf.js → layout-9ecfd95f343793f0.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/app/page-7e9530a7122506c5.js +1 -0
- package/dist/web/standalone/.next/static/chunks/main-app-d3d4c336195465f9.js +1 -0
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ab5a8926e07ec673.js +1 -0
- package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
- package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
- package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
- package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api.target.mk +14 -14
- package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_except.target.mk +14 -14
- package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_maybe.target.mk +14 -14
- package/dist/web/standalone/server.js +1 -1
- package/package.json +1 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.test.js +8 -6
- package/packages/pi-coding-agent/dist/core/auth-storage.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.test.js +26 -24
- package/packages/pi-coding-agent/dist/core/extensions/runner.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/fs-utils.test.js +48 -29
- package/packages/pi-coding-agent/dist/core/fs-utils.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/resolve-config-value.test.js +44 -34
- package/packages/pi-coding-agent/dist/core/resolve-config-value.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/session-manager.test.js +34 -30
- package/packages/pi-coding-agent/dist/core/session-manager.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.js +12 -10
- package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.test.js +47 -43
- package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.test.js.map +1 -1
- package/packages/pi-coding-agent/src/core/auth-storage.test.ts +7 -7
- package/packages/pi-coding-agent/src/core/extensions/runner.test.ts +26 -26
- package/packages/pi-coding-agent/src/core/fs-utils.test.ts +43 -31
- package/packages/pi-coding-agent/src/core/resolve-config-value.test.ts +45 -40
- package/packages/pi-coding-agent/src/core/session-manager.test.ts +33 -33
- package/packages/pi-coding-agent/src/core/tools/edit-diff.test.ts +17 -17
- package/packages/pi-coding-agent/src/resources/extensions/memory/storage.test.ts +74 -74
- package/src/resources/extensions/gsd/auto-start.ts +0 -14
- package/src/resources/extensions/gsd/commands/handlers/workflow.ts +0 -8
- package/src/resources/extensions/gsd/tests/all-milestones-complete-merge.test.ts +99 -99
- package/src/resources/extensions/gsd/tests/auto-lock-creation.test.ts +16 -14
- package/src/resources/extensions/gsd/tests/auto-paused-session-validation.test.ts +57 -43
- package/src/resources/extensions/gsd/tests/auto-preflight.test.ts +13 -11
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +523 -465
- package/src/resources/extensions/gsd/tests/auto-secrets-gate.test.ts +75 -73
- package/src/resources/extensions/gsd/tests/auto-start-needs-discussion.test.ts +56 -34
- package/src/resources/extensions/gsd/tests/auto-worktree-milestone-merge.test.ts +656 -533
- package/src/resources/extensions/gsd/tests/auto-worktree.test.ts +143 -165
- package/src/resources/extensions/gsd/tests/cache-staleness-regression.test.ts +52 -29
- package/src/resources/extensions/gsd/tests/captures.test.ts +176 -148
- package/src/resources/extensions/gsd/tests/claude-import-tui.test.ts +33 -32
- package/src/resources/extensions/gsd/tests/collect-from-manifest.test.ts +143 -141
- package/src/resources/extensions/gsd/tests/commands-inspect-open-db.test.ts +25 -25
- package/src/resources/extensions/gsd/tests/commands-logs.test.ts +81 -81
- package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +59 -38
- package/src/resources/extensions/gsd/tests/complete-slice.test.ts +263 -228
- package/src/resources/extensions/gsd/tests/complete-task.test.ts +302 -250
- package/src/resources/extensions/gsd/tests/context-store.test.ts +367 -354
- package/src/resources/extensions/gsd/tests/continue-here.test.ts +72 -68
- package/src/resources/extensions/gsd/tests/cost-projection.test.ts +106 -92
- package/src/resources/extensions/gsd/tests/crash-recovery.test.ts +35 -27
- package/src/resources/extensions/gsd/tests/dashboard-budget.test.ts +237 -220
- package/src/resources/extensions/gsd/tests/db-writer.test.ts +420 -390
- package/src/resources/extensions/gsd/tests/definition-loader.test.ts +92 -76
- package/src/resources/extensions/gsd/tests/derive-state-crossval.test.ts +83 -68
- package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +183 -152
- package/src/resources/extensions/gsd/tests/derive-state-deps.test.ts +101 -78
- package/src/resources/extensions/gsd/tests/derive-state.test.ts +227 -192
- package/src/resources/extensions/gsd/tests/detection.test.ts +278 -232
- package/src/resources/extensions/gsd/tests/dev-engine-wrapper.test.ts +34 -30
- package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +180 -164
- package/src/resources/extensions/gsd/tests/dispatch-missing-task-plans.test.ts +49 -43
- package/src/resources/extensions/gsd/tests/dispatch-uat-last-completed.test.ts +32 -28
- package/src/resources/extensions/gsd/tests/doctor-completion-deferral.test.ts +29 -27
- package/src/resources/extensions/gsd/tests/doctor-delimiter-fix.test.ts +38 -34
- package/src/resources/extensions/gsd/tests/doctor-enhancements.test.ts +75 -54
- package/src/resources/extensions/gsd/tests/doctor-environment-worktree.test.ts +32 -21
- package/src/resources/extensions/gsd/tests/doctor-environment.test.ts +97 -72
- package/src/resources/extensions/gsd/tests/doctor-fixlevel.test.ts +44 -38
- package/src/resources/extensions/gsd/tests/doctor-git.test.ts +145 -104
- package/src/resources/extensions/gsd/tests/doctor-proactive.test.ts +106 -84
- package/src/resources/extensions/gsd/tests/doctor-roadmap-summary-atomicity.test.ts +60 -54
- package/src/resources/extensions/gsd/tests/doctor-runtime.test.ts +93 -72
- package/src/resources/extensions/gsd/tests/doctor.test.ts +134 -104
- package/src/resources/extensions/gsd/tests/ensure-db-open.test.ts +131 -123
- package/src/resources/extensions/gsd/tests/exit-command.test.ts +24 -20
- package/src/resources/extensions/gsd/tests/feature-branch-lifecycle-integration.test.ts +57 -48
- package/src/resources/extensions/gsd/tests/files-loadfile-eisdir.test.ts +7 -5
- package/src/resources/extensions/gsd/tests/flag-file-db.test.ts +42 -30
- package/src/resources/extensions/gsd/tests/freeform-decisions.test.ts +206 -198
- package/src/resources/extensions/gsd/tests/git-locale.test.ts +27 -13
- package/src/resources/extensions/gsd/tests/git-service.test.ts +388 -285
- package/src/resources/extensions/gsd/tests/gitignore-tracked-gsd.test.ts +39 -31
- package/src/resources/extensions/gsd/tests/graph-operations.test.ts +69 -63
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +264 -255
- package/src/resources/extensions/gsd/tests/gsd-inspect.test.ts +119 -108
- package/src/resources/extensions/gsd/tests/gsd-recover.test.ts +103 -81
- package/src/resources/extensions/gsd/tests/gsd-tools.test.ts +262 -229
- package/src/resources/extensions/gsd/tests/headless-answers.test.ts +13 -13
- package/src/resources/extensions/gsd/tests/health-widget.test.ts +37 -29
- package/src/resources/extensions/gsd/tests/idle-recovery.test.ts +102 -81
- package/src/resources/extensions/gsd/tests/init-wizard.test.ts +18 -16
- package/src/resources/extensions/gsd/tests/integration-edge.test.ts +46 -41
- package/src/resources/extensions/gsd/tests/integration-lifecycle.test.ts +53 -42
- package/src/resources/extensions/gsd/tests/integration-mixed-milestones.test.ts +91 -75
- package/src/resources/extensions/gsd/tests/integration-proof.test.ts +18 -18
- package/src/resources/extensions/gsd/tests/markdown-renderer.test.ts +194 -150
- package/src/resources/extensions/gsd/tests/md-importer.test.ts +125 -101
- package/src/resources/extensions/gsd/tests/memory-extractor.test.ts +54 -45
- package/src/resources/extensions/gsd/tests/memory-store.test.ts +93 -80
- package/src/resources/extensions/gsd/tests/migrate-command.test.ts +66 -57
- package/src/resources/extensions/gsd/tests/migrate-hierarchy.test.ts +93 -83
- package/src/resources/extensions/gsd/tests/migrate-parser.test.ts +170 -161
- package/src/resources/extensions/gsd/tests/migrate-transformer.test.ts +141 -125
- package/src/resources/extensions/gsd/tests/migrate-validator-parsers.test.ts +131 -107
- package/src/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +96 -87
- package/src/resources/extensions/gsd/tests/migrate-writer.test.ts +164 -125
- package/src/resources/extensions/gsd/tests/must-have-parser.test.ts +94 -81
- package/src/resources/extensions/gsd/tests/none-mode-gates.test.ts +36 -35
- package/src/resources/extensions/gsd/tests/overrides.test.ts +106 -99
- package/src/resources/extensions/gsd/tests/parallel-crash-recovery.test.ts +47 -40
- package/src/resources/extensions/gsd/tests/parallel-worker-monitoring.test.ts +28 -25
- package/src/resources/extensions/gsd/tests/parallel-workers-multi-milestone-e2e.test.ts +83 -66
- package/src/resources/extensions/gsd/tests/park-edge-cases.test.ts +77 -54
- package/src/resources/extensions/gsd/tests/park-milestone.test.ts +115 -68
- package/src/resources/extensions/gsd/tests/parsers.test.ts +611 -546
- package/src/resources/extensions/gsd/tests/paths.test.ts +87 -72
- package/src/resources/extensions/gsd/tests/post-unit-hooks.test.ts +117 -77
- package/src/resources/extensions/gsd/tests/prompt-db.test.ts +56 -56
- package/src/resources/extensions/gsd/tests/queue-draft-detection.test.ts +119 -93
- package/src/resources/extensions/gsd/tests/queue-order.test.ts +82 -70
- package/src/resources/extensions/gsd/tests/queue-reorder-e2e.test.ts +55 -42
- package/src/resources/extensions/gsd/tests/quick-branch-lifecycle.test.ts +73 -45
- package/src/resources/extensions/gsd/tests/reassess-prompt.test.ts +38 -28
- package/src/resources/extensions/gsd/tests/replan-slice.test.ts +80 -73
- package/src/resources/extensions/gsd/tests/repo-identity-worktree.test.ts +74 -71
- package/src/resources/extensions/gsd/tests/requirements.test.ts +75 -70
- package/src/resources/extensions/gsd/tests/retry-state-reset.test.ts +66 -44
- package/src/resources/extensions/gsd/tests/roadmap-parse-regression.test.ts +181 -114
- package/src/resources/extensions/gsd/tests/rule-registry.test.ts +65 -63
- package/src/resources/extensions/gsd/tests/run-uat.test.ts +128 -66
- package/src/resources/extensions/gsd/tests/session-lock-multipath.test.ts +25 -18
- package/src/resources/extensions/gsd/tests/session-lock-regression.test.ts +44 -37
- package/src/resources/extensions/gsd/tests/shared-wal.test.ts +26 -19
- package/src/resources/extensions/gsd/tests/stalled-tool-recovery.test.ts +8 -6
- package/src/resources/extensions/gsd/tests/symlink-numbered-variants.test.ts +28 -22
- package/src/resources/extensions/gsd/tests/token-savings.test.ts +56 -54
- package/src/resources/extensions/gsd/tests/tool-call-loop-guard.test.ts +25 -23
- package/src/resources/extensions/gsd/tests/tool-naming.test.ts +11 -9
- package/src/resources/extensions/gsd/tests/unique-milestone-ids.test.ts +82 -66
- package/src/resources/extensions/gsd/tests/unit-runtime.test.ts +47 -46
- package/src/resources/extensions/gsd/tests/visualizer-critical-path.test.ts +22 -20
- package/src/resources/extensions/gsd/tests/visualizer-data.test.ts +86 -84
- package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +43 -41
- package/src/resources/extensions/gsd/tests/visualizer-views.test.ts +96 -94
- package/src/resources/extensions/gsd/tests/windows-path-normalization.test.ts +13 -11
- package/src/resources/extensions/gsd/tests/worker-registry.test.ts +29 -27
- package/src/resources/extensions/gsd/tests/workflow-templates.test.ts +52 -50
- package/src/resources/extensions/gsd/tests/worktree-bugfix.test.ts +13 -10
- package/src/resources/extensions/gsd/tests/worktree-db-integration.test.ts +18 -14
- package/src/resources/extensions/gsd/tests/worktree-db.test.ts +39 -38
- package/src/resources/extensions/gsd/tests/worktree-e2e.test.ts +21 -17
- package/src/resources/extensions/gsd/tests/worktree-health.test.ts +30 -25
- package/src/resources/extensions/gsd/tests/worktree-integration.test.ts +37 -30
- package/src/resources/extensions/gsd/tests/worktree-symlink-removal.test.ts +22 -15
- package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +66 -59
- package/src/resources/extensions/gsd/tests/worktree.test.ts +50 -44
- package/dist/web/standalone/.next/static/chunks/app/page-b9367c5ae13b99c6.js +0 -1
- package/dist/web/standalone/.next/static/chunks/main-app-fdab67f7802d7832.js +0 -1
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +0 -1
- package/src/resources/extensions/gsd/tests/quick-auto-guard.test.ts +0 -100
- package/src/resources/extensions/gsd/tests/sqlite-unavailable-gate.test.ts +0 -63
- /package/dist/web/standalone/.next/static/{-zps1Q9mQmioAKLcQiCr8 → mgkxN0mGP6gSUmGPEzbk_}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{-zps1Q9mQmioAKLcQiCr8 → mgkxN0mGP6gSUmGPEzbk_}/_ssgManifest.js +0 -0
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import { describe, test } from 'node:test';
|
|
2
|
-
import assert from 'node:assert/strict';
|
|
3
1
|
// gsd-tools — Structured LLM tool tests
|
|
4
2
|
//
|
|
5
3
|
// Tests the three registered tools: gsd_decision_save, gsd_requirement_update, gsd_summary_save.
|
|
6
4
|
// Each tool is tested via direct function invocation against an in-memory DB.
|
|
7
5
|
|
|
6
|
+
import { createTestContext } from './test-helpers.ts';
|
|
8
7
|
import * as path from 'node:path';
|
|
9
8
|
import * as os from 'node:os';
|
|
10
9
|
import * as fs from 'node:fs';
|
|
@@ -26,6 +25,8 @@ import {
|
|
|
26
25
|
} from '../db-writer.ts';
|
|
27
26
|
import type { Requirement } from '../types.ts';
|
|
28
27
|
|
|
28
|
+
const { assertEq, assertTrue, assertMatch, report } = createTestContext();
|
|
29
|
+
|
|
29
30
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
30
31
|
// Helpers
|
|
31
32
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
@@ -45,249 +46,281 @@ function cleanupDir(dir: string): void {
|
|
|
45
46
|
/**
|
|
46
47
|
* Simulate tool execute by calling the underlying DB functions directly.
|
|
47
48
|
* The actual tool registration happens in index.ts; here we test the
|
|
48
|
-
* execute logic pattern: check DB
|
|
49
|
+
* execute logic pattern: check DB → call writer → return result.
|
|
49
50
|
*/
|
|
50
51
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
try {
|
|
55
|
-
const dbPath = path.join(tmpDir, '.gsd', 'gsd.db');
|
|
56
|
-
openDatabase(dbPath);
|
|
57
|
-
assert.ok(isDbAvailable(), 'DB should be available after open');
|
|
58
|
-
|
|
59
|
-
// (a) Decision tool creates DB row + returns new ID
|
|
60
|
-
const result = await saveDecisionToDb(
|
|
61
|
-
{
|
|
62
|
-
scope: 'architecture',
|
|
63
|
-
decision: 'Use SQLite for metadata',
|
|
64
|
-
choice: 'SQLite',
|
|
65
|
-
rationale: 'Sync API fits the CLI model',
|
|
66
|
-
revisable: 'Yes',
|
|
67
|
-
when_context: 'M001',
|
|
68
|
-
},
|
|
69
|
-
tmpDir,
|
|
70
|
-
);
|
|
52
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
53
|
+
// gsd_decision_save tool tests
|
|
54
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
71
55
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
// Verify DB row exists
|
|
75
|
-
const row = getDecisionById('D001');
|
|
76
|
-
assert.ok(row !== null, 'Decision D001 should exist in DB');
|
|
77
|
-
assert.deepStrictEqual(row!.scope, 'architecture', 'Decision scope should match');
|
|
78
|
-
assert.deepStrictEqual(row!.decision, 'Use SQLite for metadata', 'Decision text should match');
|
|
79
|
-
assert.deepStrictEqual(row!.choice, 'SQLite', 'Decision choice should match');
|
|
80
|
-
|
|
81
|
-
// Verify DECISIONS.md was generated
|
|
82
|
-
const mdPath = path.join(tmpDir, '.gsd', 'DECISIONS.md');
|
|
83
|
-
assert.ok(fs.existsSync(mdPath), 'DECISIONS.md should be created');
|
|
84
|
-
const mdContent = fs.readFileSync(mdPath, 'utf-8');
|
|
85
|
-
assert.ok(mdContent.includes('D001'), 'DECISIONS.md should contain D001');
|
|
86
|
-
assert.ok(mdContent.includes('SQLite'), 'DECISIONS.md should contain choice');
|
|
87
|
-
|
|
88
|
-
// (e) Decision tool auto-assigns correct next ID
|
|
89
|
-
const result2 = await saveDecisionToDb(
|
|
90
|
-
{
|
|
91
|
-
scope: 'testing',
|
|
92
|
-
decision: 'Test runner',
|
|
93
|
-
choice: 'vitest',
|
|
94
|
-
rationale: 'Fast and ESM-native',
|
|
95
|
-
},
|
|
96
|
-
tmpDir,
|
|
97
|
-
);
|
|
98
|
-
assert.deepStrictEqual(result2.id, 'D002', 'Second decision should be D002');
|
|
99
|
-
|
|
100
|
-
const result3 = await saveDecisionToDb(
|
|
101
|
-
{
|
|
102
|
-
scope: 'CI',
|
|
103
|
-
decision: 'CI platform',
|
|
104
|
-
choice: 'GitHub Actions',
|
|
105
|
-
rationale: 'Integrated with repo',
|
|
106
|
-
},
|
|
107
|
-
tmpDir,
|
|
108
|
-
);
|
|
109
|
-
assert.deepStrictEqual(result3.id, 'D003', 'Third decision should be D003');
|
|
56
|
+
console.log('\n── gsd_decision_save ──');
|
|
110
57
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
58
|
+
{
|
|
59
|
+
const tmpDir = makeTmpDir();
|
|
60
|
+
try {
|
|
61
|
+
const dbPath = path.join(tmpDir, '.gsd', 'gsd.db');
|
|
62
|
+
openDatabase(dbPath);
|
|
63
|
+
assertTrue(isDbAvailable(), 'DB should be available after open');
|
|
64
|
+
|
|
65
|
+
// (a) Decision tool creates DB row + returns new ID
|
|
66
|
+
const result = await saveDecisionToDb(
|
|
67
|
+
{
|
|
68
|
+
scope: 'architecture',
|
|
69
|
+
decision: 'Use SQLite for metadata',
|
|
70
|
+
choice: 'SQLite',
|
|
71
|
+
rationale: 'Sync API fits the CLI model',
|
|
72
|
+
revisable: 'Yes',
|
|
73
|
+
when_context: 'M001',
|
|
74
|
+
},
|
|
75
|
+
tmpDir,
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
assertEq(result.id, 'D001', 'First decision should be D001');
|
|
79
|
+
|
|
80
|
+
// Verify DB row exists
|
|
81
|
+
const row = getDecisionById('D001');
|
|
82
|
+
assertTrue(row !== null, 'Decision D001 should exist in DB');
|
|
83
|
+
assertEq(row!.scope, 'architecture', 'Decision scope should match');
|
|
84
|
+
assertEq(row!.decision, 'Use SQLite for metadata', 'Decision text should match');
|
|
85
|
+
assertEq(row!.choice, 'SQLite', 'Decision choice should match');
|
|
86
|
+
|
|
87
|
+
// Verify DECISIONS.md was generated
|
|
88
|
+
const mdPath = path.join(tmpDir, '.gsd', 'DECISIONS.md');
|
|
89
|
+
assertTrue(fs.existsSync(mdPath), 'DECISIONS.md should be created');
|
|
90
|
+
const mdContent = fs.readFileSync(mdPath, 'utf-8');
|
|
91
|
+
assertTrue(mdContent.includes('D001'), 'DECISIONS.md should contain D001');
|
|
92
|
+
assertTrue(mdContent.includes('SQLite'), 'DECISIONS.md should contain choice');
|
|
93
|
+
|
|
94
|
+
// (e) Decision tool auto-assigns correct next ID
|
|
95
|
+
const result2 = await saveDecisionToDb(
|
|
96
|
+
{
|
|
97
|
+
scope: 'testing',
|
|
98
|
+
decision: 'Test runner',
|
|
99
|
+
choice: 'vitest',
|
|
100
|
+
rationale: 'Fast and ESM-native',
|
|
101
|
+
},
|
|
102
|
+
tmpDir,
|
|
103
|
+
);
|
|
104
|
+
assertEq(result2.id, 'D002', 'Second decision should be D002');
|
|
105
|
+
|
|
106
|
+
const result3 = await saveDecisionToDb(
|
|
107
|
+
{
|
|
108
|
+
scope: 'CI',
|
|
109
|
+
decision: 'CI platform',
|
|
110
|
+
choice: 'GitHub Actions',
|
|
111
|
+
rationale: 'Integrated with repo',
|
|
112
|
+
},
|
|
113
|
+
tmpDir,
|
|
114
|
+
);
|
|
115
|
+
assertEq(result3.id, 'D003', 'Third decision should be D003');
|
|
116
|
+
|
|
117
|
+
closeDatabase();
|
|
118
|
+
} finally {
|
|
119
|
+
cleanupDir(tmpDir);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
116
122
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
const dbPath = path.join(tmpDir, '.gsd', 'gsd.db');
|
|
121
|
-
openDatabase(dbPath);
|
|
122
|
-
|
|
123
|
-
// Seed a requirement
|
|
124
|
-
const seedReq: Requirement = {
|
|
125
|
-
id: 'R001',
|
|
126
|
-
class: 'functional',
|
|
127
|
-
status: 'active',
|
|
128
|
-
description: 'Must support SQLite storage',
|
|
129
|
-
why: 'Structured data needs',
|
|
130
|
-
source: 'design',
|
|
131
|
-
primary_owner: 'S03',
|
|
132
|
-
supporting_slices: '',
|
|
133
|
-
validation: '',
|
|
134
|
-
notes: '',
|
|
135
|
-
full_content: '',
|
|
136
|
-
superseded_by: null,
|
|
137
|
-
};
|
|
138
|
-
upsertRequirement(seedReq);
|
|
139
|
-
|
|
140
|
-
// (b) Requirement update tool modifies existing requirement
|
|
141
|
-
await updateRequirementInDb(
|
|
142
|
-
'R001',
|
|
143
|
-
{ status: 'validated', validation: 'Unit tests pass', notes: 'Verified in S06' },
|
|
144
|
-
tmpDir,
|
|
145
|
-
);
|
|
123
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
124
|
+
// gsd_requirement_update tool tests
|
|
125
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
146
126
|
|
|
147
|
-
|
|
148
|
-
assert.ok(updated !== null, 'R001 should still exist');
|
|
149
|
-
assert.deepStrictEqual(updated!.status, 'validated', 'Status should be updated');
|
|
150
|
-
assert.deepStrictEqual(updated!.validation, 'Unit tests pass', 'Validation should be updated');
|
|
151
|
-
assert.deepStrictEqual(updated!.notes, 'Verified in S06', 'Notes should be updated');
|
|
152
|
-
// Original fields preserved
|
|
153
|
-
assert.deepStrictEqual(updated!.description, 'Must support SQLite storage', 'Description should be preserved');
|
|
154
|
-
assert.deepStrictEqual(updated!.primary_owner, 'S03', 'Primary owner should be preserved');
|
|
155
|
-
|
|
156
|
-
// Verify REQUIREMENTS.md was generated
|
|
157
|
-
const mdPath = path.join(tmpDir, '.gsd', 'REQUIREMENTS.md');
|
|
158
|
-
assert.ok(fs.existsSync(mdPath), 'REQUIREMENTS.md should be created');
|
|
159
|
-
const mdContent = fs.readFileSync(mdPath, 'utf-8');
|
|
160
|
-
assert.ok(mdContent.includes('R001'), 'REQUIREMENTS.md should contain R001');
|
|
161
|
-
assert.ok(mdContent.includes('validated'), 'REQUIREMENTS.md should reflect updated status');
|
|
162
|
-
|
|
163
|
-
// Updating non-existent requirement throws
|
|
164
|
-
let threwForMissing = false;
|
|
165
|
-
try {
|
|
166
|
-
await updateRequirementInDb('R999', { status: 'deferred' }, tmpDir);
|
|
167
|
-
} catch (err) {
|
|
168
|
-
threwForMissing = true;
|
|
169
|
-
assert.ok(
|
|
170
|
-
(err as Error).message.includes('R999'),
|
|
171
|
-
'Error should mention the missing requirement ID',
|
|
172
|
-
);
|
|
173
|
-
}
|
|
174
|
-
assert.ok(threwForMissing, 'Should throw for non-existent requirement');
|
|
175
|
-
|
|
176
|
-
closeDatabase();
|
|
177
|
-
} finally {
|
|
178
|
-
cleanupDir(tmpDir);
|
|
179
|
-
}
|
|
180
|
-
});
|
|
127
|
+
console.log('\n── gsd_requirement_update ──');
|
|
181
128
|
|
|
182
|
-
|
|
183
|
-
|
|
129
|
+
{
|
|
130
|
+
const tmpDir = makeTmpDir();
|
|
131
|
+
try {
|
|
132
|
+
const dbPath = path.join(tmpDir, '.gsd', 'gsd.db');
|
|
133
|
+
openDatabase(dbPath);
|
|
134
|
+
|
|
135
|
+
// Seed a requirement
|
|
136
|
+
const seedReq: Requirement = {
|
|
137
|
+
id: 'R001',
|
|
138
|
+
class: 'functional',
|
|
139
|
+
status: 'active',
|
|
140
|
+
description: 'Must support SQLite storage',
|
|
141
|
+
why: 'Structured data needs',
|
|
142
|
+
source: 'design',
|
|
143
|
+
primary_owner: 'S03',
|
|
144
|
+
supporting_slices: '',
|
|
145
|
+
validation: '',
|
|
146
|
+
notes: '',
|
|
147
|
+
full_content: '',
|
|
148
|
+
superseded_by: null,
|
|
149
|
+
};
|
|
150
|
+
upsertRequirement(seedReq);
|
|
151
|
+
|
|
152
|
+
// (b) Requirement update tool modifies existing requirement
|
|
153
|
+
await updateRequirementInDb(
|
|
154
|
+
'R001',
|
|
155
|
+
{ status: 'validated', validation: 'Unit tests pass', notes: 'Verified in S06' },
|
|
156
|
+
tmpDir,
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
const updated = getRequirementById('R001');
|
|
160
|
+
assertTrue(updated !== null, 'R001 should still exist');
|
|
161
|
+
assertEq(updated!.status, 'validated', 'Status should be updated');
|
|
162
|
+
assertEq(updated!.validation, 'Unit tests pass', 'Validation should be updated');
|
|
163
|
+
assertEq(updated!.notes, 'Verified in S06', 'Notes should be updated');
|
|
164
|
+
// Original fields preserved
|
|
165
|
+
assertEq(updated!.description, 'Must support SQLite storage', 'Description should be preserved');
|
|
166
|
+
assertEq(updated!.primary_owner, 'S03', 'Primary owner should be preserved');
|
|
167
|
+
|
|
168
|
+
// Verify REQUIREMENTS.md was generated
|
|
169
|
+
const mdPath = path.join(tmpDir, '.gsd', 'REQUIREMENTS.md');
|
|
170
|
+
assertTrue(fs.existsSync(mdPath), 'REQUIREMENTS.md should be created');
|
|
171
|
+
const mdContent = fs.readFileSync(mdPath, 'utf-8');
|
|
172
|
+
assertTrue(mdContent.includes('R001'), 'REQUIREMENTS.md should contain R001');
|
|
173
|
+
assertTrue(mdContent.includes('validated'), 'REQUIREMENTS.md should reflect updated status');
|
|
174
|
+
|
|
175
|
+
// Updating non-existent requirement throws
|
|
176
|
+
let threwForMissing = false;
|
|
184
177
|
try {
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
path: 'milestones/M001/slices/S01/S01-SUMMARY.md',
|
|
192
|
-
artifact_type: 'SUMMARY',
|
|
193
|
-
content: '# S01 Summary\n\nThis is a test summary.',
|
|
194
|
-
milestone_id: 'M001',
|
|
195
|
-
slice_id: 'S01',
|
|
196
|
-
},
|
|
197
|
-
tmpDir,
|
|
178
|
+
await updateRequirementInDb('R999', { status: 'deferred' }, tmpDir);
|
|
179
|
+
} catch (err) {
|
|
180
|
+
threwForMissing = true;
|
|
181
|
+
assertTrue(
|
|
182
|
+
(err as Error).message.includes('R999'),
|
|
183
|
+
'Error should mention the missing requirement ID',
|
|
198
184
|
);
|
|
185
|
+
}
|
|
186
|
+
assertTrue(threwForMissing, 'Should throw for non-existent requirement');
|
|
199
187
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
).all();
|
|
206
|
-
assert.deepStrictEqual(rows.length, 1, 'Should have 1 artifact row');
|
|
207
|
-
assert.deepStrictEqual(rows[0]['artifact_type'] as string, 'SUMMARY', 'Artifact type should be SUMMARY');
|
|
208
|
-
assert.deepStrictEqual(rows[0]['milestone_id'] as string, 'M001', 'Milestone ID should match');
|
|
209
|
-
assert.deepStrictEqual(rows[0]['slice_id'] as string, 'S01', 'Slice ID should match');
|
|
210
|
-
|
|
211
|
-
// Verify file was written to disk
|
|
212
|
-
const filePath = path.join(tmpDir, '.gsd', 'milestones', 'M001', 'slices', 'S01', 'S01-SUMMARY.md');
|
|
213
|
-
assert.ok(fs.existsSync(filePath), 'Summary file should be written to disk');
|
|
214
|
-
const fileContent = fs.readFileSync(filePath, 'utf-8');
|
|
215
|
-
assert.ok(fileContent.includes('S01 Summary'), 'File should contain summary content');
|
|
216
|
-
|
|
217
|
-
// Test milestone-level artifact (no slice_id)
|
|
218
|
-
await saveArtifactToDb(
|
|
219
|
-
{
|
|
220
|
-
path: 'milestones/M001/M001-CONTEXT.md',
|
|
221
|
-
artifact_type: 'CONTEXT',
|
|
222
|
-
content: '# M001 Context\n\nContext notes.',
|
|
223
|
-
milestone_id: 'M001',
|
|
224
|
-
},
|
|
225
|
-
tmpDir,
|
|
226
|
-
);
|
|
188
|
+
closeDatabase();
|
|
189
|
+
} finally {
|
|
190
|
+
cleanupDir(tmpDir);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
227
193
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
// Test task-level artifact
|
|
232
|
-
await saveArtifactToDb(
|
|
233
|
-
{
|
|
234
|
-
path: 'milestones/M001/slices/S01/tasks/T01-SUMMARY.md',
|
|
235
|
-
artifact_type: 'SUMMARY',
|
|
236
|
-
content: '# T01 Summary\n\nTask summary.',
|
|
237
|
-
milestone_id: 'M001',
|
|
238
|
-
slice_id: 'S01',
|
|
239
|
-
task_id: 'T01',
|
|
240
|
-
},
|
|
241
|
-
tmpDir,
|
|
242
|
-
);
|
|
194
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
195
|
+
// gsd_summary_save tool tests
|
|
196
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
243
197
|
|
|
244
|
-
|
|
245
|
-
assert.ok(fs.existsSync(tFilePath), 'Task-level artifact file should be created');
|
|
198
|
+
console.log('\n── gsd_summary_save ──');
|
|
246
199
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
200
|
+
{
|
|
201
|
+
const tmpDir = makeTmpDir();
|
|
202
|
+
try {
|
|
203
|
+
const dbPath = path.join(tmpDir, '.gsd', 'gsd.db');
|
|
204
|
+
openDatabase(dbPath);
|
|
205
|
+
|
|
206
|
+
// (c) Summary tool creates artifact row
|
|
207
|
+
await saveArtifactToDb(
|
|
208
|
+
{
|
|
209
|
+
path: 'milestones/M001/slices/S01/S01-SUMMARY.md',
|
|
210
|
+
artifact_type: 'SUMMARY',
|
|
211
|
+
content: '# S01 Summary\n\nThis is a test summary.',
|
|
212
|
+
milestone_id: 'M001',
|
|
213
|
+
slice_id: 'S01',
|
|
214
|
+
},
|
|
215
|
+
tmpDir,
|
|
216
|
+
);
|
|
217
|
+
|
|
218
|
+
// Verify artifact in DB
|
|
219
|
+
const adapter = _getAdapter();
|
|
220
|
+
assertTrue(adapter !== null, 'Adapter should be available');
|
|
221
|
+
const rows = adapter!.prepare(
|
|
222
|
+
"SELECT * FROM artifacts WHERE path = 'milestones/M001/slices/S01/S01-SUMMARY.md'",
|
|
223
|
+
).all();
|
|
224
|
+
assertEq(rows.length, 1, 'Should have 1 artifact row');
|
|
225
|
+
assertEq(rows[0]['artifact_type'] as string, 'SUMMARY', 'Artifact type should be SUMMARY');
|
|
226
|
+
assertEq(rows[0]['milestone_id'] as string, 'M001', 'Milestone ID should match');
|
|
227
|
+
assertEq(rows[0]['slice_id'] as string, 'S01', 'Slice ID should match');
|
|
228
|
+
|
|
229
|
+
// Verify file was written to disk
|
|
230
|
+
const filePath = path.join(tmpDir, '.gsd', 'milestones', 'M001', 'slices', 'S01', 'S01-SUMMARY.md');
|
|
231
|
+
assertTrue(fs.existsSync(filePath), 'Summary file should be written to disk');
|
|
232
|
+
const fileContent = fs.readFileSync(filePath, 'utf-8');
|
|
233
|
+
assertTrue(fileContent.includes('S01 Summary'), 'File should contain summary content');
|
|
234
|
+
|
|
235
|
+
// Test milestone-level artifact (no slice_id)
|
|
236
|
+
await saveArtifactToDb(
|
|
237
|
+
{
|
|
238
|
+
path: 'milestones/M001/M001-CONTEXT.md',
|
|
239
|
+
artifact_type: 'CONTEXT',
|
|
240
|
+
content: '# M001 Context\n\nContext notes.',
|
|
241
|
+
milestone_id: 'M001',
|
|
242
|
+
},
|
|
243
|
+
tmpDir,
|
|
244
|
+
);
|
|
245
|
+
|
|
246
|
+
const mFilePath = path.join(tmpDir, '.gsd', 'milestones', 'M001', 'M001-CONTEXT.md');
|
|
247
|
+
assertTrue(fs.existsSync(mFilePath), 'Milestone-level artifact file should be created');
|
|
248
|
+
|
|
249
|
+
// Test task-level artifact
|
|
250
|
+
await saveArtifactToDb(
|
|
251
|
+
{
|
|
252
|
+
path: 'milestones/M001/slices/S01/tasks/T01-SUMMARY.md',
|
|
253
|
+
artifact_type: 'SUMMARY',
|
|
254
|
+
content: '# T01 Summary\n\nTask summary.',
|
|
255
|
+
milestone_id: 'M001',
|
|
256
|
+
slice_id: 'S01',
|
|
257
|
+
task_id: 'T01',
|
|
258
|
+
},
|
|
259
|
+
tmpDir,
|
|
260
|
+
);
|
|
261
|
+
|
|
262
|
+
const tFilePath = path.join(tmpDir, '.gsd', 'milestones', 'M001', 'slices', 'S01', 'tasks', 'T01-SUMMARY.md');
|
|
263
|
+
assertTrue(fs.existsSync(tFilePath), 'Task-level artifact file should be created');
|
|
264
|
+
|
|
265
|
+
closeDatabase();
|
|
266
|
+
} finally {
|
|
267
|
+
cleanupDir(tmpDir);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
252
270
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
try { closeDatabase(); } catch { /* already closed */ }
|
|
271
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
272
|
+
// DB unavailable error paths
|
|
273
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
257
274
|
|
|
258
|
-
|
|
259
|
-
assert.ok(!isDbAvailable(), 'DB should be unavailable after close');
|
|
275
|
+
console.log('\n── DB unavailable error paths ──');
|
|
260
276
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
277
|
+
{
|
|
278
|
+
// (d) All tools return isError when DB unavailable
|
|
279
|
+
// Close any open DB and don't open a new one
|
|
280
|
+
try { closeDatabase(); } catch { /* already closed */ }
|
|
265
281
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
const result = await saveDecisionToDb(
|
|
274
|
-
{
|
|
275
|
-
scope: 'format-test',
|
|
276
|
-
decision: 'Test format',
|
|
277
|
-
choice: 'TypeBox',
|
|
278
|
-
rationale: 'Schema validation',
|
|
279
|
-
},
|
|
280
|
-
tmpDir,
|
|
281
|
-
);
|
|
282
|
+
// isDbAvailable() should return false
|
|
283
|
+
assertTrue(!isDbAvailable(), 'DB should be unavailable after close');
|
|
284
|
+
|
|
285
|
+
// nextDecisionId degrades gracefully
|
|
286
|
+
const fallbackId = await nextDecisionId();
|
|
287
|
+
assertEq(fallbackId, 'D001', 'nextDecisionId should return D001 when DB unavailable');
|
|
288
|
+
}
|
|
282
289
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
assert.match(result.id, /^D\d{3}$/, 'ID should match DXXX pattern');
|
|
290
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
291
|
+
// Tool result format verification
|
|
292
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
287
293
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
+
console.log('\n── Tool result format ──');
|
|
295
|
+
|
|
296
|
+
{
|
|
297
|
+
const tmpDir = makeTmpDir();
|
|
298
|
+
try {
|
|
299
|
+
const dbPath = path.join(tmpDir, '.gsd', 'gsd.db');
|
|
300
|
+
openDatabase(dbPath);
|
|
301
|
+
|
|
302
|
+
// Verify result follows AgentToolResult interface: {content: [{type: "text", text}], details}
|
|
303
|
+
const result = await saveDecisionToDb(
|
|
304
|
+
{
|
|
305
|
+
scope: 'format-test',
|
|
306
|
+
decision: 'Test format',
|
|
307
|
+
choice: 'TypeBox',
|
|
308
|
+
rationale: 'Schema validation',
|
|
309
|
+
},
|
|
310
|
+
tmpDir,
|
|
311
|
+
);
|
|
312
|
+
|
|
313
|
+
// The saveDecisionToDb returns {id} — the tool wrapping adds the AgentToolResult shape.
|
|
314
|
+
// Verify the raw function returns the expected shape.
|
|
315
|
+
assertTrue(typeof result.id === 'string', 'saveDecisionToDb should return {id: string}');
|
|
316
|
+
assertMatch(result.id, /^D\d{3}$/, 'ID should match DXXX pattern');
|
|
317
|
+
|
|
318
|
+
closeDatabase();
|
|
319
|
+
} finally {
|
|
320
|
+
cleanupDir(tmpDir);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
325
|
+
|
|
326
|
+
report();
|
|
@@ -23,7 +23,7 @@ function makeTempDir(prefix: string): string {
|
|
|
23
23
|
// loadAndValidateAnswerFile
|
|
24
24
|
// ---------------------------------------------------------------------------
|
|
25
25
|
|
|
26
|
-
test('loadAndValidateAnswerFile — valid file', (
|
|
26
|
+
test('loadAndValidateAnswerFile — valid file', () => {
|
|
27
27
|
const tmp = makeTempDir('answers-valid');
|
|
28
28
|
try {
|
|
29
29
|
const data = {
|
|
@@ -43,7 +43,7 @@ test('loadAndValidateAnswerFile — valid file', (t) => {
|
|
|
43
43
|
}
|
|
44
44
|
});
|
|
45
45
|
|
|
46
|
-
test('loadAndValidateAnswerFile — invalid JSON', (
|
|
46
|
+
test('loadAndValidateAnswerFile — invalid JSON', () => {
|
|
47
47
|
const tmp = makeTempDir('answers-bad-json');
|
|
48
48
|
try {
|
|
49
49
|
const filePath = join(tmp, 'answers.json');
|
|
@@ -58,7 +58,7 @@ test('loadAndValidateAnswerFile — invalid JSON', (t) => {
|
|
|
58
58
|
}
|
|
59
59
|
});
|
|
60
60
|
|
|
61
|
-
test('loadAndValidateAnswerFile — wrong types (non-string question value)', (
|
|
61
|
+
test('loadAndValidateAnswerFile — wrong types (non-string question value)', () => {
|
|
62
62
|
const tmp = makeTempDir('answers-bad-q');
|
|
63
63
|
try {
|
|
64
64
|
const filePath = join(tmp, 'answers.json');
|
|
@@ -73,7 +73,7 @@ test('loadAndValidateAnswerFile — wrong types (non-string question value)', (t
|
|
|
73
73
|
}
|
|
74
74
|
});
|
|
75
75
|
|
|
76
|
-
test('loadAndValidateAnswerFile — wrong types (non-string secret value)', (
|
|
76
|
+
test('loadAndValidateAnswerFile — wrong types (non-string secret value)', () => {
|
|
77
77
|
const tmp = makeTempDir('answers-bad-secret');
|
|
78
78
|
try {
|
|
79
79
|
const filePath = join(tmp, 'answers.json');
|
|
@@ -116,7 +116,7 @@ function makeSelectEvent(
|
|
|
116
116
|
};
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
-
test('observeEvent stores metadata', (
|
|
119
|
+
test('observeEvent stores metadata', () => {
|
|
120
120
|
const injector = new AnswerInjector({});
|
|
121
121
|
|
|
122
122
|
injector.observeEvent(makeToolExecutionStart([{
|
|
@@ -140,7 +140,7 @@ test('observeEvent stores metadata', (t) => {
|
|
|
140
140
|
assert.strictEqual(injector.getStats().questionsDefaulted, 1);
|
|
141
141
|
});
|
|
142
142
|
|
|
143
|
-
test('tryHandle matches by question ID — single select', (
|
|
143
|
+
test('tryHandle matches by question ID — single select', () => {
|
|
144
144
|
const injector = new AnswerInjector({ questions: { deploy_target: 'GCP' } });
|
|
145
145
|
|
|
146
146
|
injector.observeEvent(makeToolExecutionStart([{
|
|
@@ -164,7 +164,7 @@ test('tryHandle matches by question ID — single select', (t) => {
|
|
|
164
164
|
assert.strictEqual(injector.getStats().questionsAnswered, 1);
|
|
165
165
|
});
|
|
166
166
|
|
|
167
|
-
test('tryHandle unknown question deferred — first_option timeout', async (
|
|
167
|
+
test('tryHandle unknown question deferred — first_option timeout', async () => {
|
|
168
168
|
const injector = new AnswerInjector({ defaults: { strategy: 'first_option' } });
|
|
169
169
|
|
|
170
170
|
const captured: string[] = [];
|
|
@@ -188,7 +188,7 @@ test('tryHandle unknown question deferred — first_option timeout', async (t) =
|
|
|
188
188
|
assert.strictEqual(injector.getStats().questionsDefaulted, 1);
|
|
189
189
|
});
|
|
190
190
|
|
|
191
|
-
test('tryHandle multi-select', (
|
|
191
|
+
test('tryHandle multi-select', () => {
|
|
192
192
|
const injector = new AnswerInjector({ questions: { features: ['auth', 'payments'] } });
|
|
193
193
|
|
|
194
194
|
injector.observeEvent(makeToolExecutionStart([{
|
|
@@ -218,7 +218,7 @@ test('tryHandle multi-select', (t) => {
|
|
|
218
218
|
assert.strictEqual(injector.getStats().questionsAnswered, 1);
|
|
219
219
|
});
|
|
220
220
|
|
|
221
|
-
test('tryHandle answer not in options — first_option strategy returns false', (
|
|
221
|
+
test('tryHandle answer not in options — first_option strategy returns false', () => {
|
|
222
222
|
const injector = new AnswerInjector({ questions: { deploy_target: 'Azure' } });
|
|
223
223
|
|
|
224
224
|
injector.observeEvent(makeToolExecutionStart([{
|
|
@@ -240,7 +240,7 @@ test('tryHandle answer not in options — first_option strategy returns false',
|
|
|
240
240
|
assert.strictEqual(injector.getStats().questionsAnswered, 0);
|
|
241
241
|
});
|
|
242
242
|
|
|
243
|
-
test('tryHandle deferred resolution — observeEvent after tryHandle', async (
|
|
243
|
+
test('tryHandle deferred resolution — observeEvent after tryHandle', async () => {
|
|
244
244
|
const injector = new AnswerInjector({ questions: { deploy_target: 'GCP' } });
|
|
245
245
|
|
|
246
246
|
const captured: string[] = [];
|
|
@@ -272,7 +272,7 @@ test('tryHandle deferred resolution — observeEvent after tryHandle', async (t)
|
|
|
272
272
|
// AnswerInjector — getSecretEnvVars
|
|
273
273
|
// ---------------------------------------------------------------------------
|
|
274
274
|
|
|
275
|
-
test('getSecretEnvVars returns secrets map', (
|
|
275
|
+
test('getSecretEnvVars returns secrets map', () => {
|
|
276
276
|
const secrets = { API_KEY: 'sk-123', DB_URL: 'postgres://localhost/db' };
|
|
277
277
|
const injector = new AnswerInjector({ secrets });
|
|
278
278
|
|
|
@@ -283,7 +283,7 @@ test('getSecretEnvVars returns secrets map', (t) => {
|
|
|
283
283
|
// AnswerInjector — getUnusedWarnings
|
|
284
284
|
// ---------------------------------------------------------------------------
|
|
285
285
|
|
|
286
|
-
test('getUnusedWarnings reports unused question IDs and secret keys', (
|
|
286
|
+
test('getUnusedWarnings reports unused question IDs and secret keys', () => {
|
|
287
287
|
const injector = new AnswerInjector({
|
|
288
288
|
questions: { q1: 'val1', q2: 'val2' },
|
|
289
289
|
secrets: { KEY1: 'v1' },
|
|
@@ -314,7 +314,7 @@ test('getUnusedWarnings reports unused question IDs and secret keys', (t) => {
|
|
|
314
314
|
// AnswerInjector — defaults.strategy cancel
|
|
315
315
|
// ---------------------------------------------------------------------------
|
|
316
316
|
|
|
317
|
-
test('defaults.strategy cancel — sends cancelled response', (
|
|
317
|
+
test('defaults.strategy cancel — sends cancelled response', () => {
|
|
318
318
|
const injector = new AnswerInjector({ defaults: { strategy: 'cancel' } });
|
|
319
319
|
|
|
320
320
|
injector.observeEvent(makeToolExecutionStart([{
|