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,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import assert from 'node:assert/strict';
|
|
1
|
+
import { createTestContext } from './test-helpers.ts';
|
|
3
2
|
import * as fs from 'node:fs';
|
|
4
3
|
import * as path from 'node:path';
|
|
5
4
|
import * as os from 'node:os';
|
|
@@ -17,6 +16,7 @@ import {
|
|
|
17
16
|
reconcileWorktreeDb,
|
|
18
17
|
} from '../gsd-db.ts';
|
|
19
18
|
|
|
19
|
+
const { assertEq, assertTrue, report } = createTestContext();
|
|
20
20
|
|
|
21
21
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
22
22
|
// Helpers
|
|
@@ -91,18 +91,18 @@ console.log('\n=== worktree-db: copyWorktreeDb ===');
|
|
|
91
91
|
closeDatabase();
|
|
92
92
|
|
|
93
93
|
const result = copyWorktreeDb(srcDb, destDb);
|
|
94
|
-
|
|
95
|
-
|
|
94
|
+
assertTrue(result === true, 'copyWorktreeDb returns true on success');
|
|
95
|
+
assertTrue(fs.existsSync(destDb), 'dest DB file exists after copy');
|
|
96
96
|
|
|
97
97
|
// Open the copy and verify data is queryable
|
|
98
98
|
openDatabase(destDb);
|
|
99
99
|
const d = getDecisionById('D001');
|
|
100
|
-
|
|
101
|
-
|
|
100
|
+
assertTrue(d !== null, 'decision queryable in copied DB');
|
|
101
|
+
assertEq(d?.choice, 'node:sqlite', 'decision data preserved in copy');
|
|
102
102
|
|
|
103
103
|
const r = getRequirementById('R001');
|
|
104
|
-
|
|
105
|
-
|
|
104
|
+
assertTrue(r !== null, 'requirement queryable in copied DB');
|
|
105
|
+
assertEq(r?.description, 'Must store decisions', 'requirement data preserved in copy');
|
|
106
106
|
|
|
107
107
|
cleanup(srcDir, destDir);
|
|
108
108
|
}
|
|
@@ -123,9 +123,9 @@ console.log('\n=== worktree-db: copyWorktreeDb ===');
|
|
|
123
123
|
|
|
124
124
|
copyWorktreeDb(srcDb, destDb);
|
|
125
125
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
126
|
+
assertTrue(fs.existsSync(destDb), 'DB file copied');
|
|
127
|
+
assertTrue(!fs.existsSync(destDb + '-wal'), 'WAL file NOT copied');
|
|
128
|
+
assertTrue(!fs.existsSync(destDb + '-shm'), 'SHM file NOT copied');
|
|
129
129
|
|
|
130
130
|
cleanup(srcDir, destDir);
|
|
131
131
|
}
|
|
@@ -134,7 +134,7 @@ console.log('\n=== worktree-db: copyWorktreeDb ===');
|
|
|
134
134
|
{
|
|
135
135
|
const destDir = tempDir();
|
|
136
136
|
const result = copyWorktreeDb('/nonexistent/path/gsd.db', path.join(destDir, 'gsd.db'));
|
|
137
|
-
|
|
137
|
+
assertEq(result, false, 'returns false for missing source');
|
|
138
138
|
cleanup(destDir);
|
|
139
139
|
}
|
|
140
140
|
|
|
@@ -149,8 +149,8 @@ console.log('\n=== worktree-db: copyWorktreeDb ===');
|
|
|
149
149
|
closeDatabase();
|
|
150
150
|
|
|
151
151
|
const result = copyWorktreeDb(srcDb, deepDest);
|
|
152
|
-
|
|
153
|
-
|
|
152
|
+
assertTrue(result === true, 'copyWorktreeDb succeeds with nested dest');
|
|
153
|
+
assertTrue(fs.existsSync(deepDest), 'DB file created at deeply nested path');
|
|
154
154
|
|
|
155
155
|
cleanup(srcDir, destDir);
|
|
156
156
|
}
|
|
@@ -192,10 +192,10 @@ console.log('\n=== worktree-db: reconcileWorktreeDb ===');
|
|
|
192
192
|
openDatabase(mainDb);
|
|
193
193
|
const result = reconcileWorktreeDb(mainDb, wtDb);
|
|
194
194
|
|
|
195
|
-
|
|
195
|
+
assertTrue(result.decisions > 0, 'decisions merged count > 0');
|
|
196
196
|
const d2 = getDecisionById('D002');
|
|
197
|
-
|
|
198
|
-
|
|
197
|
+
assertTrue(d2 !== null, 'D002 from worktree now in main');
|
|
198
|
+
assertEq(d2?.choice, 'WAL', 'D002 data correct after merge');
|
|
199
199
|
|
|
200
200
|
cleanup(mainDir, wtDir);
|
|
201
201
|
}
|
|
@@ -231,10 +231,10 @@ console.log('\n=== worktree-db: reconcileWorktreeDb ===');
|
|
|
231
231
|
openDatabase(mainDb);
|
|
232
232
|
const result = reconcileWorktreeDb(mainDb, wtDb);
|
|
233
233
|
|
|
234
|
-
|
|
234
|
+
assertTrue(result.requirements > 0, 'requirements merged count > 0');
|
|
235
235
|
const r2 = getRequirementById('R002');
|
|
236
|
-
|
|
237
|
-
|
|
236
|
+
assertTrue(r2 !== null, 'R002 from worktree now in main');
|
|
237
|
+
assertEq(r2?.description, 'Must be fast', 'R002 data correct after merge');
|
|
238
238
|
|
|
239
239
|
cleanup(mainDir, wtDir);
|
|
240
240
|
}
|
|
@@ -264,11 +264,11 @@ console.log('\n=== worktree-db: reconcileWorktreeDb ===');
|
|
|
264
264
|
openDatabase(mainDb);
|
|
265
265
|
const result = reconcileWorktreeDb(mainDb, wtDb);
|
|
266
266
|
|
|
267
|
-
|
|
267
|
+
assertTrue(result.artifacts > 0, 'artifacts merged count > 0');
|
|
268
268
|
const adapter = _getAdapter()!;
|
|
269
269
|
const row = adapter.prepare('SELECT * FROM artifacts WHERE path = ?').get('docs/api.md');
|
|
270
|
-
|
|
271
|
-
|
|
270
|
+
assertTrue(row !== null, 'artifact from worktree now in main');
|
|
271
|
+
assertEq(row?.['artifact_type'], 'reference', 'artifact data correct after merge');
|
|
272
272
|
|
|
273
273
|
cleanup(mainDir, wtDir);
|
|
274
274
|
}
|
|
@@ -305,15 +305,15 @@ console.log('\n=== worktree-db: reconcileWorktreeDb ===');
|
|
|
305
305
|
openDatabase(mainDb);
|
|
306
306
|
const result = reconcileWorktreeDb(mainDb, wtDb);
|
|
307
307
|
|
|
308
|
-
|
|
309
|
-
|
|
308
|
+
assertTrue(result.conflicts.length > 0, 'conflicts detected');
|
|
309
|
+
assertTrue(
|
|
310
310
|
result.conflicts.some(c => c.includes('D001')),
|
|
311
311
|
'conflict mentions D001',
|
|
312
312
|
);
|
|
313
313
|
|
|
314
314
|
// Worktree-wins: D001 should now have worktree's value
|
|
315
315
|
const d1 = getDecisionById('D001');
|
|
316
|
-
|
|
316
|
+
assertEq(d1?.choice, 'sql.js', 'worktree wins on conflict (INSERT OR REPLACE)');
|
|
317
317
|
|
|
318
318
|
cleanup(mainDir, wtDir);
|
|
319
319
|
}
|
|
@@ -326,10 +326,10 @@ console.log('\n=== worktree-db: reconcileWorktreeDb ===');
|
|
|
326
326
|
seedMainDb(mainDb);
|
|
327
327
|
|
|
328
328
|
const result = reconcileWorktreeDb(mainDb, '/nonexistent/worktree.db');
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
329
|
+
assertEq(result.decisions, 0, 'no decisions merged for missing worktree DB');
|
|
330
|
+
assertEq(result.requirements, 0, 'no requirements merged for missing worktree DB');
|
|
331
|
+
assertEq(result.artifacts, 0, 'no artifacts merged for missing worktree DB');
|
|
332
|
+
assertEq(result.conflicts.length, 0, 'no conflicts for missing worktree DB');
|
|
333
333
|
|
|
334
334
|
cleanup(mainDir);
|
|
335
335
|
}
|
|
@@ -366,9 +366,9 @@ console.log('\n=== worktree-db: reconcileWorktreeDb ===');
|
|
|
366
366
|
|
|
367
367
|
openDatabase(mainDb);
|
|
368
368
|
const result = reconcileWorktreeDb(mainDb, wtDb);
|
|
369
|
-
|
|
369
|
+
assertTrue(result.decisions > 0, 'reconciliation works with spaces in path');
|
|
370
370
|
const d3 = getDecisionById('D003');
|
|
371
|
-
|
|
371
|
+
assertTrue(d3 !== null, 'D003 merged from worktree with spaces in path');
|
|
372
372
|
|
|
373
373
|
cleanup(baseDir);
|
|
374
374
|
}
|
|
@@ -388,7 +388,7 @@ console.log('\n=== worktree-db: reconcileWorktreeDb ===');
|
|
|
388
388
|
reconcileWorktreeDb(mainDb, wtDb);
|
|
389
389
|
|
|
390
390
|
// Verify main DB is still fully usable after DETACH
|
|
391
|
-
|
|
391
|
+
assertTrue(isDbAvailable(), 'DB still available after reconciliation');
|
|
392
392
|
|
|
393
393
|
insertDecision({
|
|
394
394
|
id: 'D099',
|
|
@@ -403,8 +403,8 @@ console.log('\n=== worktree-db: reconcileWorktreeDb ===');
|
|
|
403
403
|
});
|
|
404
404
|
|
|
405
405
|
const d99 = getDecisionById('D099');
|
|
406
|
-
|
|
407
|
-
|
|
406
|
+
assertTrue(d99 !== null, 'can insert and query after reconciliation');
|
|
407
|
+
assertEq(d99?.choice, 'works', 'post-reconcile data correct');
|
|
408
408
|
|
|
409
409
|
// Verify no "wt" database still attached
|
|
410
410
|
const adapter = _getAdapter()!;
|
|
@@ -415,7 +415,7 @@ console.log('\n=== worktree-db: reconcileWorktreeDb ===');
|
|
|
415
415
|
} catch {
|
|
416
416
|
// Expected — wt should be detached
|
|
417
417
|
}
|
|
418
|
-
|
|
418
|
+
assertTrue(!wtAccessible, 'wt database is detached after reconciliation');
|
|
419
419
|
|
|
420
420
|
cleanup(mainDir, wtDir);
|
|
421
421
|
}
|
|
@@ -436,10 +436,11 @@ console.log('\n=== worktree-db: reconcileWorktreeDb ===');
|
|
|
436
436
|
const result = reconcileWorktreeDb(mainDb, wtDb);
|
|
437
437
|
|
|
438
438
|
// Should still report counts for the existing rows (INSERT OR REPLACE touches them)
|
|
439
|
-
|
|
440
|
-
|
|
439
|
+
assertTrue(result.conflicts.length === 0, 'no conflicts when DBs are identical');
|
|
440
|
+
assertTrue(isDbAvailable(), 'DB usable after no-change reconciliation');
|
|
441
441
|
|
|
442
442
|
cleanup(mainDir, wtDir);
|
|
443
443
|
}
|
|
444
444
|
|
|
445
445
|
// ─── Final Report ──────────────────────────────────────────────────────────
|
|
446
|
+
report();
|
|
@@ -22,9 +22,9 @@ import {
|
|
|
22
22
|
import { getSliceBranchName } from "../worktree.ts";
|
|
23
23
|
import { abortAndReset } from "../git-self-heal.ts";
|
|
24
24
|
import { runGSDDoctor } from "../doctor.ts";
|
|
25
|
-
import {
|
|
26
|
-
import assert from 'node:assert/strict';
|
|
25
|
+
import { createTestContext } from "./test-helpers.ts";
|
|
27
26
|
|
|
27
|
+
const { assertEq, assertTrue, assertMatch, report } = createTestContext();
|
|
28
28
|
|
|
29
29
|
// ---- Helpers ----
|
|
30
30
|
|
|
@@ -80,7 +80,7 @@ function addSliceToMilestone(
|
|
|
80
80
|
run(`git merge --no-ff ${sliceBranch} -m "merge ${sliceId}"`, wtPath);
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
-
|
|
83
|
+
async function main(): Promise<void> {
|
|
84
84
|
const savedCwd = process.cwd();
|
|
85
85
|
const tempDirs: string[] = [];
|
|
86
86
|
|
|
@@ -100,7 +100,7 @@ describe('worktree-e2e', async () => {
|
|
|
100
100
|
// Create worktree for M001
|
|
101
101
|
const wtPath = createAutoWorktree(repo, "M001");
|
|
102
102
|
tempDirs.push(wtPath);
|
|
103
|
-
|
|
103
|
+
assertTrue(existsSync(wtPath), "worktree directory created");
|
|
104
104
|
|
|
105
105
|
// Add two slices with commits
|
|
106
106
|
addSliceToMilestone(repo, wtPath, "M001", "S01", "Add auth", [
|
|
@@ -124,19 +124,19 @@ describe('worktree-e2e', async () => {
|
|
|
124
124
|
// Assert exactly one new commit on main
|
|
125
125
|
const mainLogAfter = run("git log --oneline main", repo);
|
|
126
126
|
const commitCountAfter = mainLogAfter.split("\n").length;
|
|
127
|
-
|
|
127
|
+
assertEq(commitCountAfter, commitCountBefore + 1, "exactly one new commit on main");
|
|
128
128
|
|
|
129
129
|
// Commit message contains both slice titles
|
|
130
130
|
const lastCommitMsg = run("git log -1 --format=%B main", repo);
|
|
131
|
-
|
|
132
|
-
|
|
131
|
+
assertMatch(lastCommitMsg, /Add auth/, "commit message contains S01 title");
|
|
132
|
+
assertMatch(lastCommitMsg, /Add dashboard/, "commit message contains S02 title");
|
|
133
133
|
|
|
134
134
|
// Worktree directory removed
|
|
135
|
-
|
|
135
|
+
assertTrue(!existsSync(wtPath), "worktree directory removed after merge");
|
|
136
136
|
|
|
137
137
|
// Milestone branch deleted
|
|
138
138
|
const branches = run("git branch", repo);
|
|
139
|
-
|
|
139
|
+
assertTrue(!branches.includes("milestone/M001"), "milestone branch deleted");
|
|
140
140
|
}
|
|
141
141
|
|
|
142
142
|
// ================================================================
|
|
@@ -159,11 +159,11 @@ describe('worktree-e2e', async () => {
|
|
|
159
159
|
|
|
160
160
|
// Trigger merge conflict
|
|
161
161
|
try { run("git merge feature", repo); } catch { /* expected */ }
|
|
162
|
-
|
|
162
|
+
assertTrue(existsSync(join(repo, ".git", "MERGE_HEAD")), "MERGE_HEAD exists before abort");
|
|
163
163
|
|
|
164
164
|
const abortResult = abortAndReset(repo);
|
|
165
|
-
|
|
166
|
-
|
|
165
|
+
assertTrue(!existsSync(join(repo, ".git", "MERGE_HEAD")), "MERGE_HEAD removed after abort");
|
|
166
|
+
assertTrue(abortResult.cleaned.length > 0, "abortAndReset reports cleaned items");
|
|
167
167
|
}
|
|
168
168
|
|
|
169
169
|
// ================================================================
|
|
@@ -211,19 +211,19 @@ _None_
|
|
|
211
211
|
// Detect
|
|
212
212
|
const detect = await runGSDDoctor(repo, { isolationMode: "worktree" });
|
|
213
213
|
const orphanIssues = detect.issues.filter(i => i.code === "orphaned_auto_worktree");
|
|
214
|
-
|
|
215
|
-
|
|
214
|
+
assertTrue(orphanIssues.length > 0, "doctor detects orphaned worktree");
|
|
215
|
+
assertEq(orphanIssues[0]?.unitId, "M001", "orphaned worktree unitId is M001");
|
|
216
216
|
|
|
217
217
|
// Fix
|
|
218
218
|
const fixed = await runGSDDoctor(repo, { fix: true, isolationMode: "worktree" });
|
|
219
|
-
|
|
219
|
+
assertTrue(
|
|
220
220
|
fixed.fixesApplied.some(f => f.includes("removed orphaned worktree")),
|
|
221
221
|
"doctor fix removes orphaned worktree",
|
|
222
222
|
);
|
|
223
223
|
|
|
224
224
|
// Verify gone
|
|
225
225
|
const wtList = run("git worktree list", repo);
|
|
226
|
-
|
|
226
|
+
assertTrue(!wtList.includes("milestone/M001"), "worktree gone after doctor fix");
|
|
227
227
|
}
|
|
228
228
|
} else {
|
|
229
229
|
console.log("\n=== Doctor: orphaned worktree detection (skipped on Windows) ===");
|
|
@@ -234,4 +234,8 @@ _None_
|
|
|
234
234
|
try { rmSync(d, { recursive: true, force: true }); } catch { /* ignore */ }
|
|
235
235
|
}
|
|
236
236
|
}
|
|
237
|
-
|
|
237
|
+
|
|
238
|
+
report();
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
main();
|
|
@@ -12,9 +12,9 @@ import { execSync } from "node:child_process";
|
|
|
12
12
|
|
|
13
13
|
import { getWorktreeHealth, formatWorktreeStatusLine } from "../worktree-health.ts";
|
|
14
14
|
import { listWorktrees } from "../worktree-manager.ts";
|
|
15
|
-
import {
|
|
16
|
-
import assert from 'node:assert/strict';
|
|
15
|
+
import { createTestContext } from "./test-helpers.ts";
|
|
17
16
|
|
|
17
|
+
const { assertEq, assertTrue, report } = createTestContext();
|
|
18
18
|
|
|
19
19
|
function run(cmd: string, cwd: string): string {
|
|
20
20
|
return execSync(cmd, { cwd, stdio: ["ignore", "pipe", "pipe"], encoding: "utf-8" }).trim();
|
|
@@ -32,10 +32,11 @@ function createBaseRepo(): string {
|
|
|
32
32
|
return dir;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
async function main(): Promise<void> {
|
|
36
36
|
// Skip all tests on Windows — git worktree path resolution issues
|
|
37
37
|
if (process.platform === "win32") {
|
|
38
38
|
console.log("(all worktree-health tests skipped on Windows)");
|
|
39
|
+
report();
|
|
39
40
|
return;
|
|
40
41
|
}
|
|
41
42
|
|
|
@@ -58,16 +59,16 @@ describe('worktree-health', async () => {
|
|
|
58
59
|
|
|
59
60
|
const worktrees = listWorktrees(dir);
|
|
60
61
|
const wt = worktrees.find(w => w.name === "done-feature");
|
|
61
|
-
|
|
62
|
+
assertTrue(!!wt, "worktree found");
|
|
62
63
|
|
|
63
64
|
const health = getWorktreeHealth(dir, wt!);
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
65
|
+
assertTrue(health.mergedIntoMain, "branch detected as merged");
|
|
66
|
+
assertTrue(!health.dirty, "not dirty");
|
|
67
|
+
assertTrue(health.safeToRemove, "safe to remove");
|
|
67
68
|
|
|
68
69
|
const line = formatWorktreeStatusLine(health);
|
|
69
|
-
|
|
70
|
-
|
|
70
|
+
assertTrue(line.includes("merged"), "status line mentions merged");
|
|
71
|
+
assertTrue(line.includes("safe to remove"), "status line mentions safe to remove");
|
|
71
72
|
}
|
|
72
73
|
|
|
73
74
|
// ─── Test: unmerged worktree with dirty files ──────────────────────
|
|
@@ -88,13 +89,13 @@ describe('worktree-health', async () => {
|
|
|
88
89
|
|
|
89
90
|
const worktrees = listWorktrees(dir);
|
|
90
91
|
const wt = worktrees.find(w => w.name === "dirty-wip");
|
|
91
|
-
|
|
92
|
+
assertTrue(!!wt, "worktree found");
|
|
92
93
|
|
|
93
94
|
const health = getWorktreeHealth(dir, wt!);
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
95
|
+
assertTrue(!health.mergedIntoMain, "not merged");
|
|
96
|
+
assertTrue(health.dirty, "dirty detected");
|
|
97
|
+
assertTrue(health.dirtyFileCount > 0, "dirty file count > 0");
|
|
98
|
+
assertTrue(!health.safeToRemove, "not safe to remove");
|
|
98
99
|
}
|
|
99
100
|
|
|
100
101
|
// ─── Test: unmerged worktree with unpushed commits ─────────────────
|
|
@@ -112,12 +113,12 @@ describe('worktree-health', async () => {
|
|
|
112
113
|
|
|
113
114
|
const worktrees = listWorktrees(dir);
|
|
114
115
|
const wt = worktrees.find(w => w.name === "unpushed");
|
|
115
|
-
|
|
116
|
+
assertTrue(!!wt, "worktree found");
|
|
116
117
|
|
|
117
118
|
const health = getWorktreeHealth(dir, wt!);
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
119
|
+
assertTrue(!health.mergedIntoMain, "not merged");
|
|
120
|
+
assertTrue(health.unpushedCommits > 0, "unpushed commits detected");
|
|
121
|
+
assertTrue(!health.safeToRemove, "not safe to remove");
|
|
121
122
|
}
|
|
122
123
|
|
|
123
124
|
// ─── Test: stale detection with short threshold ────────────────────
|
|
@@ -136,17 +137,17 @@ describe('worktree-health', async () => {
|
|
|
136
137
|
|
|
137
138
|
const worktrees = listWorktrees(dir);
|
|
138
139
|
const wt = worktrees.find(w => w.name === "stale-test");
|
|
139
|
-
|
|
140
|
+
assertTrue(!!wt, "worktree found");
|
|
140
141
|
|
|
141
142
|
// With staleDays=0, any worktree should be stale (commit was just now, but threshold is 0)
|
|
142
143
|
// Actually, a just-created worktree has lastCommitAgeDays ~0 which is >= 0
|
|
143
144
|
const health = getWorktreeHealth(dir, wt!, 0);
|
|
144
|
-
|
|
145
|
-
|
|
145
|
+
assertTrue(health.stale, "stale with 0-day threshold");
|
|
146
|
+
assertTrue(health.lastCommitAgeDays >= 0, "last commit age is non-negative");
|
|
146
147
|
|
|
147
148
|
// With staleDays=9999, should NOT be stale
|
|
148
149
|
const healthNotStale = getWorktreeHealth(dir, wt!, 9999);
|
|
149
|
-
|
|
150
|
+
assertTrue(!healthNotStale.stale, "not stale with high threshold");
|
|
150
151
|
}
|
|
151
152
|
|
|
152
153
|
// ─── Test: formatWorktreeStatusLine for clean active worktree ──────
|
|
@@ -165,12 +166,12 @@ describe('worktree-health', async () => {
|
|
|
165
166
|
|
|
166
167
|
const worktrees = listWorktrees(dir);
|
|
167
168
|
const wt = worktrees.find(w => w.name === "clean-active");
|
|
168
|
-
|
|
169
|
+
assertTrue(!!wt, "worktree found");
|
|
169
170
|
|
|
170
171
|
const health = getWorktreeHealth(dir, wt!, 9999); // high threshold so not stale
|
|
171
172
|
const line = formatWorktreeStatusLine(health);
|
|
172
173
|
// Should show last commit age since it's not merged and not stale
|
|
173
|
-
|
|
174
|
+
assertTrue(line.includes("last commit"), "shows last commit age for active worktree");
|
|
174
175
|
}
|
|
175
176
|
|
|
176
177
|
} finally {
|
|
@@ -178,4 +179,8 @@ describe('worktree-health', async () => {
|
|
|
178
179
|
try { rmSync(dir, { recursive: true, force: true }); } catch { /* ignore */ }
|
|
179
180
|
}
|
|
180
181
|
}
|
|
181
|
-
|
|
182
|
+
|
|
183
|
+
report();
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
main();
|
|
@@ -29,9 +29,9 @@ import {
|
|
|
29
29
|
} from "../worktree.ts";
|
|
30
30
|
|
|
31
31
|
import { deriveState } from "../state.ts";
|
|
32
|
-
import {
|
|
33
|
-
import assert from 'node:assert/strict';
|
|
32
|
+
import { createTestContext } from './test-helpers.ts';
|
|
34
33
|
|
|
34
|
+
const { assertEq, assertTrue, report } = createTestContext();
|
|
35
35
|
function run(command: string, cwd: string): string {
|
|
36
36
|
return execSync(command, { cwd, stdio: ["ignore", "pipe", "pipe"], encoding: "utf-8" }).trim();
|
|
37
37
|
}
|
|
@@ -73,42 +73,42 @@ writeFileSync(
|
|
|
73
73
|
run("git add .", base);
|
|
74
74
|
run('git commit -m "chore: init"', base);
|
|
75
75
|
|
|
76
|
-
|
|
76
|
+
async function main(): Promise<void> {
|
|
77
77
|
// ── Verify main tree baseline ──────────────────────────────────────────────
|
|
78
78
|
|
|
79
79
|
console.log("\n=== Main tree baseline ===");
|
|
80
|
-
|
|
81
|
-
|
|
80
|
+
assertEq(getMainBranch(base), "main", "main tree getMainBranch returns main");
|
|
81
|
+
assertEq(detectWorktreeName(base), null, "main tree not detected as worktree");
|
|
82
82
|
|
|
83
83
|
// ── Create worktree and verify detection ───────────────────────────────────
|
|
84
84
|
|
|
85
85
|
console.log("\n=== Create worktree ===");
|
|
86
86
|
const wt = createWorktree(base, "alpha");
|
|
87
|
-
|
|
88
|
-
|
|
87
|
+
assertTrue(existsSync(wt.path), "worktree created on disk");
|
|
88
|
+
assertEq(wt.branch, "worktree/alpha", "worktree branch name");
|
|
89
89
|
|
|
90
90
|
console.log("\n=== Worktree detection ===");
|
|
91
|
-
|
|
92
|
-
|
|
91
|
+
assertEq(detectWorktreeName(wt.path), "alpha", "detectWorktreeName inside worktree");
|
|
92
|
+
assertEq(getMainBranch(wt.path), "worktree/alpha", "getMainBranch returns worktree branch inside worktree");
|
|
93
93
|
|
|
94
94
|
// ── Verify current branch inside worktree ──────────────────────────────────
|
|
95
95
|
|
|
96
96
|
console.log("\n=== Worktree initial branch ===");
|
|
97
|
-
|
|
97
|
+
assertEq(getCurrentBranch(wt.path), "worktree/alpha", "worktree starts on its own branch");
|
|
98
98
|
|
|
99
99
|
// ── Verify branch name helper ──────────────────────────────────────────────
|
|
100
100
|
|
|
101
101
|
console.log("\n=== getSliceBranchName with worktree ===");
|
|
102
|
-
|
|
103
|
-
|
|
102
|
+
assertEq(getSliceBranchName("M001", "S01", "alpha"), "gsd/alpha/M001/S01", "explicit worktree param");
|
|
103
|
+
assertEq(getSliceBranchName("M001", "S01"), "gsd/M001/S01", "no worktree param = plain branch");
|
|
104
104
|
|
|
105
105
|
// ── Slice branch creation and detection inside worktree ────────────────────
|
|
106
106
|
|
|
107
107
|
console.log("\n=== Slice branch in worktree ===");
|
|
108
108
|
const sliceBranch = getSliceBranchName("M001", "S01", "alpha");
|
|
109
109
|
run(`git checkout -b ${sliceBranch}`, wt.path);
|
|
110
|
-
|
|
111
|
-
|
|
110
|
+
assertEq(getCurrentBranch(wt.path), "gsd/alpha/M001/S01", "worktree-namespaced slice branch");
|
|
111
|
+
assertTrue(SLICE_BRANCH_RE.test(getCurrentBranch(wt.path)), "slice branch regex matches namespaced branch");
|
|
112
112
|
|
|
113
113
|
// ── Do work on slice branch, then merge to worktree branch ─────────────────
|
|
114
114
|
|
|
@@ -119,23 +119,23 @@ describe('worktree-integration', async () => {
|
|
|
119
119
|
|
|
120
120
|
// Checkout worktree base branch and merge slice branch
|
|
121
121
|
run("git checkout worktree/alpha", wt.path);
|
|
122
|
-
|
|
122
|
+
assertEq(getCurrentBranch(wt.path), "worktree/alpha", "back on worktree branch");
|
|
123
123
|
|
|
124
124
|
run(`git merge --no-ff ${sliceBranch} -m "feat(M001/S01): First"`, wt.path);
|
|
125
125
|
run(`git branch -d ${sliceBranch}`, wt.path);
|
|
126
|
-
|
|
127
|
-
|
|
126
|
+
assertEq(getCurrentBranch(wt.path), "worktree/alpha", "still on worktree branch after merge");
|
|
127
|
+
assertTrue(readFileSync(join(wt.path, "feature.txt"), "utf-8").includes("new feature"), "merge brought feature to worktree branch");
|
|
128
128
|
|
|
129
129
|
// Verify slice branch is gone
|
|
130
130
|
const branches = run("git branch", base);
|
|
131
|
-
|
|
131
|
+
assertTrue(!branches.includes("gsd/alpha/M001/S01"), "slice branch cleaned up");
|
|
132
132
|
|
|
133
133
|
// ── Second slice in same worktree ──────────────────────────────────────────
|
|
134
134
|
|
|
135
135
|
console.log("\n=== Second slice in worktree ===");
|
|
136
136
|
const sliceBranch2 = getSliceBranchName("M001", "S02", "alpha");
|
|
137
137
|
run(`git checkout -b ${sliceBranch2}`, wt.path);
|
|
138
|
-
|
|
138
|
+
assertEq(getCurrentBranch(wt.path), "gsd/alpha/M001/S02", "on S02 namespaced branch");
|
|
139
139
|
|
|
140
140
|
writeFileSync(join(wt.path, "feature2.txt"), "second feature\n", "utf-8");
|
|
141
141
|
run("git add .", wt.path);
|
|
@@ -144,28 +144,28 @@ describe('worktree-integration', async () => {
|
|
|
144
144
|
run("git checkout worktree/alpha", wt.path);
|
|
145
145
|
run(`git merge --no-ff ${sliceBranch2} -m "feat(M001/S02): Second"`, wt.path);
|
|
146
146
|
run(`git branch -d ${sliceBranch2}`, wt.path);
|
|
147
|
-
|
|
147
|
+
assertEq(getCurrentBranch(wt.path), "worktree/alpha", "back on worktree branch");
|
|
148
148
|
|
|
149
149
|
// ── Parallel worktrees don't conflict ──────────────────────────────────────
|
|
150
150
|
|
|
151
151
|
console.log("\n=== Parallel worktrees ===");
|
|
152
152
|
const wt2 = createWorktree(base, "beta");
|
|
153
|
-
|
|
153
|
+
assertEq(getMainBranch(wt2.path), "worktree/beta", "second worktree has its own base branch");
|
|
154
154
|
|
|
155
155
|
// Both worktrees can create S01 branches without conflict
|
|
156
156
|
const betaBranch = getSliceBranchName("M001", "S01", "beta");
|
|
157
157
|
run(`git checkout -b ${betaBranch}`, wt2.path);
|
|
158
|
-
|
|
158
|
+
assertEq(getCurrentBranch(wt2.path), "gsd/beta/M001/S01", "beta has its own namespaced branch");
|
|
159
159
|
|
|
160
160
|
// Alpha worktree can re-create S01 too (it was already merged+deleted earlier)
|
|
161
161
|
const alphaReBranch = getSliceBranchName("M001", "S01", "alpha");
|
|
162
162
|
run(`git checkout -b ${alphaReBranch}`, wt.path);
|
|
163
|
-
|
|
163
|
+
assertEq(getCurrentBranch(wt.path), "gsd/alpha/M001/S01", "alpha re-created S01");
|
|
164
164
|
|
|
165
165
|
// Both exist simultaneously
|
|
166
166
|
const allBranches = run("git branch", base);
|
|
167
|
-
|
|
168
|
-
|
|
167
|
+
assertTrue(allBranches.includes("gsd/alpha/M001/S01"), "alpha S01 branch exists");
|
|
168
|
+
assertTrue(allBranches.includes("gsd/beta/M001/S01"), "beta S01 branch exists");
|
|
169
169
|
|
|
170
170
|
// ── State derivation in worktree ───────────────────────────────────────────
|
|
171
171
|
|
|
@@ -173,8 +173,8 @@ describe('worktree-integration', async () => {
|
|
|
173
173
|
// Switch alpha back to its base so deriveState sees milestone files
|
|
174
174
|
run("git checkout worktree/alpha", wt.path);
|
|
175
175
|
const state = await deriveState(wt.path);
|
|
176
|
-
|
|
177
|
-
|
|
176
|
+
assertTrue(state.activeMilestone !== null, "worktree has active milestone");
|
|
177
|
+
assertEq(state.activeMilestone?.id, "M001", "correct milestone");
|
|
178
178
|
|
|
179
179
|
// ── autoCommitCurrentBranch in worktree ────────────────────────────────────
|
|
180
180
|
|
|
@@ -183,8 +183,8 @@ describe('worktree-integration', async () => {
|
|
|
183
183
|
run(`git checkout ${betaBranch}`, wt2.path);
|
|
184
184
|
writeFileSync(join(wt2.path, "dirty.txt"), "uncommitted\n", "utf-8");
|
|
185
185
|
const commitMsg = autoCommitCurrentBranch(wt2.path, "execute-task", "M001/S01/T01");
|
|
186
|
-
|
|
187
|
-
|
|
186
|
+
assertTrue(commitMsg !== null, "auto-commit works in worktree");
|
|
187
|
+
assertEq(run("git status --short", wt2.path), "", "worktree clean after auto-commit");
|
|
188
188
|
|
|
189
189
|
// ── Cleanup ────────────────────────────────────────────────────────────────
|
|
190
190
|
|
|
@@ -194,7 +194,14 @@ describe('worktree-integration', async () => {
|
|
|
194
194
|
run("git checkout worktree/beta", wt2.path);
|
|
195
195
|
removeWorktree(base, "alpha", { deleteBranch: true });
|
|
196
196
|
removeWorktree(base, "beta", { deleteBranch: true });
|
|
197
|
-
|
|
197
|
+
assertEq(listWorktrees(base).length, 0, "all worktrees removed");
|
|
198
198
|
|
|
199
199
|
rmSync(base, { recursive: true, force: true });
|
|
200
|
+
|
|
201
|
+
report();
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
main().catch((error) => {
|
|
205
|
+
console.error(error);
|
|
206
|
+
process.exit(1);
|
|
200
207
|
});
|