@opengsd/gsd-pi 1.2.0-dev.b1abb545 → 1.2.0-dev.fb12b103
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli-style.d.ts +17 -0
- package/dist/cli-style.js +28 -0
- package/dist/cli.js +1 -1
- package/dist/headless-events.d.ts +4 -2
- package/dist/headless-events.js +7 -29
- package/dist/models-resolver.d.ts +3 -13
- package/dist/models-resolver.js +3 -22
- package/dist/resource-loader.js +2 -14
- package/dist/resources/.managed-resources-content-hash +1 -1
- package/dist/resources/extensions/async-jobs/async-bash-tool.js +30 -64
- package/dist/resources/extensions/async-jobs/await-tool.js +80 -12
- package/dist/resources/extensions/async-jobs/index.js +65 -0
- package/dist/resources/extensions/async-jobs/job-manager.js +12 -1
- package/dist/resources/extensions/bg-shell/bg-shell-command.js +6 -6
- package/dist/resources/extensions/bg-shell/bg-shell-tool.js +10 -7
- package/dist/resources/extensions/bg-shell/overlay.js +9 -6
- package/dist/resources/extensions/bg-shell/process-manager.js +54 -25
- package/dist/resources/extensions/bg-shell/readiness-detector.js +11 -0
- package/dist/resources/extensions/bg-shell/utilities.js +5 -2
- package/dist/resources/extensions/browser-tools/engine/managed-gsd-browser.js +209 -88
- package/dist/resources/extensions/browser-tools/engine/selection.js +73 -5
- package/dist/resources/extensions/browser-tools/index.js +69 -12
- package/dist/resources/extensions/claude-code-cli/models.js +9 -0
- package/dist/resources/extensions/claude-code-cli/stream-adapter.js +38 -6
- package/dist/resources/extensions/gsd/auto/orchestrator.js +40 -9
- package/dist/resources/extensions/gsd/auto/phases.js +6 -1
- package/dist/resources/extensions/gsd/auto-dispatch.js +12 -1
- package/dist/resources/extensions/gsd/auto-model-selection.js +25 -6
- package/dist/resources/extensions/gsd/auto-post-unit.js +19 -8
- package/dist/resources/extensions/gsd/auto-prompts.js +15 -10
- package/dist/resources/extensions/gsd/auto-start.js +21 -21
- package/dist/resources/extensions/gsd/auto-tool-tracking.js +18 -0
- package/dist/resources/extensions/gsd/auto-unit-tool-scope.js +7 -16
- package/dist/resources/extensions/gsd/auto-worktree-repair.js +10 -2
- package/dist/resources/extensions/gsd/auto-worktree.js +35 -352
- package/dist/resources/extensions/gsd/auto.js +8 -20
- package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +3 -2
- package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +32 -12
- package/dist/resources/extensions/gsd/bootstrap/register-extension.js +19 -0
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +151 -20
- package/dist/resources/extensions/gsd/bootstrap/write-gate.js +30 -4
- package/dist/resources/extensions/gsd/branch-patterns.js +2 -0
- package/dist/resources/extensions/gsd/browser-daemon-auto-prep.js +83 -0
- package/dist/resources/extensions/gsd/browser-evidence.js +8 -2
- package/dist/resources/extensions/gsd/captures.js +5 -15
- package/dist/resources/extensions/gsd/closeout-recovery.js +3 -2
- package/dist/resources/extensions/gsd/commands/catalog.js +6 -62
- package/dist/resources/extensions/gsd/constants.js +0 -2
- package/dist/resources/extensions/gsd/crash-recovery.js +4 -12
- package/dist/resources/extensions/gsd/db/engine.js +755 -0
- package/dist/resources/extensions/gsd/db/queries.js +372 -0
- package/dist/resources/extensions/gsd/db/sql-constants.js +11 -0
- package/dist/resources/extensions/gsd/db/writers/cascades.js +194 -0
- package/dist/resources/extensions/gsd/db/writers/import-restore.js +182 -0
- package/dist/resources/extensions/gsd/db/writers/memory.js +149 -0
- package/dist/resources/extensions/gsd/db/writers/reconcile.js +458 -0
- package/dist/resources/extensions/gsd/db/writers/status.js +70 -0
- package/dist/resources/extensions/gsd/doctor-environment.js +5 -11
- package/dist/resources/extensions/gsd/doctor-format.js +9 -6
- package/dist/resources/extensions/gsd/doctor-git-checks.js +4 -3
- package/dist/resources/extensions/gsd/doctor-runtime-checks.js +21 -16
- package/dist/resources/extensions/gsd/error-classifier.js +9 -0
- package/dist/resources/extensions/gsd/exec-sandbox.js +30 -10
- package/dist/resources/extensions/gsd/git-service.js +1 -0
- package/dist/resources/extensions/gsd/gitignore.js +3 -0
- package/dist/resources/extensions/gsd/gsd-db.js +171 -2048
- package/dist/resources/extensions/gsd/guidance.js +98 -0
- package/dist/resources/extensions/gsd/guided-flow.js +51 -5
- package/dist/resources/extensions/gsd/mcp-filter.js +2 -19
- package/dist/resources/extensions/gsd/mcp-tool-name.js +5 -13
- package/dist/resources/extensions/gsd/memory-consolidation-scanner.js +1 -1
- package/dist/resources/extensions/gsd/migrate/safety.js +20 -9
- package/dist/resources/extensions/gsd/migration-auto-check.js +24 -3
- package/dist/resources/extensions/gsd/model-cost-table.js +1 -0
- package/dist/resources/extensions/gsd/model-router.js +3 -0
- package/dist/resources/extensions/gsd/notification-store.js +11 -4
- package/dist/resources/extensions/gsd/parallel-merge.js +14 -11
- package/dist/resources/extensions/gsd/parallel-monitor-overlay.js +11 -7
- package/dist/resources/extensions/gsd/paths.js +37 -24
- package/dist/resources/extensions/gsd/pre-execution-checks.js +91 -3
- package/dist/resources/extensions/gsd/preferences-models.js +14 -48
- package/dist/resources/extensions/gsd/preferences.js +14 -0
- package/dist/resources/extensions/gsd/prompts/complete-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/refine-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/replan-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/run-uat.md +1 -1
- package/dist/resources/extensions/gsd/prompts/system.md +5 -2
- package/dist/resources/extensions/gsd/provider-error-guidance.js +1 -5
- package/dist/resources/extensions/gsd/provider-switch-observer.js +1 -1
- package/dist/resources/extensions/gsd/publication.js +87 -0
- package/dist/resources/extensions/gsd/recovery-classification.js +41 -87
- package/dist/resources/extensions/gsd/safety/destructive-confirmation.js +108 -0
- package/dist/resources/extensions/gsd/safety/evidence-collector.js +37 -4
- package/dist/resources/extensions/gsd/safety/evidence-cross-ref.js +7 -2
- package/dist/resources/extensions/gsd/safety/file-change-validator.js +10 -0
- package/dist/resources/extensions/gsd/state-transition-matrix.js +38 -0
- package/dist/resources/extensions/gsd/state.js +1 -20
- package/dist/resources/extensions/gsd/status-guards.js +56 -8
- package/dist/resources/extensions/gsd/stop-notice.js +57 -0
- package/dist/resources/extensions/gsd/tool-surface-readiness.js +56 -0
- package/dist/resources/extensions/gsd/tools/complete-slice.js +24 -43
- package/dist/resources/extensions/gsd/tools/exec-tool.js +10 -8
- package/dist/resources/extensions/gsd/tools/plan-slice.js +12 -6
- package/dist/resources/extensions/gsd/tools/reopen-milestone.js +11 -29
- package/dist/resources/extensions/gsd/tools/reopen-slice.js +14 -33
- package/dist/resources/extensions/gsd/tools/skip-slice.js +18 -36
- package/dist/resources/extensions/gsd/uat-policy.js +2 -1
- package/dist/resources/extensions/gsd/undo.js +8 -7
- package/dist/resources/extensions/gsd/unit-closeout.js +138 -0
- package/dist/resources/extensions/gsd/unit-context-composer.js +74 -1
- package/dist/resources/extensions/gsd/unit-context-manifest.js +4 -27
- package/dist/resources/extensions/gsd/unit-registry.js +337 -0
- package/dist/resources/extensions/gsd/unit-tool-contracts.js +9 -182
- package/dist/resources/extensions/gsd/web-app-uat.js +45 -8
- package/dist/resources/extensions/gsd/workflow-tool-surface.js +1 -1
- package/dist/resources/extensions/gsd/worktree-git-recovery.js +293 -0
- package/dist/resources/extensions/gsd/worktree-lifecycle.js +9 -1
- package/dist/resources/extensions/gsd/worktree-manager.js +45 -28
- package/dist/resources/extensions/gsd/worktree-placement.js +59 -0
- package/dist/resources/extensions/gsd/worktree-reentry.js +12 -8
- package/dist/resources/extensions/gsd/worktree-root.js +28 -6
- package/dist/resources/extensions/gsd/worktree-safety.js +8 -5
- package/dist/resources/extensions/gsd/worktree-session-state.js +12 -11
- package/dist/resources/extensions/search-the-web/native-search.js +5 -3
- package/dist/resources/extensions/shared/browser-contract.js +59 -0
- package/dist/resources/extensions/shared/gsd-browser-cli.js +96 -5
- package/dist/resources/shared/package.json +3 -0
- package/dist/resources/skills/create-skill/references/executable-code.md +1 -1
- package/dist/resources/skills/create-skill/workflows/add-reference.md +8 -3
- package/dist/resources/skills/create-skill/workflows/add-script.md +4 -2
- package/dist/resources/skills/create-skill/workflows/add-template.md +3 -1
- package/dist/resources/skills/create-skill/workflows/add-workflow.md +8 -3
- package/dist/resources/skills/create-skill/workflows/upgrade-to-router.md +10 -5
- package/dist/resources/skills/create-skill/workflows/verify-skill.md +9 -4
- package/dist/resources/skills/gsd-browser/SKILL.md +1 -1
- package/dist/resources/skills/spike-wrap-up/SKILL.md +9 -9
- package/dist/tsconfig.extensions.tsbuildinfo +1 -1
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +10 -10
- package/dist/web/standalone/.next/build-manifest.json +3 -3
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/api/boot/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/captures/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/cleanup/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/doctor/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/export-data/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/files/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/forensics/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/git/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/history/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/hooks/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/inspect/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/knowledge/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/live-state/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/mcp-connections/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/notifications/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/onboarding/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/projects/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/recovery/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/session/browser/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/session/command/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/session/events/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/session/manage/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/settings-data/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/shutdown/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/skill-health/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/steer/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/switch-root/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/undo/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/update/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/visualizer/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +10 -10
- package/dist/web/standalone/.next/server/chunks/5124.js +1 -1
- package/dist/web/standalone/.next/server/chunks/{5047.js → 5942.js} +2 -2
- package/dist/web/standalone/.next/server/chunks/8357.js +1 -1
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +1 -1
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/dist/web/standalone/.next/static/chunks/{796.cf859a427a2cb2ac.js → 796.e0bdc932325d7e03.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/{webpack-fbea77b5f9953368.js → webpack-f0285ce91d4ec9ef.js} +1 -1
- package/dist/web/standalone/node_modules/node-pty/build/Makefile +1 -1
- package/dist/web/standalone/node_modules/postcss/lib/container.js +18 -26
- package/dist/web/standalone/node_modules/postcss/lib/css-syntax-error.js +14 -47
- package/dist/web/standalone/node_modules/postcss/lib/declaration.js +4 -4
- package/dist/web/standalone/node_modules/postcss/lib/fromJSON.js +3 -3
- package/dist/web/standalone/node_modules/postcss/lib/input.js +29 -54
- package/dist/web/standalone/node_modules/postcss/lib/lazy-result.js +37 -47
- package/dist/web/standalone/node_modules/postcss/lib/map-generator.js +9 -26
- package/dist/web/standalone/node_modules/postcss/lib/no-work-result.js +55 -57
- package/dist/web/standalone/node_modules/postcss/lib/node.js +31 -99
- package/dist/web/standalone/node_modules/postcss/lib/parse.js +1 -1
- package/dist/web/standalone/node_modules/postcss/lib/parser.js +9 -10
- package/dist/web/standalone/node_modules/postcss/lib/postcss.js +12 -12
- package/dist/web/standalone/node_modules/postcss/lib/previous-map.js +11 -30
- package/dist/web/standalone/node_modules/postcss/lib/processor.js +7 -7
- package/dist/web/standalone/node_modules/postcss/lib/result.js +5 -5
- package/dist/web/standalone/node_modules/postcss/lib/rule.js +6 -6
- package/dist/web/standalone/node_modules/postcss/lib/stringifier.js +28 -69
- package/dist/web/standalone/node_modules/postcss/lib/tokenize.js +2 -6
- package/dist/web/standalone/node_modules/postcss/package.json +48 -48
- package/dist/web/standalone/package.json +1 -1
- package/dist/worktree-cli.js +3 -6
- package/dist/worktree-status-banner.js +7 -11
- package/package.json +1 -1
- package/packages/cloud-mcp-gateway/package.json +2 -2
- package/packages/contracts/dist/rpc.d.ts +1 -0
- package/packages/contracts/dist/rpc.d.ts.map +1 -1
- package/packages/contracts/dist/rpc.js.map +1 -1
- package/packages/contracts/dist/workflow.d.ts +4 -0
- package/packages/contracts/dist/workflow.d.ts.map +1 -1
- package/packages/contracts/dist/workflow.js.map +1 -1
- package/packages/contracts/package.json +1 -1
- package/packages/daemon/package.json +4 -4
- package/packages/gsd-agent-core/package.json +5 -5
- package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts +5 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js +8 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js +7 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/input-controller.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/input-controller.js +8 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/input-controller.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-chat-render.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-chat-render.js +11 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-chat-render.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-auth.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-auth.js +4 -4
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-auth.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/rpc/rpc-mode.js +3 -1
- package/packages/gsd-agent-modes/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/packages/gsd-agent-modes/package.json +7 -7
- package/packages/mcp-server/dist/cli.js +6 -3
- package/packages/mcp-server/dist/cli.js.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.d.ts +8 -0
- package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.js +46 -21
- package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
- package/packages/mcp-server/package.json +3 -3
- package/packages/native/package.json +1 -1
- package/packages/pi-agent-core/dist/harness/env/nodejs.d.ts +1 -0
- package/packages/pi-agent-core/dist/harness/env/nodejs.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/harness/env/nodejs.js +34 -3
- package/packages/pi-agent-core/dist/harness/env/nodejs.js.map +1 -1
- package/packages/pi-agent-core/dist/index.d.ts +1 -0
- package/packages/pi-agent-core/dist/index.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/index.js +3 -0
- package/packages/pi-agent-core/dist/index.js.map +1 -1
- package/packages/pi-agent-core/package.json +1 -1
- package/packages/pi-ai/dist/image-models.generated.d.ts +2 -2
- package/packages/pi-ai/dist/image-models.generated.js +6 -6
- package/packages/pi-ai/dist/image-models.generated.js.map +1 -1
- package/packages/pi-ai/dist/models.generated.d.ts +478 -484
- package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
- package/packages/pi-ai/dist/models.generated.js +500 -533
- package/packages/pi-ai/dist/models.generated.js.map +1 -1
- package/packages/pi-ai/package.json +1 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.d.ts +2 -2
- package/packages/pi-coding-agent/dist/core/auth-storage.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.js +19 -13
- package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/capability-patches.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/capability-patches.js +3 -1
- package/packages/pi-coding-agent/dist/core/capability-patches.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/provider-readiness.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/provider-readiness.js +13 -6
- package/packages/pi-coding-agent/dist/core/provider-readiness.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/bash.d.ts +11 -0
- package/packages/pi-coding-agent/dist/core/tools/bash.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/bash.js +53 -11
- package/packages/pi-coding-agent/dist/core/tools/bash.js.map +1 -1
- package/packages/pi-coding-agent/dist/index.d.ts +1 -1
- package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/index.js +1 -1
- package/packages/pi-coding-agent/dist/index.js.map +1 -1
- package/packages/pi-coding-agent/dist/utils/shell.d.ts +28 -2
- package/packages/pi-coding-agent/dist/utils/shell.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/utils/shell.js +56 -10
- package/packages/pi-coding-agent/dist/utils/shell.js.map +1 -1
- package/packages/pi-coding-agent/package.json +7 -7
- package/packages/pi-tui/dist/tui.d.ts.map +1 -1
- package/packages/pi-tui/dist/tui.js +9 -0
- package/packages/pi-tui/dist/tui.js.map +1 -1
- package/packages/pi-tui/package.json +2 -2
- package/packages/rpc-client/package.json +2 -2
- package/pkg/package.json +1 -1
- package/src/resources/extensions/async-jobs/async-bash-cancel.test.ts +360 -0
- package/src/resources/extensions/async-jobs/async-bash-tool.ts +33 -56
- package/src/resources/extensions/async-jobs/await-tool.test.ts +139 -0
- package/src/resources/extensions/async-jobs/await-tool.ts +82 -12
- package/src/resources/extensions/async-jobs/index.ts +79 -0
- package/src/resources/extensions/async-jobs/job-manager.ts +21 -1
- package/src/resources/extensions/bg-shell/bg-shell-command.ts +6 -6
- package/src/resources/extensions/bg-shell/bg-shell-tool.ts +10 -6
- package/src/resources/extensions/bg-shell/overlay.ts +9 -5
- package/src/resources/extensions/bg-shell/process-manager.ts +50 -25
- package/src/resources/extensions/bg-shell/readiness-detector.ts +12 -0
- package/src/resources/extensions/bg-shell/tests/lifecycle-and-utilities.test.ts +48 -1
- package/src/resources/extensions/bg-shell/utilities.ts +5 -2
- package/src/resources/extensions/browser-tools/engine/managed-gsd-browser.ts +265 -98
- package/src/resources/extensions/browser-tools/engine/selection.ts +90 -4
- package/src/resources/extensions/browser-tools/index.ts +71 -13
- package/src/resources/extensions/browser-tools/tests/browser-engine-selection.test.mjs +83 -13
- package/src/resources/extensions/browser-tools/tests/gsd-browser-launch-config.test.mjs +29 -1
- package/src/resources/extensions/browser-tools/tests/managed-gsd-browser-tools.test.mjs +136 -0
- package/src/resources/extensions/claude-code-cli/models.ts +9 -0
- package/src/resources/extensions/claude-code-cli/stream-adapter.ts +40 -4
- package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +28 -0
- package/src/resources/extensions/gsd/auto/loop-deps.ts +1 -1
- package/src/resources/extensions/gsd/auto/orchestrator.ts +46 -10
- package/src/resources/extensions/gsd/auto/phases.ts +10 -1
- package/src/resources/extensions/gsd/auto-dispatch.ts +12 -0
- package/src/resources/extensions/gsd/auto-model-selection.ts +25 -5
- package/src/resources/extensions/gsd/auto-post-unit.ts +25 -7
- package/src/resources/extensions/gsd/auto-prompts.ts +40 -26
- package/src/resources/extensions/gsd/auto-start.ts +21 -22
- package/src/resources/extensions/gsd/auto-tool-tracking.ts +19 -0
- package/src/resources/extensions/gsd/auto-unit-tool-scope.ts +10 -17
- package/src/resources/extensions/gsd/auto-worktree-repair.ts +13 -2
- package/src/resources/extensions/gsd/auto-worktree.ts +41 -364
- package/src/resources/extensions/gsd/auto.ts +20 -24
- package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +3 -5
- package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +33 -12
- package/src/resources/extensions/gsd/bootstrap/register-extension.ts +24 -0
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +180 -15
- package/src/resources/extensions/gsd/bootstrap/write-gate.ts +29 -3
- package/src/resources/extensions/gsd/branch-patterns.ts +3 -0
- package/src/resources/extensions/gsd/browser-daemon-auto-prep.ts +108 -0
- package/src/resources/extensions/gsd/browser-evidence.ts +18 -2
- package/src/resources/extensions/gsd/captures.ts +5 -16
- package/src/resources/extensions/gsd/closeout-recovery.ts +2 -1
- package/src/resources/extensions/gsd/commands/catalog.ts +6 -68
- package/src/resources/extensions/gsd/constants.ts +0 -3
- package/src/resources/extensions/gsd/crash-recovery.ts +3 -9
- package/src/resources/extensions/gsd/db/engine.ts +809 -0
- package/src/resources/extensions/gsd/db/queries.ts +453 -0
- package/src/resources/extensions/gsd/db/sql-constants.ts +12 -0
- package/src/resources/extensions/gsd/db/writers/cascades.ts +237 -0
- package/src/resources/extensions/gsd/db/writers/import-restore.ts +310 -0
- package/src/resources/extensions/gsd/db/writers/memory.ts +220 -0
- package/src/resources/extensions/gsd/db/writers/reconcile.ts +500 -0
- package/src/resources/extensions/gsd/db/writers/status.ts +88 -0
- package/src/resources/extensions/gsd/doctor-environment.ts +5 -13
- package/src/resources/extensions/gsd/doctor-format.ts +12 -7
- package/src/resources/extensions/gsd/doctor-git-checks.ts +3 -3
- package/src/resources/extensions/gsd/doctor-runtime-checks.ts +22 -17
- package/src/resources/extensions/gsd/error-classifier.ts +11 -0
- package/src/resources/extensions/gsd/exec-sandbox.ts +49 -9
- package/src/resources/extensions/gsd/git-service.ts +1 -0
- package/src/resources/extensions/gsd/gitignore.ts +3 -0
- package/src/resources/extensions/gsd/gsd-db.ts +173 -2373
- package/src/resources/extensions/gsd/guidance.ts +139 -0
- package/src/resources/extensions/gsd/guided-flow.ts +50 -5
- package/src/resources/extensions/gsd/mcp-filter.ts +2 -23
- package/src/resources/extensions/gsd/mcp-tool-name.ts +6 -11
- package/src/resources/extensions/gsd/memory-consolidation-scanner.ts +1 -1
- package/src/resources/extensions/gsd/migrate/safety.ts +18 -7
- package/src/resources/extensions/gsd/migration-auto-check.ts +28 -3
- package/src/resources/extensions/gsd/model-cost-table.ts +1 -0
- package/src/resources/extensions/gsd/model-router.ts +3 -0
- package/src/resources/extensions/gsd/notification-store.ts +26 -3
- package/src/resources/extensions/gsd/parallel-merge.ts +12 -9
- package/src/resources/extensions/gsd/parallel-monitor-overlay.ts +10 -7
- package/src/resources/extensions/gsd/paths.ts +42 -22
- package/src/resources/extensions/gsd/pre-execution-checks.ts +109 -3
- package/src/resources/extensions/gsd/preferences-models.ts +12 -47
- package/src/resources/extensions/gsd/preferences.ts +18 -0
- package/src/resources/extensions/gsd/prompts/complete-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/refine-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/replan-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/run-uat.md +1 -1
- package/src/resources/extensions/gsd/prompts/system.md +5 -2
- package/src/resources/extensions/gsd/provider-error-guidance.ts +4 -9
- package/src/resources/extensions/gsd/provider-switch-observer.ts +1 -1
- package/src/resources/extensions/gsd/publication.ts +122 -0
- package/src/resources/extensions/gsd/recovery-classification.ts +47 -88
- package/src/resources/extensions/gsd/safety/destructive-confirmation.ts +134 -0
- package/src/resources/extensions/gsd/safety/evidence-collector.ts +36 -4
- package/src/resources/extensions/gsd/safety/evidence-cross-ref.ts +7 -2
- package/src/resources/extensions/gsd/safety/file-change-validator.ts +14 -0
- package/src/resources/extensions/gsd/state-transition-matrix.ts +42 -0
- package/src/resources/extensions/gsd/state.ts +4 -21
- package/src/resources/extensions/gsd/status-guards.ts +59 -8
- package/src/resources/extensions/gsd/stop-notice.ts +75 -0
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +123 -0
- package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +22 -0
- package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +16 -19
- package/src/resources/extensions/gsd/tests/auto-paused-ui-cleanup.test.ts +3 -1
- package/src/resources/extensions/gsd/tests/auto-post-unit-evidence-crossref-4909.test.ts +46 -0
- package/src/resources/extensions/gsd/tests/auto-worktree-registry.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/auto-worktree-repair.test.ts +4 -2
- package/src/resources/extensions/gsd/tests/browser-automation-contract-fixture.ts +39 -0
- package/src/resources/extensions/gsd/tests/browser-contract.test.ts +44 -0
- package/src/resources/extensions/gsd/tests/browser-daemon-auto-prep.test.ts +144 -0
- package/src/resources/extensions/gsd/tests/checkout-branch-stash-guard.test.ts +66 -1
- package/src/resources/extensions/gsd/tests/clear-stale-autostart.test.ts +44 -0
- package/src/resources/extensions/gsd/tests/commands-verdict.test.ts +8 -7
- package/src/resources/extensions/gsd/tests/destructive-confirmation.test.ts +303 -0
- package/src/resources/extensions/gsd/tests/dispatch-run-uat-browser-tools.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/dynamic-bash-no-cap.test.ts +132 -0
- package/src/resources/extensions/gsd/tests/evidence-xref-gsd-exec.test.ts +157 -0
- package/src/resources/extensions/gsd/tests/exec-graceful-kill.test.ts +193 -0
- package/src/resources/extensions/gsd/tests/exec-tool.test.ts +29 -1
- package/src/resources/extensions/gsd/tests/extension-bootstrap-isolation.test.ts +35 -1
- package/src/resources/extensions/gsd/tests/file-change-validator.test.ts +33 -1
- package/src/resources/extensions/gsd/tests/guidance.test.ts +125 -0
- package/src/resources/extensions/gsd/tests/integration/auto-worktree-milestone-merge.test.ts +58 -15
- package/src/resources/extensions/gsd/tests/integration/auto-worktree.test.ts +74 -59
- package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +3 -2
- package/src/resources/extensions/gsd/tests/integration/gsd-integration-fixture.ts +80 -0
- package/src/resources/extensions/gsd/tests/mcp-project-config.test.ts +3 -1
- package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +85 -1
- package/src/resources/extensions/gsd/tests/model-unittype-mapping.test.ts +32 -1
- package/src/resources/extensions/gsd/tests/notification-store.test.ts +32 -0
- package/src/resources/extensions/gsd/tests/oauth-api-model-routing.test.ts +167 -0
- package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +193 -1
- package/src/resources/extensions/gsd/tests/provider-error-guidance.test.ts +3 -3
- package/src/resources/extensions/gsd/tests/publication.test.ts +120 -0
- package/src/resources/extensions/gsd/tests/recovery-classification-illegal-transition.test.ts +30 -0
- package/src/resources/extensions/gsd/tests/register-hooks-depth-verification.test.ts +248 -1
- package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/safety-harness-false-positives.test.ts +38 -0
- package/src/resources/extensions/gsd/tests/session-switch-clears-pending-autostart.test.ts +108 -0
- package/src/resources/extensions/gsd/tests/single-writer-invariant.test.ts +43 -6
- package/src/resources/extensions/gsd/tests/state-transition-matrix.test.ts +36 -0
- package/src/resources/extensions/gsd/tests/status-guards.test.ts +38 -0
- package/src/resources/extensions/gsd/tests/stop-notice.test.ts +70 -0
- package/src/resources/extensions/gsd/tests/token-tool-gating.test.ts +76 -0
- package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +8 -0
- package/src/resources/extensions/gsd/tests/tool-surface-readiness.test.ts +155 -0
- package/src/resources/extensions/gsd/tests/uat-policy.test.ts +24 -29
- package/src/resources/extensions/gsd/tests/unit-closeout.test.ts +209 -0
- package/src/resources/extensions/gsd/tests/unit-context-composer.test.ts +67 -2
- package/src/resources/extensions/gsd/tests/unit-registry.test.ts +163 -0
- package/src/resources/extensions/gsd/tests/web-app-uat.test.ts +44 -1
- package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/worktree-lifecycle.test.ts +41 -4
- package/src/resources/extensions/gsd/tests/worktree-manager.test.ts +22 -1
- package/src/resources/extensions/gsd/tests/worktree-placement.test.ts +113 -0
- package/src/resources/extensions/gsd/tests/worktree-reentry.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/worktree-safety.test.ts +3 -1
- package/src/resources/extensions/gsd/tests/worktree-symlink-removal.test.ts +12 -6
- package/src/resources/extensions/gsd/tests/worktree-teardown-safety.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/write-gate.test.ts +42 -0
- package/src/resources/extensions/gsd/tool-surface-readiness.ts +76 -0
- package/src/resources/extensions/gsd/tools/complete-slice.ts +23 -58
- package/src/resources/extensions/gsd/tools/exec-tool.ts +9 -8
- package/src/resources/extensions/gsd/tools/plan-slice.ts +12 -6
- package/src/resources/extensions/gsd/tools/reopen-milestone.ts +11 -38
- package/src/resources/extensions/gsd/tools/reopen-slice.ts +14 -42
- package/src/resources/extensions/gsd/tools/skip-slice.ts +18 -44
- package/src/resources/extensions/gsd/uat-policy.ts +2 -1
- package/src/resources/extensions/gsd/undo.ts +9 -8
- package/src/resources/extensions/gsd/unit-closeout.ts +201 -0
- package/src/resources/extensions/gsd/unit-context-composer.ts +111 -1
- package/src/resources/extensions/gsd/unit-context-manifest.ts +4 -28
- package/src/resources/extensions/gsd/unit-registry.ts +412 -0
- package/src/resources/extensions/gsd/unit-tool-contracts.ts +27 -192
- package/src/resources/extensions/gsd/web-app-uat.ts +51 -8
- package/src/resources/extensions/gsd/workflow-tool-surface.ts +4 -1
- package/src/resources/extensions/gsd/worktree-git-recovery.ts +314 -0
- package/src/resources/extensions/gsd/worktree-lifecycle.ts +10 -1
- package/src/resources/extensions/gsd/worktree-manager.ts +47 -28
- package/src/resources/extensions/gsd/worktree-placement.ts +63 -0
- package/src/resources/extensions/gsd/worktree-reentry.ts +10 -7
- package/src/resources/extensions/gsd/worktree-root.ts +29 -6
- package/src/resources/extensions/gsd/worktree-safety.ts +8 -5
- package/src/resources/extensions/gsd/worktree-session-state.ts +11 -11
- package/src/resources/extensions/search-the-web/native-search.ts +5 -3
- package/src/resources/extensions/shared/browser-contract.ts +66 -0
- package/src/resources/extensions/shared/gsd-browser-cli.ts +119 -5
- package/src/resources/shared/package.json +3 -0
- package/src/resources/skills/create-skill/references/executable-code.md +1 -1
- package/src/resources/skills/create-skill/workflows/add-reference.md +8 -3
- package/src/resources/skills/create-skill/workflows/add-script.md +4 -2
- package/src/resources/skills/create-skill/workflows/add-template.md +3 -1
- package/src/resources/skills/create-skill/workflows/add-workflow.md +8 -3
- package/src/resources/skills/create-skill/workflows/upgrade-to-router.md +10 -5
- package/src/resources/skills/create-skill/workflows/verify-skill.md +9 -4
- package/src/resources/skills/gsd-browser/SKILL.md +1 -1
- package/src/resources/skills/spike-wrap-up/SKILL.md +9 -9
- /package/dist/web/standalone/.next/static/{3PtrU9qGPEXwNLWkIyiqk → mU4QIDlpVHDdjDpeEKh5W}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{3PtrU9qGPEXwNLWkIyiqk → mU4QIDlpVHDdjDpeEKh5W}/_ssgManifest.js +0 -0
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
// Project/App: gsd-pi
|
|
2
|
-
// File Purpose: Web-app detection and
|
|
2
|
+
// File Purpose: Web-app detection and browser-UAT guidance for planning and slice closeout.
|
|
3
3
|
|
|
4
4
|
import { existsSync, readFileSync } from "node:fs";
|
|
5
5
|
import { resolve } from "node:path";
|
|
6
6
|
|
|
7
|
+
import { resolveAmbientBrowserEngineResolution, type BrowserEngineResolution } from "../browser-tools/engine/selection.js";
|
|
7
8
|
import { detectWebApp } from "../browser-tools/web-app-detect.js";
|
|
9
|
+
import { UAT_MODE_POLICIES, type UatType } from "./uat-policy.js";
|
|
8
10
|
|
|
9
11
|
export { detectWebApp };
|
|
10
12
|
|
|
@@ -47,24 +49,61 @@ export function findPlaywrightTestScript(projectRoot: string): string | null {
|
|
|
47
49
|
return null;
|
|
48
50
|
}
|
|
49
51
|
|
|
52
|
+
function describeBrowserToolBacking(engineResolution: BrowserEngineResolution): string {
|
|
53
|
+
switch (engineResolution.engine) {
|
|
54
|
+
case "gsd-browser":
|
|
55
|
+
return "This project looks browser-facing. GSD exposes `browser_*` tools backed by the managed gsd-browser engine for run-uat.";
|
|
56
|
+
case "legacy":
|
|
57
|
+
return "This project looks browser-facing. GSD exposes Playwright-backed `browser_*` tools for run-uat.";
|
|
58
|
+
case "off":
|
|
59
|
+
return "This project looks browser-facing, but Pi browser tools are disabled (GSD_BROWSER_ENGINE=off) — prefer `runtime-executable` UAT with automated browser test commands.";
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// One bullet per recommended UAT mode; `mode` keys into UAT_MODE_POLICIES so
|
|
64
|
+
// modes that require browser tools drop out of the guidance when the resolved
|
|
65
|
+
// engine provides none (mixed/live-runtime share one bullet and one policy bit).
|
|
66
|
+
const UAT_MODE_GUIDANCE: ReadonlyArray<{ mode: UatType; bullet: string }> = [
|
|
67
|
+
{
|
|
68
|
+
mode: "browser-executable",
|
|
69
|
+
bullet: "- `browser-executable` — navigate to `http://localhost:…`, click, screenshot, assert via `browser_*` tools during run-uat",
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
mode: "runtime-executable",
|
|
73
|
+
bullet: "- `runtime-executable` — run an automated browser test command via `gsd_uat_exec` (for example `npx playwright test`)",
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
mode: "mixed",
|
|
77
|
+
bullet: "- `mixed` / `live-runtime` — combine runtime startup checks with interactive browser verification",
|
|
78
|
+
},
|
|
79
|
+
];
|
|
80
|
+
|
|
50
81
|
/**
|
|
51
82
|
* Markdown block injected into plan/complete-slice prompts when the project
|
|
52
|
-
* looks browser-facing. Returns null for CLI/library-only repos.
|
|
83
|
+
* looks browser-facing. Returns null for CLI/library-only repos. Guidance is
|
|
84
|
+
* composed from the resolved Browser Automation Engine so prompts never claim
|
|
85
|
+
* an engine the runtime is not using; `engineResolution` is injectable for
|
|
86
|
+
* tests and defaults to the ambient resolution.
|
|
53
87
|
*/
|
|
54
|
-
export function buildWebAppUatGuidanceBlock(
|
|
88
|
+
export function buildWebAppUatGuidanceBlock(
|
|
89
|
+
projectRoot: string,
|
|
90
|
+
engineResolution?: BrowserEngineResolution,
|
|
91
|
+
): string | null {
|
|
55
92
|
if (!detectWebApp(projectRoot)) return null;
|
|
56
93
|
|
|
94
|
+
const resolvedEngine = engineResolution ?? resolveAmbientBrowserEngineResolution(projectRoot);
|
|
95
|
+
const browserToolsAvailable = resolvedEngine.engine !== "off";
|
|
57
96
|
const playwrightScript = findPlaywrightTestScript(projectRoot);
|
|
58
97
|
const hasPlaywright = hasPlaywrightTestDependency(projectRoot) || playwrightScript !== null;
|
|
59
98
|
const lines = [
|
|
60
99
|
"### Web App UAT (detected)",
|
|
61
100
|
"",
|
|
62
|
-
|
|
101
|
+
describeBrowserToolBacking(resolvedEngine),
|
|
63
102
|
"",
|
|
64
103
|
"**UAT modes (pick one per slice — do not use `artifact-driven` for browser steps):**",
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
104
|
+
...UAT_MODE_GUIDANCE
|
|
105
|
+
.filter(({ mode }) => browserToolsAvailable || !UAT_MODE_POLICIES[mode].browserTools)
|
|
106
|
+
.map(({ bullet }) => bullet),
|
|
68
107
|
"",
|
|
69
108
|
"**Planning / closeout rules:**",
|
|
70
109
|
"- Preconditions must name the dev-server command and URL (for example `npm run dev` → `http://localhost:3000`)",
|
|
@@ -93,8 +132,12 @@ export function buildWebAppUatGuidanceBlock(projectRoot: string): string | null
|
|
|
93
132
|
"**Playwright scaffolding (first UI slice):** no `playwright` / `@playwright/test` dependency yet.",
|
|
94
133
|
"- Add a planning task that installs Playwright, adds `playwright.config.ts`, and creates a minimal smoke spec (for example `e2e/smoke.spec.ts`)",
|
|
95
134
|
"- Task `verify` should run `npx playwright test` (or the focused spec) with a safe, simple command",
|
|
96
|
-
"- Until specs exist, use `browser-executable` UAT with localhost preconditions and interactive `browser_*` checks at slice closeout",
|
|
97
135
|
);
|
|
136
|
+
if (browserToolsAvailable) {
|
|
137
|
+
lines.push(
|
|
138
|
+
"- Until specs exist, use `browser-executable` UAT with localhost preconditions and interactive `browser_*` checks at slice closeout",
|
|
139
|
+
);
|
|
140
|
+
}
|
|
98
141
|
}
|
|
99
142
|
|
|
100
143
|
return lines.join("\n");
|
|
@@ -28,7 +28,7 @@ export const WORKFLOW_TOOL_ALIAS_PAIRS: readonly WorkflowToolAliasPair[] =
|
|
|
28
28
|
export const WORKFLOW_TOOL_ALIAS_TO_CANONICAL: Readonly<Record<string, string>> =
|
|
29
29
|
Object.fromEntries(WORKFLOW_TOOL_ALIAS_PAIRS.map(({ alias, canonical }) => [alias, canonical]));
|
|
30
30
|
|
|
31
|
-
const WORKFLOW_MCP_ADAPTER_TOOL_NAMES = [
|
|
31
|
+
export const WORKFLOW_MCP_ADAPTER_TOOL_NAMES = [
|
|
32
32
|
"gsd_cancel",
|
|
33
33
|
"gsd_captures",
|
|
34
34
|
"ask_user_questions",
|
|
@@ -45,6 +45,9 @@ const WORKFLOW_MCP_ADAPTER_TOOL_NAMES = [
|
|
|
45
45
|
"gsd_status",
|
|
46
46
|
] as const;
|
|
47
47
|
|
|
48
|
+
/** Session-orchestration tools exposed by the workflow MCP adapter alongside the contract tools. */
|
|
49
|
+
export type WorkflowMcpAdapterToolName = (typeof WORKFLOW_MCP_ADAPTER_TOOL_NAMES)[number];
|
|
50
|
+
|
|
48
51
|
export const WORKFLOW_TOOL_SURFACE_NAMES = [
|
|
49
52
|
...WORKFLOW_MCP_ADAPTER_TOOL_NAMES,
|
|
50
53
|
...DB_WORKFLOW_TOOL_NAMES,
|
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
// Project/App: gsd-pi
|
|
2
|
+
// File Purpose: Git checkout/stash/merge-state recovery primitives for worktree operations.
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Worktree Git Recovery — the recurring-bug hot spot, in one place.
|
|
6
|
+
*
|
|
7
|
+
* Owns the verbs that recover a repository from interrupted or conflicting
|
|
8
|
+
* git operations during worktree transitions:
|
|
9
|
+
*
|
|
10
|
+
* - `checkoutBranchWithStashGuard` — branch switch with stash protection,
|
|
11
|
+
* including the stash-pop EEXIST collision recovery for untracked files
|
|
12
|
+
* (force-checkout + targeted stash drop; #645 broadened it beyond `.gsd/`,
|
|
13
|
+
* guarded by "no non-.gsd unmerged entries remain").
|
|
14
|
+
* - `removeMergeStateFiles` — clears SQUASH_MSG / MERGE_HEAD / etc. left by
|
|
15
|
+
* a failed merge so subsequent merges don't fail on stale state.
|
|
16
|
+
* - `cleanupConflictState` — merge-abort + index reset + state-file cleanup
|
|
17
|
+
* after a conflicted (including squash) merge.
|
|
18
|
+
* - stash helpers (`popStashByRef`, `stashRefFromError`,
|
|
19
|
+
* `stashAlreadyExistsFilesFromError`, `gsdJsonlFilesWithConflictMarkers`)
|
|
20
|
+
* used by the merge pipeline in auto-worktree.ts.
|
|
21
|
+
*
|
|
22
|
+
* Extracted from auto-worktree.ts so recovery fixes land here instead of as
|
|
23
|
+
* embedded special cases in a 2,600-line orchestration module, and so the
|
|
24
|
+
* rules can be tested against scripted git states.
|
|
25
|
+
*
|
|
26
|
+
* The State Reconciliation drift repair (`state-reconciliation/drift/
|
|
27
|
+
* merge-state.ts`) keeps its own merge-state primitive by design — drift
|
|
28
|
+
* repairs own their raw primitives (see CONTEXT.md, Drift repair).
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
import { execFileSync } from "node:child_process";
|
|
32
|
+
import { existsSync, readFileSync, unlinkSync } from "node:fs";
|
|
33
|
+
import { isAbsolute, join, resolve } from "node:path";
|
|
34
|
+
|
|
35
|
+
import { debugLog } from "./debug-logger.js";
|
|
36
|
+
import { logError, logWarning } from "./workflow-logger.js";
|
|
37
|
+
import {
|
|
38
|
+
nativeAddPaths,
|
|
39
|
+
nativeCheckoutBranch,
|
|
40
|
+
nativeConflictFiles,
|
|
41
|
+
nativeLsFiles,
|
|
42
|
+
nativeMergeAbort,
|
|
43
|
+
nativeWorkingTreeStatus,
|
|
44
|
+
} from "./native-git-bridge.js";
|
|
45
|
+
import { resolveGitDir } from "./worktree-manager.js";
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Pop the stash entry created with `stashMarker` in its subject, resolving it
|
|
49
|
+
* to a concrete `stash@{n}` ref first so a concurrent stash push cannot make
|
|
50
|
+
* `git stash pop` grab the wrong entry.
|
|
51
|
+
*
|
|
52
|
+
* If `stashMarker` is null or no longer present in the stash list (e.g. a
|
|
53
|
+
* concurrent process popped/dropped it), leaves the stash list untouched and
|
|
54
|
+
* returns null.
|
|
55
|
+
*
|
|
56
|
+
* Throws on pop failure so callers can handle conflict cases the same way
|
|
57
|
+
* they would with the prior `git stash pop` form. When throwing after a
|
|
58
|
+
* targeted pop attempt, the error is annotated with the targeted stash ref.
|
|
59
|
+
*
|
|
60
|
+
* (Issue #4980 HIGH-6)
|
|
61
|
+
*/
|
|
62
|
+
export function popStashByRef(basePath: string, stashMarker: string | null): string | null {
|
|
63
|
+
let popArg: string | null = null;
|
|
64
|
+
if (stashMarker) {
|
|
65
|
+
try {
|
|
66
|
+
const list = execFileSync("git", ["stash", "list", "--format=%gd%x00%s"], {
|
|
67
|
+
cwd: basePath,
|
|
68
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
69
|
+
encoding: "utf-8",
|
|
70
|
+
}).trim().split("\n").filter(Boolean);
|
|
71
|
+
for (const entry of list) {
|
|
72
|
+
const [ref, subject] = entry.split("\0");
|
|
73
|
+
if (ref && subject?.includes(stashMarker)) {
|
|
74
|
+
popArg = ref;
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
} catch (err) {
|
|
79
|
+
logWarning("worktree", `stash list lookup failed; leaving stash untouched: ${err instanceof Error ? err.message : String(err)}`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
if (!popArg) {
|
|
83
|
+
logWarning("worktree", "recorded stash entry could not be resolved; skipping automatic pop");
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
try {
|
|
87
|
+
execFileSync("git", ["stash", "pop", popArg], {
|
|
88
|
+
cwd: basePath,
|
|
89
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
90
|
+
encoding: "utf-8",
|
|
91
|
+
});
|
|
92
|
+
} catch (err) {
|
|
93
|
+
if (err && typeof err === "object") {
|
|
94
|
+
(err as { stashRef?: string }).stashRef = popArg;
|
|
95
|
+
}
|
|
96
|
+
throw err;
|
|
97
|
+
}
|
|
98
|
+
return popArg;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Extract a stash ref annotation injected by popStashByRef() when git stash
|
|
103
|
+
* pop fails and we need to conditionally drop the exact stash entry later.
|
|
104
|
+
*/
|
|
105
|
+
export function stashRefFromError(err: unknown): string | null {
|
|
106
|
+
if (!err || typeof err !== "object") return null;
|
|
107
|
+
const stashRef = (err as { stashRef?: unknown }).stashRef;
|
|
108
|
+
return typeof stashRef === "string" && stashRef.length > 0 ? stashRef : null;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export function stashAlreadyExistsFilesFromError(err: unknown): string[] {
|
|
112
|
+
if (!err || typeof err !== "object") return [];
|
|
113
|
+
const stderr = (err as { stderr?: unknown }).stderr;
|
|
114
|
+
const stderrText = typeof stderr === "string"
|
|
115
|
+
? stderr
|
|
116
|
+
: stderr instanceof Uint8Array
|
|
117
|
+
? Buffer.from(stderr).toString("utf-8")
|
|
118
|
+
: "";
|
|
119
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
120
|
+
const text = `${stderrText}\n${message}`;
|
|
121
|
+
const files = new Set<string>();
|
|
122
|
+
for (const line of text.split("\n")) {
|
|
123
|
+
const m = line.match(/^(.*?)\s+already exists, no checkout\s*$/i);
|
|
124
|
+
if (!m) continue;
|
|
125
|
+
const filePath = m[1]?.trim();
|
|
126
|
+
if (filePath) files.add(filePath);
|
|
127
|
+
}
|
|
128
|
+
return [...files];
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Detect whether an on-disk file still contains unresolved merge conflict
|
|
133
|
+
* markers from a failed stash-pop or merge attempt.
|
|
134
|
+
*
|
|
135
|
+
* Returns false when the file cannot be read.
|
|
136
|
+
*/
|
|
137
|
+
export function hasConflictMarkers(filePath: string): boolean {
|
|
138
|
+
try {
|
|
139
|
+
const content = readFileSync(filePath, "utf-8");
|
|
140
|
+
return content.includes("<<<<<<<") && content.includes("=======") && content.includes(">>>>>>>");
|
|
141
|
+
} catch {
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export function gsdJsonlFilesWithConflictMarkers(basePath: string): string[] {
|
|
147
|
+
return nativeLsFiles(basePath, ".gsd/*.jsonl").filter((f) =>
|
|
148
|
+
hasConflictMarkers(join(basePath, f)),
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export function removeMergeStateFiles(basePath: string, contextLabel: string): void {
|
|
153
|
+
try {
|
|
154
|
+
for (const f of ["SQUASH_MSG", "MERGE_MSG", "MERGE_MODE", "MERGE_HEAD", "AUTO_MERGE"]) {
|
|
155
|
+
const rawPath = execFileSync("git", ["rev-parse", "--git-path", f], {
|
|
156
|
+
cwd: basePath,
|
|
157
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
158
|
+
encoding: "utf-8",
|
|
159
|
+
}).trim();
|
|
160
|
+
const p = rawPath.length > 0
|
|
161
|
+
? (isAbsolute(rawPath) ? rawPath : resolve(basePath, rawPath))
|
|
162
|
+
: join(resolveGitDir(basePath), f);
|
|
163
|
+
if (existsSync(p)) unlinkSync(p);
|
|
164
|
+
}
|
|
165
|
+
} catch (err) {
|
|
166
|
+
logError("worktree", `${contextLabel} merge state cleanup failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export function cleanupConflictState(basePath: string): void {
|
|
171
|
+
// Merge conflicts can leave unmerged index entries; merge-abort alone is not
|
|
172
|
+
// enough for squash merges (MERGE_HEAD is never written). Reset the merge
|
|
173
|
+
// index, then remove merge message files that native/libgit2 paths may have
|
|
174
|
+
// created.
|
|
175
|
+
try {
|
|
176
|
+
nativeMergeAbort(basePath);
|
|
177
|
+
} catch (err) {
|
|
178
|
+
// MERGE_HEAD absent (squash merge path) — abort is a no-op, which is fine.
|
|
179
|
+
debugLog("conflict-cleanup:merge-abort-skipped", {
|
|
180
|
+
error: err instanceof Error ? err.message : String(err),
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
try {
|
|
184
|
+
execFileSync("git", ["reset", "--merge"], {
|
|
185
|
+
cwd: basePath,
|
|
186
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
187
|
+
encoding: "utf-8",
|
|
188
|
+
});
|
|
189
|
+
} catch (err) {
|
|
190
|
+
logError("worktree", `git reset --merge failed after merge conflict: ${err instanceof Error ? err.message : String(err)}`);
|
|
191
|
+
}
|
|
192
|
+
removeMergeStateFiles(basePath, "conflict");
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
export function checkoutBranchWithStashGuard(
|
|
196
|
+
basePath: string,
|
|
197
|
+
branch: string,
|
|
198
|
+
reason: string,
|
|
199
|
+
): void {
|
|
200
|
+
let stashMarker: string | null = null;
|
|
201
|
+
let stashed = false;
|
|
202
|
+
|
|
203
|
+
const status = nativeWorkingTreeStatus(basePath).trim();
|
|
204
|
+
if (status.length > 0) {
|
|
205
|
+
stashMarker = `gsd-checkout-stash:${reason}:${process.pid}:${Date.now()}:${process.hrtime.bigint().toString(36)}`;
|
|
206
|
+
const stashListBefore = execFileSync("git", ["stash", "list"], {
|
|
207
|
+
cwd: basePath,
|
|
208
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
209
|
+
encoding: "utf-8",
|
|
210
|
+
});
|
|
211
|
+
execFileSync(
|
|
212
|
+
"git",
|
|
213
|
+
["stash", "push", "--include-untracked", "-m", `gsd: checkout stash [${stashMarker}]`],
|
|
214
|
+
{
|
|
215
|
+
cwd: basePath,
|
|
216
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
217
|
+
encoding: "utf-8",
|
|
218
|
+
},
|
|
219
|
+
);
|
|
220
|
+
const stashListAfter = execFileSync("git", ["stash", "list"], {
|
|
221
|
+
cwd: basePath,
|
|
222
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
223
|
+
encoding: "utf-8",
|
|
224
|
+
});
|
|
225
|
+
stashed = stashListAfter !== stashListBefore;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Checkout and stash-restore are split so we can distinguish two failure
|
|
229
|
+
// modes: (a) checkout failed → HEAD did not move, restore stash and rethrow;
|
|
230
|
+
// (b) checkout succeeded but stash pop failed → HEAD moved to `branch` but
|
|
231
|
+
// the working-tree changes remain in the stash list. We surface a distinct
|
|
232
|
+
// error in case (b) so callers don't assume the branch switch was rolled back.
|
|
233
|
+
try {
|
|
234
|
+
nativeCheckoutBranch(basePath, branch);
|
|
235
|
+
} catch (checkoutErr) {
|
|
236
|
+
if (stashed) {
|
|
237
|
+
try {
|
|
238
|
+
popStashByRef(basePath, stashMarker);
|
|
239
|
+
} catch (restoreErr) {
|
|
240
|
+
logWarning("worktree", `git stash pop failed during checkout restore: ${restoreErr instanceof Error ? restoreErr.message : String(restoreErr)}`);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
throw checkoutErr;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
if (stashed) {
|
|
247
|
+
try {
|
|
248
|
+
popStashByRef(basePath, stashMarker);
|
|
249
|
+
} catch (popErr) {
|
|
250
|
+
const msg = popErr instanceof Error ? popErr.message : String(popErr);
|
|
251
|
+
const stderr = popErr && typeof popErr === "object"
|
|
252
|
+
? (popErr as { stderr?: unknown }).stderr
|
|
253
|
+
: undefined;
|
|
254
|
+
const stderrText = typeof stderr === "string"
|
|
255
|
+
? stderr
|
|
256
|
+
: stderr instanceof Uint8Array
|
|
257
|
+
? Buffer.from(stderr).toString("utf-8")
|
|
258
|
+
: "";
|
|
259
|
+
const stashPopMessage = `${stderrText}\n${msg}`.trim();
|
|
260
|
+
const alreadyExists = stashAlreadyExistsFilesFromError(popErr);
|
|
261
|
+
const isUntrackedRestoreFailure = stashPopMessage.includes("could not restore untracked files from stash");
|
|
262
|
+
const stashRefForDrop = stashRefFromError(popErr);
|
|
263
|
+
const allConflictFiles = nativeConflictFiles(basePath);
|
|
264
|
+
const nonGsdUnmerged = allConflictFiles.filter((f) => !f.startsWith(".gsd/"));
|
|
265
|
+
const gsdUnmerged = allConflictFiles.filter((f) => f.startsWith(".gsd/"));
|
|
266
|
+
const gsdContentConflicts = isUntrackedRestoreFailure
|
|
267
|
+
? gsdJsonlFilesWithConflictMarkers(basePath)
|
|
268
|
+
: [];
|
|
269
|
+
// Resolve ALL untracked-collision files by accepting HEAD — files in
|
|
270
|
+
// alreadyExists were untracked on the source branch by definition of the
|
|
271
|
+
// "already exists, no checkout" failure, so target HEAD is authoritative.
|
|
272
|
+
// gsdUnmerged: .gsd/ index conflicts left by the partial stash pop are
|
|
273
|
+
// also resolved via HEAD — .gsd/ runtime state is always authoritative
|
|
274
|
+
// on the target branch, so accepting HEAD is safe here too.
|
|
275
|
+
const resolvable = [...new Set([...alreadyExists, ...gsdContentConflicts, ...gsdUnmerged])];
|
|
276
|
+
|
|
277
|
+
if (
|
|
278
|
+
isUntrackedRestoreFailure &&
|
|
279
|
+
resolvable.length > 0 &&
|
|
280
|
+
nonGsdUnmerged.length === 0
|
|
281
|
+
) {
|
|
282
|
+
for (const f of resolvable) {
|
|
283
|
+
execFileSync("git", ["checkout", "HEAD", "--", f], {
|
|
284
|
+
cwd: basePath,
|
|
285
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
286
|
+
encoding: "utf-8",
|
|
287
|
+
});
|
|
288
|
+
nativeAddPaths(basePath, [f]);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
if (stashRefForDrop) {
|
|
292
|
+
try {
|
|
293
|
+
execFileSync("git", ["stash", "drop", stashRefForDrop], {
|
|
294
|
+
cwd: basePath,
|
|
295
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
296
|
+
encoding: "utf-8",
|
|
297
|
+
});
|
|
298
|
+
} catch (err) { /* stash may already be consumed */
|
|
299
|
+
logWarning("worktree", `git stash drop failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
300
|
+
}
|
|
301
|
+
} else {
|
|
302
|
+
logWarning("worktree", "recorded stash entry could not be resolved; skipping automatic drop");
|
|
303
|
+
}
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
const wrapped = new Error(
|
|
308
|
+
`checkout to '${branch}' succeeded but stash restore failed; working tree changes remain in the stash list. Original error: ${msg}`,
|
|
309
|
+
);
|
|
310
|
+
if (stashRefForDrop) (wrapped as { stashRef?: string }).stashRef = stashRefForDrop;
|
|
311
|
+
throw wrapped;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
@@ -675,7 +675,16 @@ export function _enterMilestoneCore(
|
|
|
675
675
|
// Handles the case where originalBasePath is falsy and basePath is itself
|
|
676
676
|
// a worktree path — prevents double-nested worktree paths (#3729).
|
|
677
677
|
const basePath = resolveWorktreeProjectRoot(s.basePath, s.originalBasePath);
|
|
678
|
-
|
|
678
|
+
// A stranded-recovery session that adopted the milestone branch in the
|
|
679
|
+
// project root must keep re-entering in that mode: the root checkout holds
|
|
680
|
+
// the branch, so creating the canonical worktree would fail with "already
|
|
681
|
+
// in use by another worktree". The override clears when the recovered
|
|
682
|
+
// milestone merges (_mergeAndExit), restoring configured isolation for
|
|
683
|
+
// subsequent milestones.
|
|
684
|
+
const mode =
|
|
685
|
+
opts.modeOverride ??
|
|
686
|
+
s.strandedRecoveryIsolationMode ??
|
|
687
|
+
getIsolationMode(basePath);
|
|
679
688
|
|
|
680
689
|
if (s.isolationDegraded) {
|
|
681
690
|
if (mode === "worktree") {
|
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
/**
|
|
5
5
|
* GSD Worktree Manager
|
|
6
6
|
*
|
|
7
|
-
* Creates and manages git worktrees under .gsd
|
|
7
|
+
* Creates and manages git worktrees under .gsd-worktrees/<name>/ (canonical;
|
|
8
|
+
* legacy .gsd/worktrees/<name>/ stays recognized — see worktree-placement.ts).
|
|
8
9
|
* Each worktree gets its own branch (worktree/<name>) and a full
|
|
9
10
|
* working copy of the project, enabling parallel work streams.
|
|
10
11
|
*
|
|
@@ -12,7 +13,7 @@
|
|
|
12
13
|
* the main branch, then dispatches an LLM-guided merge flow.
|
|
13
14
|
*
|
|
14
15
|
* Flow:
|
|
15
|
-
* 1. create() — git worktree add .gsd
|
|
16
|
+
* 1. create() — git worktree add .gsd-worktrees/<name> -b worktree/<name>
|
|
16
17
|
* 2. user works in the worktree (new plans, milestones, etc.)
|
|
17
18
|
* 3. merge() — LLM-guided reconciliation of .gsd/ artifacts back to main
|
|
18
19
|
* 4. remove() — git worktree remove + branch cleanup
|
|
@@ -48,6 +49,7 @@ import {
|
|
|
48
49
|
normalizeWorktreePathForCompare,
|
|
49
50
|
resolveWorktreeProjectRoot,
|
|
50
51
|
} from "./worktree-root.js";
|
|
52
|
+
import { canonicalWorktreesDir, worktreePathFor, worktreesDirs } from "./worktree-placement.js";
|
|
51
53
|
|
|
52
54
|
// ─── Types ─────────────────────────────────────────────────────────────────
|
|
53
55
|
|
|
@@ -138,12 +140,20 @@ export function resolveGitDir(basePath: string): string {
|
|
|
138
140
|
return gitPath;
|
|
139
141
|
}
|
|
140
142
|
|
|
143
|
+
/** Canonical container for new worktrees. For scans that must also see legacy
|
|
144
|
+
* worktrees, use allWorktreesDirs(). */
|
|
141
145
|
export function worktreesDir(basePath: string): string {
|
|
142
|
-
return
|
|
146
|
+
return canonicalWorktreesDir(resolveWorktreeProjectRoot(basePath));
|
|
143
147
|
}
|
|
144
148
|
|
|
149
|
+
/** Every container a GSD worktree may live in (canonical + legacy), canonical first. */
|
|
150
|
+
export function allWorktreesDirs(basePath: string): string[] {
|
|
151
|
+
return worktreesDirs(resolveWorktreeProjectRoot(basePath));
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/** Path for worktree `name` — an existing legacy worktree keeps its location. */
|
|
145
155
|
export function worktreePath(basePath: string, name: string): string {
|
|
146
|
-
return
|
|
156
|
+
return worktreePathFor(resolveWorktreeProjectRoot(basePath), name);
|
|
147
157
|
}
|
|
148
158
|
|
|
149
159
|
export function worktreeBranchName(name: string): string {
|
|
@@ -151,20 +161,22 @@ export function worktreeBranchName(name: string): string {
|
|
|
151
161
|
}
|
|
152
162
|
|
|
153
163
|
/**
|
|
154
|
-
* Validate that a path is inside
|
|
155
|
-
*
|
|
156
|
-
* so that a symlink-resolved
|
|
164
|
+
* Validate that a path is inside a GSD worktrees container (canonical
|
|
165
|
+
* .gsd-worktrees/ or legacy .gsd/worktrees/). Resolves symlinks and
|
|
166
|
+
* normalizes ".." traversals before comparison so that a symlink-resolved
|
|
167
|
+
* or crafted path cannot escape containment.
|
|
157
168
|
*
|
|
158
169
|
* Used as a safety gate before any destructive operation (rmSync,
|
|
159
170
|
* nativeWorktreeRemove --force) to prevent #2365-style data loss.
|
|
160
171
|
*/
|
|
161
172
|
export function isInsideWorktreesDir(basePath: string, targetPath: string): boolean {
|
|
162
|
-
const wtDirPath = worktreesDir(basePath);
|
|
163
|
-
const wtDir = existsSync(wtDirPath) ? realpathSync(wtDirPath) : resolve(wtDirPath);
|
|
164
173
|
const resolved = existsSync(targetPath) ? realpathSync(targetPath) : resolve(targetPath);
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
174
|
+
return allWorktreesDirs(basePath).some((wtDirPath) => {
|
|
175
|
+
const wtDir = existsSync(wtDirPath) ? realpathSync(wtDirPath) : resolve(wtDirPath);
|
|
176
|
+
// The resolved path must start with the worktrees dir followed by a separator,
|
|
177
|
+
// not merely be a prefix match (e.g. ".gsd/worktrees-extra" must not match).
|
|
178
|
+
return resolved === wtDir || resolved.startsWith(wtDir + sep);
|
|
179
|
+
});
|
|
168
180
|
}
|
|
169
181
|
|
|
170
182
|
function isRegisteredGitWorktreeAtPath(basePath: string, wtPath: string): boolean {
|
|
@@ -277,12 +289,12 @@ export function buildManualValidationGuidance(
|
|
|
277
289
|
): string | null {
|
|
278
290
|
if (!milestoneId) return null;
|
|
279
291
|
const validationRoot = resolveCanonicalMilestoneRoot(basePath, milestoneId);
|
|
280
|
-
const inWorktree = validationRoot
|
|
292
|
+
const inWorktree = isGsdWorktreePath(validationRoot);
|
|
281
293
|
const lines: string[] = [`Validate the work here: ${validationRoot}`];
|
|
282
294
|
if (inWorktree) {
|
|
283
295
|
lines.push(
|
|
284
|
-
"This milestone runs in a git worktree, so the code lives under the
|
|
285
|
-
|
|
296
|
+
"This milestone runs in a git worktree, so the code lives under the " +
|
|
297
|
+
`GSD worktrees directory. Open it with: cd "${validationRoot}"`,
|
|
286
298
|
);
|
|
287
299
|
}
|
|
288
300
|
if (opts.uatPath) {
|
|
@@ -307,24 +319,33 @@ export function createWorktree(basePath: string, name: string, opts: { branch?:
|
|
|
307
319
|
throw new GSDError(GSD_PARSE_ERROR, `Invalid worktree name "${name}". Use only letters, numbers, hyphens, and underscores.`);
|
|
308
320
|
}
|
|
309
321
|
|
|
310
|
-
const
|
|
322
|
+
const existingPath = worktreePath(basePath, name);
|
|
311
323
|
const branch = opts.branch ?? worktreeBranchName(name);
|
|
312
324
|
|
|
313
|
-
if (existsSync(
|
|
325
|
+
if (existsSync(existingPath)) {
|
|
314
326
|
// A valid git worktree is registered in `git worktree list` and has a .git
|
|
315
327
|
// *file* with a gitdir: pointer. Leftover directories (no .git, a standalone
|
|
316
328
|
// .git directory from accidental `git init`, or an orphan pointer) block
|
|
317
329
|
// creation unless removed.
|
|
318
|
-
if (isRegisteredGitWorktreeAtPath(basePath,
|
|
319
|
-
throw new GSDError(GSD_STALE_STATE, `Worktree "${name}" already exists at ${
|
|
330
|
+
if (isRegisteredGitWorktreeAtPath(basePath, existingPath)) {
|
|
331
|
+
throw new GSDError(GSD_STALE_STATE, `Worktree "${name}" already exists at ${existingPath}`);
|
|
320
332
|
}
|
|
321
|
-
removeStaleWorktreeDirectory(
|
|
333
|
+
removeStaleWorktreeDirectory(existingPath, name);
|
|
322
334
|
}
|
|
323
335
|
|
|
324
|
-
//
|
|
336
|
+
// New worktrees always land in the canonical container, even when a stale
|
|
337
|
+
// legacy directory was just cleaned up.
|
|
325
338
|
const wtDir = worktreesDir(basePath);
|
|
339
|
+
const wtPath = join(wtDir, name);
|
|
326
340
|
mkdirSync(wtDir, { recursive: true });
|
|
327
341
|
|
|
342
|
+
// When existingPath resolved to a legacy location, the canonical target may
|
|
343
|
+
// still hold a stale directory from a prior aborted creation (no .git marker).
|
|
344
|
+
// Remove it so git worktree add does not fail with "path already exists".
|
|
345
|
+
if (existingPath !== wtPath && existsSync(wtPath) && !isRegisteredGitWorktreeAtPath(basePath, wtPath)) {
|
|
346
|
+
removeStaleWorktreeDirectory(wtPath, name);
|
|
347
|
+
}
|
|
348
|
+
|
|
328
349
|
// Prune any stale worktree entries from a previous removal
|
|
329
350
|
nativeWorktreePrune(basePath);
|
|
330
351
|
|
|
@@ -405,7 +426,8 @@ export function createWorktree(basePath: string, name: string, opts: { branch?:
|
|
|
405
426
|
|
|
406
427
|
/**
|
|
407
428
|
* List all GSD-managed worktrees.
|
|
408
|
-
* Uses native worktree list and filters to those under
|
|
429
|
+
* Uses native worktree list and filters to those under a GSD worktrees
|
|
430
|
+
* container (canonical .gsd-worktrees/ or legacy .gsd/worktrees/).
|
|
409
431
|
*/
|
|
410
432
|
export function listWorktrees(basePath: string): WorktreeInfo[] {
|
|
411
433
|
basePath = normalizeBasePathForWorktreeOps(basePath);
|
|
@@ -416,12 +438,8 @@ export function listWorktrees(basePath: string): WorktreeInfo[] {
|
|
|
416
438
|
}
|
|
417
439
|
const seenRoots = new Set<string>();
|
|
418
440
|
const worktreeRoots = baseVariants
|
|
419
|
-
.
|
|
420
|
-
|
|
421
|
-
return {
|
|
422
|
-
normalized: normalizePathForComparison(path),
|
|
423
|
-
};
|
|
424
|
-
})
|
|
441
|
+
.flatMap(baseVariant => worktreesDirs(baseVariant))
|
|
442
|
+
.map(path => ({ normalized: normalizePathForComparison(path) }))
|
|
425
443
|
.filter(root => {
|
|
426
444
|
if (seenRoots.has(root.normalized)) return false;
|
|
427
445
|
seenRoots.add(root.normalized);
|
|
@@ -794,6 +812,7 @@ export function removeWorktree(
|
|
|
794
812
|
* This module uses a split representation (paths/exact/prefixes) for efficient matching.
|
|
795
813
|
*/
|
|
796
814
|
const SKIP_PATHS = [
|
|
815
|
+
".gsd-worktrees/",
|
|
797
816
|
".gsd/worktrees/",
|
|
798
817
|
".gsd/runtime/",
|
|
799
818
|
".gsd/activity/",
|