@opengsd/gsd-pi 1.4.0-dev.c18009cd → 1.5.0-dev.4fb2d460
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 +10 -10
- package/dist/resources/.managed-resources-content-hash +1 -1
- package/dist/resources/extensions/ask-user-questions.js +1 -1
- package/dist/resources/extensions/gsd/auto/closeout.js +97 -5
- package/dist/resources/extensions/gsd/auto/orchestrator.js +4 -3
- package/dist/resources/extensions/gsd/auto/pre-dispatch.js +6 -3
- package/dist/resources/extensions/gsd/auto/unit-phase.js +5 -9
- package/dist/resources/extensions/gsd/auto-artifact-paths.js +12 -3
- package/dist/resources/extensions/gsd/auto-direct-dispatch.js +11 -7
- package/dist/resources/extensions/gsd/auto-dispatch.js +16 -2
- package/dist/resources/extensions/gsd/auto-recovery.js +50 -6
- package/dist/resources/extensions/gsd/auto-start.js +4 -3
- package/dist/resources/extensions/gsd/auto-worktree-branch-lifecycle.js +189 -0
- package/dist/resources/extensions/gsd/auto-worktree-cleanup.js +133 -0
- package/dist/resources/extensions/gsd/auto-worktree-conflict-auto-resolve.js +35 -0
- package/dist/resources/extensions/gsd/auto-worktree-creation.js +130 -0
- package/dist/resources/extensions/gsd/auto-worktree-entry.js +118 -0
- package/dist/resources/extensions/gsd/auto-worktree-merge-already-merged.js +98 -0
- package/dist/resources/extensions/gsd/auto-worktree-merge-branch-head.js +52 -0
- package/dist/resources/extensions/gsd/auto-worktree-merge-cleanup.js +87 -0
- package/dist/resources/extensions/gsd/auto-worktree-merge-code-changes.js +46 -0
- package/dist/resources/extensions/gsd/auto-worktree-merge-db-ready.js +68 -0
- package/dist/resources/extensions/gsd/auto-worktree-merge-integration-branch.js +51 -0
- package/dist/resources/extensions/gsd/auto-worktree-merge-message.js +106 -0
- package/dist/resources/extensions/gsd/auto-worktree-merge-pre-teardown.js +64 -0
- package/dist/resources/extensions/gsd/auto-worktree-merge-stash.js +168 -0
- package/dist/resources/extensions/gsd/auto-worktree-merge.js +302 -0
- package/dist/resources/extensions/gsd/auto-worktree-milestone-shelter.js +119 -0
- package/dist/resources/extensions/gsd/auto-worktree-path-resolution.js +5 -0
- package/dist/resources/extensions/gsd/auto-worktree-path-utils.js +5 -0
- package/dist/resources/extensions/gsd/auto-worktree-resource-version.js +41 -0
- package/dist/resources/extensions/gsd/auto-worktree-runtime-cleanup.js +99 -0
- package/dist/resources/extensions/gsd/auto-worktree-session-registry.js +57 -0
- package/dist/resources/extensions/gsd/auto-worktree-sync.js +249 -0
- package/dist/resources/extensions/gsd/auto-worktree-teardown.js +120 -0
- package/dist/resources/extensions/gsd/auto-worktree.js +20 -2073
- package/dist/resources/extensions/gsd/auto.js +9 -3
- package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +1 -1
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +20 -2
- package/dist/resources/extensions/gsd/bootstrap/system-context.js +1 -1
- package/dist/resources/extensions/gsd/bootstrap/tool-call-loop-guard.js +185 -27
- package/dist/resources/extensions/gsd/codebase-generator.js +7 -1
- package/dist/resources/extensions/gsd/commands-handlers.js +1 -1
- package/dist/resources/extensions/gsd/commands-workflow-templates.js +3 -3
- package/dist/resources/extensions/gsd/db/queries.js +9 -9
- package/dist/resources/extensions/gsd/db/writers/cascades.js +20 -2
- package/dist/resources/extensions/gsd/doctor-engine-checks.js +93 -11
- package/dist/resources/extensions/gsd/flat-phase-migration.js +32 -3
- package/dist/resources/extensions/gsd/forensics.js +1 -1
- package/dist/resources/extensions/gsd/git-conflict-resolve.js +2 -2
- package/dist/resources/extensions/gsd/git-service.js +45 -0
- package/dist/resources/extensions/gsd/gsd-db.js +23 -11
- package/dist/resources/extensions/gsd/guided-flow.js +22 -17
- package/dist/resources/extensions/gsd/markdown-renderer.js +15 -3
- package/dist/resources/extensions/gsd/md-importer.js +89 -23
- package/dist/resources/extensions/gsd/migration-auto-check.js +30 -3
- package/dist/resources/extensions/gsd/milestone-merge-transaction.js +11 -0
- package/dist/resources/extensions/gsd/milestone-settlement.js +1 -1
- package/dist/resources/extensions/gsd/parallel-merge.js +1 -1
- package/dist/resources/extensions/gsd/parallel-orchestrator.js +3 -1
- package/dist/resources/extensions/gsd/paths.js +17 -3
- package/dist/resources/extensions/gsd/preferences-skills.js +10 -16
- package/dist/resources/extensions/gsd/preferences-types.js +1 -0
- package/dist/resources/extensions/gsd/preferences-validation.js +82 -0
- package/dist/resources/extensions/gsd/preferences.js +20 -0
- package/dist/resources/extensions/gsd/prompts/system.md +6 -6
- package/dist/resources/extensions/gsd/quick.js +12 -3
- package/dist/resources/extensions/gsd/skill-discovery.js +36 -23
- package/dist/resources/extensions/gsd/slice-parallel-orchestrator.js +2 -1
- package/dist/resources/extensions/gsd/state/derive/from-db.js +88 -153
- package/dist/resources/extensions/gsd/tool-contract.js +14 -0
- package/dist/resources/extensions/gsd/tools/reopen-slice.js +5 -0
- package/dist/resources/extensions/gsd/tools/reopen-task.js +13 -5
- package/dist/resources/extensions/gsd/unit-registry.js +12 -2
- package/dist/resources/extensions/gsd/unmerged-milestone-guard.js +1 -1
- package/dist/resources/extensions/gsd/validation-block-guard.js +2 -1
- package/dist/resources/extensions/gsd/workspace-git-preflight.js +2 -2
- package/dist/resources/extensions/gsd/worktree-command.js +1 -1
- package/dist/resources/extensions/gsd/worktree-lifecycle.js +14 -9
- package/dist/resources/extensions/gsd/worktree-reentry.js +2 -1
- package/dist/resources/extensions/shared/interview-ui.js +62 -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 +8 -8
- package/dist/web/standalone/.next/build-manifest.json +2 -2
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- 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/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 +8 -8
- package/dist/web/standalone/.next/server/middleware-build-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/node_modules/@gsd/native/package.json +1 -1
- package/dist/web/standalone/node_modules/node-pty/build/Makefile +1 -1
- package/integrations/hermes/open_gsd_hermes/gsd_client.py +24 -35
- package/integrations/hermes/tests/test_gsd_client_timeout.py +4 -4
- package/package.json +6 -6
- package/packages/cloud-mcp-gateway/package.json +2 -2
- package/packages/contracts/package.json +1 -1
- package/packages/daemon/package.json +4 -4
- package/packages/gsd-agent-core/dist/export-html/template.js +53 -4
- package/packages/gsd-agent-core/package.json +5 -5
- 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 +3 -2
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
- package/packages/gsd-agent-modes/package.json +7 -7
- package/packages/mcp-server/package.json +4 -4
- package/packages/native/package.json +1 -1
- package/packages/pi-agent-core/dist/agent-loop.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/agent-loop.js +14 -9
- package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
- package/packages/pi-agent-core/package.json +1 -1
- package/packages/pi-ai/dist/index.d.ts +1 -0
- package/packages/pi-ai/dist/index.d.ts.map +1 -1
- package/packages/pi-ai/dist/index.js +1 -0
- package/packages/pi-ai/dist/index.js.map +1 -1
- package/packages/pi-ai/dist/providers/transform-messages.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/transform-messages.js +7 -3
- package/packages/pi-ai/dist/providers/transform-messages.js.map +1 -1
- package/packages/pi-ai/dist/tool-result-content.d.ts +3 -0
- package/packages/pi-ai/dist/tool-result-content.d.ts.map +1 -0
- package/packages/pi-ai/dist/tool-result-content.js +62 -0
- package/packages/pi-ai/dist/tool-result-content.js.map +1 -0
- package/packages/pi-ai/dist/types.d.ts +8 -20
- package/packages/pi-ai/dist/types.d.ts.map +1 -1
- package/packages/pi-ai/dist/types.js.map +1 -1
- package/packages/pi-ai/package.json +1 -1
- package/packages/pi-coding-agent/dist/core/package-manager.d.ts +9 -0
- package/packages/pi-coding-agent/dist/core/package-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/package-manager.js +74 -53
- package/packages/pi-coding-agent/dist/core/package-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/resource-loader.d.ts +6 -0
- package/packages/pi-coding-agent/dist/core/resource-loader.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/resource-loader.js +30 -2
- package/packages/pi-coding-agent/dist/core/resource-loader.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/skill-directories.d.ts +66 -0
- package/packages/pi-coding-agent/dist/core/skill-directories.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/skill-directories.js +138 -0
- package/packages/pi-coding-agent/dist/core/skill-directories.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/skills.d.ts +4 -1
- package/packages/pi-coding-agent/dist/core/skills.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/skills.js +4 -1
- package/packages/pi-coding-agent/dist/core/skills.js.map +1 -1
- package/packages/pi-coding-agent/dist/index.d.ts +1 -0
- package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/index.js +1 -0
- package/packages/pi-coding-agent/dist/index.js.map +1 -1
- package/packages/pi-coding-agent/package.json +7 -7
- package/packages/pi-tui/package.json +2 -2
- package/packages/rpc-client/package.json +2 -2
- package/pkg/dist/core/export-html/template.js +53 -4
- package/pkg/package.json +1 -1
- package/src/resources/extensions/ask-user-questions.ts +1 -1
- package/src/resources/extensions/gsd/auto/closeout.ts +114 -7
- package/src/resources/extensions/gsd/auto/orchestrator.ts +4 -3
- package/src/resources/extensions/gsd/auto/pre-dispatch.ts +6 -3
- package/src/resources/extensions/gsd/auto/unit-phase.ts +5 -9
- package/src/resources/extensions/gsd/auto-artifact-paths.ts +12 -3
- package/src/resources/extensions/gsd/auto-direct-dispatch.ts +13 -7
- package/src/resources/extensions/gsd/auto-dispatch.ts +18 -2
- package/src/resources/extensions/gsd/auto-recovery.ts +50 -6
- package/src/resources/extensions/gsd/auto-start.ts +5 -3
- package/src/resources/extensions/gsd/auto-worktree-branch-lifecycle.ts +237 -0
- package/src/resources/extensions/gsd/auto-worktree-cleanup.ts +166 -0
- package/src/resources/extensions/gsd/auto-worktree-conflict-auto-resolve.ts +37 -0
- package/src/resources/extensions/gsd/auto-worktree-creation.ts +171 -0
- package/src/resources/extensions/gsd/auto-worktree-entry.ts +154 -0
- package/src/resources/extensions/gsd/auto-worktree-merge-already-merged.ts +161 -0
- package/src/resources/extensions/gsd/auto-worktree-merge-branch-head.ts +83 -0
- package/src/resources/extensions/gsd/auto-worktree-merge-cleanup.ts +132 -0
- package/src/resources/extensions/gsd/auto-worktree-merge-code-changes.ts +71 -0
- package/src/resources/extensions/gsd/auto-worktree-merge-db-ready.ts +112 -0
- package/src/resources/extensions/gsd/auto-worktree-merge-integration-branch.ts +99 -0
- package/src/resources/extensions/gsd/auto-worktree-merge-message.ts +166 -0
- package/src/resources/extensions/gsd/auto-worktree-merge-pre-teardown.ts +93 -0
- package/src/resources/extensions/gsd/auto-worktree-merge-stash.ts +213 -0
- package/src/resources/extensions/gsd/auto-worktree-merge.ts +391 -0
- package/src/resources/extensions/gsd/auto-worktree-milestone-shelter.ts +159 -0
- package/src/resources/extensions/gsd/auto-worktree-path-resolution.ts +6 -0
- package/src/resources/extensions/gsd/auto-worktree-path-utils.ts +10 -0
- package/src/resources/extensions/gsd/auto-worktree-resource-version.ts +44 -0
- package/src/resources/extensions/gsd/auto-worktree-runtime-cleanup.ts +119 -0
- package/src/resources/extensions/gsd/auto-worktree-session-registry.ts +71 -0
- package/src/resources/extensions/gsd/auto-worktree-sync.ts +370 -0
- package/src/resources/extensions/gsd/auto-worktree-teardown.ts +161 -0
- package/src/resources/extensions/gsd/auto-worktree.ts +69 -2429
- package/src/resources/extensions/gsd/auto.ts +11 -17
- package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +1 -1
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +20 -2
- package/src/resources/extensions/gsd/bootstrap/system-context.ts +1 -1
- package/src/resources/extensions/gsd/bootstrap/tool-call-loop-guard.ts +218 -27
- package/src/resources/extensions/gsd/codebase-generator.ts +7 -1
- package/src/resources/extensions/gsd/commands-handlers.ts +1 -1
- package/src/resources/extensions/gsd/commands-workflow-templates.ts +3 -3
- package/src/resources/extensions/gsd/db/queries.ts +9 -9
- package/src/resources/extensions/gsd/db/writers/cascades.ts +23 -1
- package/src/resources/extensions/gsd/doctor-engine-checks.ts +109 -10
- package/src/resources/extensions/gsd/flat-phase-migration.ts +34 -3
- package/src/resources/extensions/gsd/forensics.ts +1 -1
- package/src/resources/extensions/gsd/git-conflict-resolve.ts +2 -2
- package/src/resources/extensions/gsd/git-service.ts +53 -0
- package/src/resources/extensions/gsd/gsd-db.ts +33 -11
- package/src/resources/extensions/gsd/guided-flow.ts +26 -18
- package/src/resources/extensions/gsd/markdown-renderer.ts +15 -3
- package/src/resources/extensions/gsd/md-importer.ts +93 -22
- package/src/resources/extensions/gsd/migration-auto-check.ts +32 -3
- package/src/resources/extensions/gsd/milestone-merge-transaction.ts +15 -0
- package/src/resources/extensions/gsd/milestone-settlement.ts +1 -1
- package/src/resources/extensions/gsd/parallel-merge.ts +1 -1
- package/src/resources/extensions/gsd/parallel-orchestrator.ts +3 -1
- package/src/resources/extensions/gsd/paths.ts +21 -3
- package/src/resources/extensions/gsd/preferences-skills.ts +10 -16
- package/src/resources/extensions/gsd/preferences-types.ts +30 -0
- package/src/resources/extensions/gsd/preferences-validation.ts +69 -0
- package/src/resources/extensions/gsd/preferences.ts +20 -0
- package/src/resources/extensions/gsd/prompts/system.md +6 -6
- package/src/resources/extensions/gsd/quick.ts +11 -3
- package/src/resources/extensions/gsd/skill-discovery.ts +47 -23
- package/src/resources/extensions/gsd/slice-parallel-orchestrator.ts +3 -1
- package/src/resources/extensions/gsd/state/derive/from-db.ts +201 -157
- package/src/resources/extensions/gsd/tests/auto-artifact-paths.test.ts +169 -1
- package/src/resources/extensions/gsd/tests/auto-loop-no-copy-artifacts.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +57 -0
- package/src/resources/extensions/gsd/tests/auto-start-clean-runtime-db-gated.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/auto-warning-noise-regression.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/auto-worktree-auto-resolve.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/auto-worktree-merge-already-merged.test.ts +177 -0
- package/src/resources/extensions/gsd/tests/auto-worktree-merge-branch-head.test.ts +118 -0
- package/src/resources/extensions/gsd/tests/auto-worktree-merge-cleanup.test.ts +115 -0
- package/src/resources/extensions/gsd/tests/auto-worktree-merge-code-changes.test.ts +76 -0
- package/src/resources/extensions/gsd/tests/auto-worktree-merge-db-ready.test.ts +140 -0
- package/src/resources/extensions/gsd/tests/auto-worktree-merge-integration-branch.test.ts +123 -0
- package/src/resources/extensions/gsd/tests/auto-worktree-merge-message.test.ts +114 -0
- package/src/resources/extensions/gsd/tests/auto-worktree-merge-pre-teardown.test.ts +84 -0
- package/src/resources/extensions/gsd/tests/auto-worktree-merge-stash.test.ts +70 -0
- package/src/resources/extensions/gsd/tests/auto-worktree-milestone-shelter.test.ts +52 -0
- package/src/resources/extensions/gsd/tests/auto-worktree-registry.test.ts +6 -6
- package/src/resources/extensions/gsd/tests/auto-worktree-resource-version.test.ts +59 -0
- package/src/resources/extensions/gsd/tests/auto-worktree-untracked-content.test.ts +3 -1
- package/src/resources/extensions/gsd/tests/codebase-generator.test.ts +22 -0
- package/src/resources/extensions/gsd/tests/completed-units-metrics-sync.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +4 -0
- package/src/resources/extensions/gsd/tests/doctor-scope-db-unavailable.test.ts +174 -0
- package/src/resources/extensions/gsd/tests/fast-forward-reused-milestone-branch.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/flat-phase-migration.test.ts +71 -1
- package/src/resources/extensions/gsd/tests/flat-phase-renderer.test.ts +25 -1
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +40 -0
- package/src/resources/extensions/gsd/tests/import-completion-lost-update.test.ts +196 -0
- package/src/resources/extensions/gsd/tests/integration/all-milestones-complete-merge.test.ts +4 -6
- package/src/resources/extensions/gsd/tests/integration/auto-stash-merge.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/integration/auto-worktree-milestone-merge.test.ts +3 -3
- package/src/resources/extensions/gsd/tests/integration/auto-worktree.test.ts +7 -8
- package/src/resources/extensions/gsd/tests/integration/feature-branch-lifecycle-integration.test.ts +2 -5
- package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +76 -0
- package/src/resources/extensions/gsd/tests/integration/merge-cwd-restore.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/integration/merge-preserve-worktree-on-preteardown-error.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/integration/merge-strategy-regular.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/integration/milestone-transition-worktree.test.ts +5 -7
- package/src/resources/extensions/gsd/tests/integration/workspace-collapse-integration.test.ts +2 -4
- package/src/resources/extensions/gsd/tests/journal-integration.test.ts +16 -15
- package/src/resources/extensions/gsd/tests/merge-closeout-consistency-gate.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/merge-db-cycle.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/merge-self-branch-guard.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +91 -0
- package/src/resources/extensions/gsd/tests/milestone-merge-stash-restore.test.ts +439 -0
- package/src/resources/extensions/gsd/tests/milestone-merge-transaction.test.ts +24 -0
- package/src/resources/extensions/gsd/tests/none-mode-gates.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/originalbase-path-comparison.test.ts +3 -3
- package/src/resources/extensions/gsd/tests/parallel-worker-lock-contention.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/preferences-worktree-sync.test.ts +1 -4
- package/src/resources/extensions/gsd/tests/preferences.test.ts +19 -0
- package/src/resources/extensions/gsd/tests/prefs-wizard-coverage.test.ts +5 -0
- package/src/resources/extensions/gsd/tests/reopen-slice.test.ts +52 -0
- package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +20 -0
- package/src/resources/extensions/gsd/tests/skill-discovery.test.ts +46 -0
- package/src/resources/extensions/gsd/tests/stale-worktree-cwd.test.ts +3 -5
- package/src/resources/extensions/gsd/tests/stash-pop-gsd-conflict.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/stash-queued-context-files.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/steer-worktree-path.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/sync-worktree-skip-current.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/system-flat-phase-layout.test.ts +33 -0
- package/src/resources/extensions/gsd/tests/teardown-chdir-failure-clears-registry.test.ts +4 -4
- package/src/resources/extensions/gsd/tests/teardown-cleanup-parity.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/teardown-failure-clears-registry.test.ts +4 -4
- package/src/resources/extensions/gsd/tests/tool-call-loop-guard.test.ts +196 -0
- package/src/resources/extensions/gsd/tests/uat-stuck-loop-orphaned-worktree.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/unit-registry.test.ts +26 -1
- package/src/resources/extensions/gsd/tests/worktree-db-integration.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/worktree-db-respawn-truncation.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/worktree-db-same-file.test.ts +1 -3
- package/src/resources/extensions/gsd/tests/worktree-expected-warnings.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/worktree-git-pathspec.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/worktree-main-branch.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/worktree-preferences-sync.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/worktree-reentry.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +5 -2
- package/src/resources/extensions/gsd/tests/worktree-sync-overwrite-loop.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/worktree-sync-tasks.test.ts +1 -1
- package/src/resources/extensions/gsd/tool-contract.ts +29 -0
- package/src/resources/extensions/gsd/tools/reopen-slice.ts +5 -0
- package/src/resources/extensions/gsd/tools/reopen-task.ts +13 -5
- package/src/resources/extensions/gsd/unit-registry.ts +16 -2
- package/src/resources/extensions/gsd/unmerged-milestone-guard.ts +1 -1
- package/src/resources/extensions/gsd/validation-block-guard.ts +2 -1
- package/src/resources/extensions/gsd/workspace-git-preflight.ts +2 -2
- package/src/resources/extensions/gsd/worktree-command.ts +1 -1
- package/src/resources/extensions/gsd/worktree-lifecycle.ts +19 -15
- package/src/resources/extensions/gsd/worktree-reentry.ts +2 -1
- package/src/resources/extensions/shared/interview-ui.ts +58 -11
- package/src/resources/extensions/shared/tests/interview-ui-border.test.ts +32 -0
- /package/dist/web/standalone/.next/static/{L2UMqXELiDH3oz-7yguXp → gXoWec4UNqcR0tkuHwYCr}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{L2UMqXELiDH3oz-7yguXp → gXoWec4UNqcR0tkuHwYCr}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
// gsd-pi — Auto-worktree runtime cleanup module.
|
|
2
|
+
//
|
|
3
|
+
// Owns stale worktree cwd escape and runtime unit cleanup during auto startup.
|
|
4
|
+
import { existsSync, readdirSync, unlinkSync } from "node:fs";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import { gsdHome } from "./gsd-home.js";
|
|
7
|
+
import { MILESTONE_ID_RE } from "./milestone-ids.js";
|
|
8
|
+
import { normalizeWorktreePathForCompare, projectRootFromWorktreePath, } from "./worktree-root.js";
|
|
9
|
+
import { logWarning } from "./workflow-logger.js";
|
|
10
|
+
const LEGACY_DEEP_SETUP_RUNTIME_UNIT_FILES = new Set([
|
|
11
|
+
"workflow-preferences-WORKFLOW-PREFS.json",
|
|
12
|
+
"discuss-project-PROJECT.json",
|
|
13
|
+
"discuss-requirements-REQUIREMENTS.json",
|
|
14
|
+
"research-decision-RESEARCH-DECISION.json",
|
|
15
|
+
"research-project-RESEARCH-PROJECT.json",
|
|
16
|
+
]);
|
|
17
|
+
/**
|
|
18
|
+
* Detect and escape a stale worktree cwd (#608).
|
|
19
|
+
*
|
|
20
|
+
* After milestone completion + merge, the worktree directory is removed but
|
|
21
|
+
* the process cwd may still point inside `.gsd/worktrees/<MID>/`.
|
|
22
|
+
* When a new session starts, `process.cwd()` is passed as `base` to startAuto
|
|
23
|
+
* and all subsequent writes land in the wrong directory. This function detects
|
|
24
|
+
* that scenario and chdir back to the project root.
|
|
25
|
+
*
|
|
26
|
+
* Returns the corrected base path.
|
|
27
|
+
*/
|
|
28
|
+
export function escapeStaleWorktree(base) {
|
|
29
|
+
const projectRoot = projectRootFromWorktreePath(base);
|
|
30
|
+
if (projectRoot === null)
|
|
31
|
+
return base;
|
|
32
|
+
// Guard: If the candidate project root's .gsd IS the user-level ~/.gsd,
|
|
33
|
+
// the string-slice heuristic matched the wrong /.gsd/ boundary. This happens
|
|
34
|
+
// when .gsd is a symlink into ~/.gsd/projects/<hash> and process.cwd()
|
|
35
|
+
// resolved through the symlink. Returning ~ would be catastrophic (#1676).
|
|
36
|
+
const candidateGsd = normalizeWorktreePathForCompare(join(projectRoot, ".gsd"));
|
|
37
|
+
const gsdHomeNorm = normalizeWorktreePathForCompare(gsdHome());
|
|
38
|
+
if (candidateGsd === gsdHomeNorm || candidateGsd.startsWith(gsdHomeNorm + "/")) {
|
|
39
|
+
// Don't chdir to home — return base unchanged.
|
|
40
|
+
// resolveProjectRoot() in worktree.ts has the full git-file-based recovery
|
|
41
|
+
// and will be called by the caller (startAuto → projectRoot()).
|
|
42
|
+
return base;
|
|
43
|
+
}
|
|
44
|
+
try {
|
|
45
|
+
process.chdir(projectRoot);
|
|
46
|
+
}
|
|
47
|
+
catch (e) {
|
|
48
|
+
// If chdir fails, return the original — caller will handle errors downstream
|
|
49
|
+
logWarning("worktree", `escapeStaleWorktree chdir failed: ${e.message}`);
|
|
50
|
+
return base;
|
|
51
|
+
}
|
|
52
|
+
return projectRoot;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Clean stale runtime unit files for completed milestones.
|
|
56
|
+
*
|
|
57
|
+
* After restart, stale runtime/units/*.json from prior milestones can
|
|
58
|
+
* cause deriveState to resume the wrong milestone (#887). Removes files
|
|
59
|
+
* for milestones that have a SUMMARY (fully complete).
|
|
60
|
+
*/
|
|
61
|
+
export function cleanStaleRuntimeUnits(gsdRootPath, hasMilestoneSummary) {
|
|
62
|
+
const runtimeUnitsDir = join(gsdRootPath, "runtime", "units");
|
|
63
|
+
if (!existsSync(runtimeUnitsDir))
|
|
64
|
+
return 0;
|
|
65
|
+
let cleaned = 0;
|
|
66
|
+
try {
|
|
67
|
+
for (const file of readdirSync(runtimeUnitsDir)) {
|
|
68
|
+
if (!file.endsWith(".json"))
|
|
69
|
+
continue;
|
|
70
|
+
if (shouldRemoveRuntimeUnit(file, hasMilestoneSummary)) {
|
|
71
|
+
cleaned += unlinkRuntimeUnit(runtimeUnitsDir, file);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
catch (err) {
|
|
76
|
+
logWarning("worktree", `stale runtime unit cleanup failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
77
|
+
}
|
|
78
|
+
return cleaned;
|
|
79
|
+
}
|
|
80
|
+
function shouldRemoveRuntimeUnit(file, hasMilestoneSummary) {
|
|
81
|
+
if (LEGACY_DEEP_SETUP_RUNTIME_UNIT_FILES.has(file))
|
|
82
|
+
return true;
|
|
83
|
+
const staleDiscussMatch = file.match(/^discuss-milestone-(.+)\.json$/);
|
|
84
|
+
if (staleDiscussMatch && !MILESTONE_ID_RE.test(staleDiscussMatch[1])) {
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
const midMatch = file.match(/(M\d+(?:-[a-z0-9]{6})?)/);
|
|
88
|
+
return Boolean(midMatch && hasMilestoneSummary(midMatch[1]));
|
|
89
|
+
}
|
|
90
|
+
function unlinkRuntimeUnit(runtimeUnitsDir, file) {
|
|
91
|
+
try {
|
|
92
|
+
unlinkSync(join(runtimeUnitsDir, file));
|
|
93
|
+
return 1;
|
|
94
|
+
}
|
|
95
|
+
catch (err) {
|
|
96
|
+
logWarning("worktree", `stale runtime unit unlink failed (${file}): ${err instanceof Error ? err.message : String(err)}`);
|
|
97
|
+
return 0;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// gsd-pi — Auto-worktree active session registry.
|
|
2
|
+
//
|
|
3
|
+
// Owns the process-local active workspace state used by auto-worktree create,
|
|
4
|
+
// enter, teardown, and merge paths. Keeping this state behind a focused module
|
|
5
|
+
// gives registry tests the same interface production uses instead of reaching
|
|
6
|
+
// through the legacy auto-worktree compatibility barrel.
|
|
7
|
+
import { nativeGetCurrentBranch } from "./native-git-bridge.js";
|
|
8
|
+
import { isGsdWorktreePath, normalizeWorktreePathForCompare, resolveWorktreeProjectRoot, } from "./worktree-root.js";
|
|
9
|
+
import { detectWorktreeName } from "./worktree.js";
|
|
10
|
+
/** Active workspace registry — replaces the legacy `originalBase` singleton. */
|
|
11
|
+
let activeWorkspace = null;
|
|
12
|
+
export function setActiveWorkspace(ws) {
|
|
13
|
+
activeWorkspace = ws;
|
|
14
|
+
}
|
|
15
|
+
export function getActiveWorkspace() {
|
|
16
|
+
return activeWorkspace;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Get the original project root stored when entering an auto-worktree.
|
|
20
|
+
* Returns null if not currently in an auto-worktree.
|
|
21
|
+
*/
|
|
22
|
+
export function getAutoWorktreeOriginalBase() {
|
|
23
|
+
return getActiveWorkspace()?.projectRoot ?? null;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Test-only — resets the module-level `activeWorkspace` registry between
|
|
27
|
+
* runs. Production code never clears the registry directly; tests call this
|
|
28
|
+
* in `beforeEach`/`afterEach` to isolate registry-mutating cases.
|
|
29
|
+
*/
|
|
30
|
+
export function _resetAutoWorktreeOriginalBaseForTests() {
|
|
31
|
+
setActiveWorkspace(null);
|
|
32
|
+
}
|
|
33
|
+
export function getActiveAutoWorktreeContext() {
|
|
34
|
+
const ws = getActiveWorkspace();
|
|
35
|
+
if (!ws)
|
|
36
|
+
return null;
|
|
37
|
+
const originalBase = ws.projectRoot;
|
|
38
|
+
const cwd = process.cwd();
|
|
39
|
+
if (!isGsdWorktreePath(cwd))
|
|
40
|
+
return null;
|
|
41
|
+
const cwdProjectRoot = resolveWorktreeProjectRoot(cwd, originalBase);
|
|
42
|
+
if (normalizeWorktreePathForCompare(cwdProjectRoot) !==
|
|
43
|
+
normalizeWorktreePathForCompare(originalBase)) {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
const worktreeName = detectWorktreeName(cwd);
|
|
47
|
+
if (!worktreeName)
|
|
48
|
+
return null;
|
|
49
|
+
const branch = nativeGetCurrentBranch(cwd);
|
|
50
|
+
if (!branch.startsWith("milestone/"))
|
|
51
|
+
return null;
|
|
52
|
+
return {
|
|
53
|
+
originalBase,
|
|
54
|
+
worktreeName,
|
|
55
|
+
branch,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
// gsd-pi — Auto-worktree state sync module.
|
|
2
|
+
//
|
|
3
|
+
// Owns project-root/worktree projection compatibility seams while the project DB
|
|
4
|
+
// remains authoritative for workflow state.
|
|
5
|
+
import { cpSync, existsSync, lstatSync as lstatSyncFn, mkdirSync, readdirSync, } from "node:fs";
|
|
6
|
+
import { join } from "node:path";
|
|
7
|
+
import { _isSamePath as isSamePath, } from "./auto-worktree-cleanup.js";
|
|
8
|
+
import { resolveGsdPathContract } from "./paths.js";
|
|
9
|
+
import { WorktreeStateProjection } from "./worktree-state-projection.js";
|
|
10
|
+
import { logWarning } from "./workflow-logger.js";
|
|
11
|
+
const PROJECT_PREFERENCES_FILE = "PREFERENCES.md";
|
|
12
|
+
const LEGACY_PROJECT_PREFERENCES_FILE = "preferences.md";
|
|
13
|
+
/**
|
|
14
|
+
* Root-level .gsd/ projections copied from project root into worktrees for
|
|
15
|
+
* compatibility. Project root remains the canonical state/projection root.
|
|
16
|
+
*/
|
|
17
|
+
const ROOT_STATE_FILES = [
|
|
18
|
+
"DECISIONS.md",
|
|
19
|
+
"REQUIREMENTS.md",
|
|
20
|
+
"PROJECT.md",
|
|
21
|
+
"KNOWLEDGE.md",
|
|
22
|
+
"OVERRIDES.md",
|
|
23
|
+
"QUEUE.md",
|
|
24
|
+
"completed-units.json",
|
|
25
|
+
"metrics.json",
|
|
26
|
+
"mcp.json",
|
|
27
|
+
// NOTE: project preferences are intentionally NOT in ROOT_STATE_FILES.
|
|
28
|
+
// Forward-sync (main → worktree) is handled explicitly in syncGsdStateToWorktree().
|
|
29
|
+
// Back-sync (worktree → main) must NEVER overwrite the project root's copy
|
|
30
|
+
// because the project root is authoritative for preferences (#2684).
|
|
31
|
+
];
|
|
32
|
+
/**
|
|
33
|
+
* Path-string entry point to WorktreeStateProjection.projectRootToWorktree.
|
|
34
|
+
* Production code goes through the Module class; this delegator survives so
|
|
35
|
+
* the projection-invariant tests (#1886, #2184, #2478, #2821) can exercise
|
|
36
|
+
* the bodies with raw paths.
|
|
37
|
+
*/
|
|
38
|
+
export function syncProjectRootToWorktree(projectRoot, worktreePath_, milestoneId) {
|
|
39
|
+
new WorktreeStateProjection().projectRootToWorktreePaths(projectRoot, worktreePath_, milestoneId);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Path-string entry point to WorktreeStateProjection.projectWorktreeToRoot.
|
|
43
|
+
* Production code goes through the Module class; this delegator survives so
|
|
44
|
+
* the projection-invariant tests can exercise the body with raw paths.
|
|
45
|
+
*/
|
|
46
|
+
export function syncStateToProjectRoot(worktreePath_, projectRoot, milestoneId) {
|
|
47
|
+
new WorktreeStateProjection().projectWorktreeToRootPaths(worktreePath_, projectRoot, milestoneId);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Scope-typed variant of syncGsdStateToWorktree.
|
|
51
|
+
*
|
|
52
|
+
* Takes an explicit (rootScope, worktreeScope) pair. Note: milestoneId is not
|
|
53
|
+
* used by syncGsdStateToWorktree — this variant only requires workspace
|
|
54
|
+
* identity. Asserts both scopes belong to the same workspace identity to
|
|
55
|
+
* prevent silent mismatch bugs.
|
|
56
|
+
*/
|
|
57
|
+
export function syncGsdStateToWorktreeByScope(rootScope, worktreeScope) {
|
|
58
|
+
if (rootScope.workspace.identityKey !== worktreeScope.workspace.identityKey) {
|
|
59
|
+
throw new Error(`syncGsdStateToWorktreeByScope: scope identity mismatch — ` +
|
|
60
|
+
`rootScope.identityKey="${rootScope.workspace.identityKey}" ` +
|
|
61
|
+
`worktreeScope.identityKey="${worktreeScope.workspace.identityKey}"`);
|
|
62
|
+
}
|
|
63
|
+
const mainBasePath = rootScope.workspace.projectRoot;
|
|
64
|
+
const worktreePath = worktreeScope.workspace.worktreeRoot
|
|
65
|
+
?? worktreeScope.workspace.projectRoot;
|
|
66
|
+
return syncGsdStateToWorktree(mainBasePath, worktreePath);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Sync .gsd/ state from the main repo into the worktree.
|
|
70
|
+
*
|
|
71
|
+
* When .gsd/ is a symlink to the external state directory, both the main
|
|
72
|
+
* repo and worktree share the same directory — no sync needed.
|
|
73
|
+
*
|
|
74
|
+
* When .gsd/ is a real directory (e.g., git-tracked or manage_gitignore:false),
|
|
75
|
+
* the worktree has its own copy that may be stale. This function copies
|
|
76
|
+
* missing milestones, CONTEXT, ROADMAP, DECISIONS, REQUIREMENTS, and
|
|
77
|
+
* PROJECT files from the main repo's .gsd/ into the worktree's .gsd/.
|
|
78
|
+
*
|
|
79
|
+
* Only adds missing content — never overwrites existing files in the worktree.
|
|
80
|
+
* Worktree files are compatibility projections; DB/project root remains
|
|
81
|
+
* authoritative for runtime state.
|
|
82
|
+
* @deprecated Use syncGsdStateToWorktreeByScope instead.
|
|
83
|
+
* TODO(C-future): remove once all callers migrated.
|
|
84
|
+
*/
|
|
85
|
+
export function syncGsdStateToWorktree(mainBasePath, worktreePath_) {
|
|
86
|
+
const contract = resolveGsdPathContract(worktreePath_, mainBasePath);
|
|
87
|
+
const mainGsd = contract.projectGsd;
|
|
88
|
+
const wtGsd = contract.worktreeGsd ?? join(worktreePath_, ".gsd");
|
|
89
|
+
const synced = [];
|
|
90
|
+
if (isSamePath(mainGsd, wtGsd))
|
|
91
|
+
return { synced };
|
|
92
|
+
if (!existsSync(mainGsd))
|
|
93
|
+
return { synced };
|
|
94
|
+
mkdirSync(wtGsd, { recursive: true });
|
|
95
|
+
syncRootStateFiles(mainGsd, wtGsd, synced);
|
|
96
|
+
syncProjectPreferences(mainGsd, wtGsd, synced);
|
|
97
|
+
syncMilestoneLayouts(mainGsd, wtGsd, synced);
|
|
98
|
+
return { synced };
|
|
99
|
+
}
|
|
100
|
+
function syncRootStateFiles(mainGsd, wtGsd, synced) {
|
|
101
|
+
for (const file of ROOT_STATE_FILES) {
|
|
102
|
+
const src = join(mainGsd, file);
|
|
103
|
+
const dst = join(wtGsd, file);
|
|
104
|
+
if (!existsSync(src) || existsSync(dst))
|
|
105
|
+
continue;
|
|
106
|
+
try {
|
|
107
|
+
cpSync(src, dst);
|
|
108
|
+
synced.push(file);
|
|
109
|
+
}
|
|
110
|
+
catch (err) {
|
|
111
|
+
logWarning("worktree", `file copy failed (${file}): ${err instanceof Error ? err.message : String(err)}`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
function syncProjectPreferences(mainGsd, wtGsd, synced) {
|
|
116
|
+
const worktreeHasPreferences = existsSync(join(wtGsd, PROJECT_PREFERENCES_FILE))
|
|
117
|
+
|| existsSync(join(wtGsd, LEGACY_PROJECT_PREFERENCES_FILE));
|
|
118
|
+
if (worktreeHasPreferences)
|
|
119
|
+
return;
|
|
120
|
+
for (const file of [PROJECT_PREFERENCES_FILE, LEGACY_PROJECT_PREFERENCES_FILE]) {
|
|
121
|
+
const src = join(mainGsd, file);
|
|
122
|
+
const dst = join(wtGsd, file);
|
|
123
|
+
if (!existsSync(src))
|
|
124
|
+
continue;
|
|
125
|
+
try {
|
|
126
|
+
cpSync(src, dst);
|
|
127
|
+
synced.push(file);
|
|
128
|
+
}
|
|
129
|
+
catch (err) {
|
|
130
|
+
logWarning("worktree", `preferences copy failed (${file}): ${err instanceof Error ? err.message : String(err)}`);
|
|
131
|
+
}
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
function syncMilestoneLayouts(mainGsd, wtGsd, synced) {
|
|
136
|
+
for (const layoutSegment of ["phases", "milestones"]) {
|
|
137
|
+
syncMilestoneLayout(mainGsd, wtGsd, layoutSegment, synced);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
function syncMilestoneLayout(mainGsd, wtGsd, layoutSegment, synced) {
|
|
141
|
+
const mainMilestonesDir = join(mainGsd, layoutSegment);
|
|
142
|
+
const wtMilestonesDir = join(wtGsd, layoutSegment);
|
|
143
|
+
if (!existsSync(mainMilestonesDir))
|
|
144
|
+
return;
|
|
145
|
+
try {
|
|
146
|
+
mkdirSync(wtMilestonesDir, { recursive: true });
|
|
147
|
+
const mainMilestones = readdirSync(mainMilestonesDir, { withFileTypes: true })
|
|
148
|
+
.filter((entry) => entry.isDirectory())
|
|
149
|
+
.map((entry) => entry.name);
|
|
150
|
+
for (const milestoneId of mainMilestones) {
|
|
151
|
+
syncMilestoneDirectory(mainMilestonesDir, wtMilestonesDir, milestoneId, synced);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
catch (err) {
|
|
155
|
+
logWarning("worktree", `milestone directory sync failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
function syncMilestoneDirectory(mainMilestonesDir, wtMilestonesDir, milestoneId, synced) {
|
|
159
|
+
const srcDir = join(mainMilestonesDir, milestoneId);
|
|
160
|
+
const dstDir = join(wtMilestonesDir, milestoneId);
|
|
161
|
+
if (!existsSync(dstDir)) {
|
|
162
|
+
try {
|
|
163
|
+
cpSync(srcDir, dstDir, { recursive: true });
|
|
164
|
+
// Preserve the legacy telemetry string used before this extraction. The
|
|
165
|
+
// actual layout may be `phases/`; callers only consumed this as a coarse
|
|
166
|
+
// "milestone projection copied" marker.
|
|
167
|
+
synced.push(`milestones/${milestoneId}/`);
|
|
168
|
+
}
|
|
169
|
+
catch (err) {
|
|
170
|
+
logWarning("worktree", `milestone copy failed (${milestoneId}): ${err instanceof Error ? err.message : String(err)}`);
|
|
171
|
+
}
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
try {
|
|
175
|
+
syncMilestoneTopLevelFiles(srcDir, dstDir, milestoneId, synced);
|
|
176
|
+
syncSlicesDirectory(srcDir, dstDir, milestoneId, synced);
|
|
177
|
+
}
|
|
178
|
+
catch (err) {
|
|
179
|
+
logWarning("worktree", `milestone file sync failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
function syncMilestoneTopLevelFiles(srcDir, dstDir, milestoneId, synced) {
|
|
183
|
+
const srcFiles = readdirSync(srcDir).filter((file) => file.endsWith(".md") || file.endsWith(".json"));
|
|
184
|
+
for (const file of srcFiles) {
|
|
185
|
+
const srcFile = join(srcDir, file);
|
|
186
|
+
const dstFile = join(dstDir, file);
|
|
187
|
+
if (existsSync(dstFile))
|
|
188
|
+
continue;
|
|
189
|
+
try {
|
|
190
|
+
const srcStat = lstatSyncFn(srcFile);
|
|
191
|
+
if (!srcStat.isFile())
|
|
192
|
+
continue;
|
|
193
|
+
cpSync(srcFile, dstFile);
|
|
194
|
+
synced.push(`milestones/${milestoneId}/${file}`);
|
|
195
|
+
}
|
|
196
|
+
catch (err) {
|
|
197
|
+
logWarning("worktree", `milestone file copy failed (${milestoneId}/${file}): ${err instanceof Error ? err.message : String(err)}`);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
function syncSlicesDirectory(srcDir, dstDir, milestoneId, synced) {
|
|
202
|
+
const srcSlicesDir = join(srcDir, "slices");
|
|
203
|
+
const dstSlicesDir = join(dstDir, "slices");
|
|
204
|
+
if (!existsSync(srcSlicesDir))
|
|
205
|
+
return;
|
|
206
|
+
if (!existsSync(dstSlicesDir)) {
|
|
207
|
+
try {
|
|
208
|
+
cpSync(srcSlicesDir, dstSlicesDir, { recursive: true });
|
|
209
|
+
synced.push(`milestones/${milestoneId}/slices/`);
|
|
210
|
+
}
|
|
211
|
+
catch (err) {
|
|
212
|
+
logWarning("worktree", `slices copy failed (${milestoneId}): ${err instanceof Error ? err.message : String(err)}`);
|
|
213
|
+
}
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
const srcSlices = readdirSync(srcSlicesDir, { withFileTypes: true })
|
|
217
|
+
.filter((entry) => entry.isDirectory())
|
|
218
|
+
.map((entry) => entry.name);
|
|
219
|
+
for (const sliceId of srcSlices) {
|
|
220
|
+
const srcSlice = join(srcSlicesDir, sliceId);
|
|
221
|
+
const dstSlice = join(dstSlicesDir, sliceId);
|
|
222
|
+
if (existsSync(dstSlice))
|
|
223
|
+
continue;
|
|
224
|
+
try {
|
|
225
|
+
cpSync(srcSlice, dstSlice, { recursive: true });
|
|
226
|
+
synced.push(`milestones/${milestoneId}/slices/${sliceId}/`);
|
|
227
|
+
}
|
|
228
|
+
catch (err) {
|
|
229
|
+
logWarning("worktree", `slice copy failed (${milestoneId}/${sliceId}): ${err instanceof Error ? err.message : String(err)}`);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Sync compatibility artifacts from worktree back to the main external state
|
|
235
|
+
* directory. Canonical workflow state lives in the project DB; worktree .gsd
|
|
236
|
+
* content is legacy projection/diagnostic data only.
|
|
237
|
+
*
|
|
238
|
+
* Syncs:
|
|
239
|
+
* 1. Legacy worktree DBs are reconciled into the canonical project DB.
|
|
240
|
+
* 2. Runtime diagnostic files may be copied for operator visibility.
|
|
241
|
+
*
|
|
242
|
+
* Markdown milestone directories are projections and are not copied from
|
|
243
|
+
* worktrees into the project root. Current workflow state must arrive through
|
|
244
|
+
* the shared project DB or the pre-upgrade DB reconciliation path above.
|
|
245
|
+
*/
|
|
246
|
+
export function syncWorktreeStateBack(mainBasePath, worktreePath, milestoneId) {
|
|
247
|
+
return new WorktreeStateProjection().finalizeProjectionForMergePaths(mainBasePath, worktreePath, milestoneId);
|
|
248
|
+
}
|
|
249
|
+
export { _shouldReconcileWorktreeDb } from "./auto-worktree-cleanup.js";
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
// gsd-pi — Auto-worktree teardown module.
|
|
2
|
+
//
|
|
3
|
+
// Owns the teardown path for milestone worktrees: returning to the project
|
|
4
|
+
// root, transient state cleanup, legacy DB reconciliation, worktree/branch
|
|
5
|
+
// removal, fallback orphan-directory cleanup, and active workspace registry
|
|
6
|
+
// clearing.
|
|
7
|
+
import { existsSync, rmSync } from "node:fs";
|
|
8
|
+
import { join } from "node:path";
|
|
9
|
+
import { GSDError, GSD_IO_ERROR } from "./errors.js";
|
|
10
|
+
import { reconcileWorktreeDb, isDbAvailable } from "./gsd-db.js";
|
|
11
|
+
import { resolveGsdPathContract } from "./paths.js";
|
|
12
|
+
import { removeWorktree, worktreePath, isInsideWorktreesDir, } from "./worktree-manager.js";
|
|
13
|
+
import { nudgeGitBranchCache } from "./worktree.js";
|
|
14
|
+
import { resolveWorktreeProjectRoot } from "./worktree-root.js";
|
|
15
|
+
import { autoWorktreeBranch } from "./auto-worktree-branch-lifecycle.js";
|
|
16
|
+
import { setActiveWorkspace } from "./auto-worktree-session-registry.js";
|
|
17
|
+
import { _shouldReconcileWorktreeDb, clearProjectRootStateFiles, } from "./auto-worktree-cleanup.js";
|
|
18
|
+
import { logWarning, logError } from "./workflow-logger.js";
|
|
19
|
+
function safeCwd(fallback) {
|
|
20
|
+
try {
|
|
21
|
+
return process.cwd();
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return fallback;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Teardown an auto-worktree: chdir back to original base, then remove
|
|
29
|
+
* the worktree and its branch.
|
|
30
|
+
*/
|
|
31
|
+
export function teardownAutoWorktree(originalBasePath, milestoneId, opts = {}) {
|
|
32
|
+
originalBasePath = resolveWorktreeProjectRoot(originalBasePath);
|
|
33
|
+
const branch = autoWorktreeBranch(milestoneId);
|
|
34
|
+
const { preserveBranch = false, preserveWorktree = false } = opts;
|
|
35
|
+
const previousCwd = safeCwd(originalBasePath);
|
|
36
|
+
let clearActiveWorkspace = true;
|
|
37
|
+
// Wrap the entire teardown body in a single try/finally so activeWorkspace
|
|
38
|
+
// is cleared on completion or irrecoverable failure — even if process.chdir
|
|
39
|
+
// throws (e.g. originalBasePath was deleted before teardown ran).
|
|
40
|
+
try {
|
|
41
|
+
try {
|
|
42
|
+
process.chdir(originalBasePath);
|
|
43
|
+
}
|
|
44
|
+
catch (err) {
|
|
45
|
+
throw new GSDError(GSD_IO_ERROR, `Failed to chdir back to ${originalBasePath} during teardown: ${err instanceof Error ? err.message : String(err)}`);
|
|
46
|
+
}
|
|
47
|
+
// Mirror cleanup steps from mergeMilestoneToMain abort path:
|
|
48
|
+
// 1. Remove transient state files (STATE.md, auto.lock, {MID}-META.json).
|
|
49
|
+
// Non-fatal — must not block teardown.
|
|
50
|
+
try {
|
|
51
|
+
clearProjectRootStateFiles(originalBasePath, milestoneId);
|
|
52
|
+
}
|
|
53
|
+
catch (err) {
|
|
54
|
+
logWarning("worktree", `clearProjectRootStateFiles failed during teardown: ${err instanceof Error ? err.message : String(err)}`);
|
|
55
|
+
}
|
|
56
|
+
// 2. Reconcile worktree-local gsd.db into project root DB if both exist.
|
|
57
|
+
// Non-fatal — handles legacy worktrees that have a local copy.
|
|
58
|
+
if (isDbAvailable()) {
|
|
59
|
+
try {
|
|
60
|
+
const contract = resolveGsdPathContract(previousCwd, originalBasePath);
|
|
61
|
+
const worktreeDbPath = join(contract.worktreeGsd ?? join(previousCwd, ".gsd"), "gsd.db");
|
|
62
|
+
const mainDbPath = contract.projectDb;
|
|
63
|
+
if (_shouldReconcileWorktreeDb(worktreeDbPath, mainDbPath)) {
|
|
64
|
+
reconcileWorktreeDb(mainDbPath, worktreeDbPath);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
catch (err) {
|
|
68
|
+
/* non-fatal */
|
|
69
|
+
logError("worktree", `DB reconciliation failed during teardown: ${err instanceof Error ? err.message : String(err)}`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
nudgeGitBranchCache(previousCwd);
|
|
73
|
+
// 3. Remove the worktree unless this exit path explicitly preserves it
|
|
74
|
+
// (slice-parallel dispatch stops the parent loop but keeps the parent
|
|
75
|
+
// milestone worktree for restart/re-entry).
|
|
76
|
+
let worktreeRemoved = preserveWorktree;
|
|
77
|
+
if (!preserveWorktree) {
|
|
78
|
+
worktreeRemoved = removeWorktree(originalBasePath, milestoneId, {
|
|
79
|
+
branch,
|
|
80
|
+
deleteBranch: !preserveBranch,
|
|
81
|
+
});
|
|
82
|
+
if (!worktreeRemoved) {
|
|
83
|
+
logWarning("worktree", `Worktree removal aborted for ${milestoneId}; uncommitted work was preserved. ` +
|
|
84
|
+
`Manual quarantine recovery may be needed before retrying teardown.`, { worktree: milestoneId });
|
|
85
|
+
clearActiveWorkspace = false;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
// Verify cleanup succeeded — warn if the worktree directory is still on disk.
|
|
89
|
+
// On Windows, bash-based cleanup can silently fail when paths contain
|
|
90
|
+
// backslashes (#1436), leaving ~1 GB+ orphaned directories.
|
|
91
|
+
const wtDir = worktreePath(originalBasePath, milestoneId);
|
|
92
|
+
if (!preserveWorktree && worktreeRemoved && existsSync(wtDir)) {
|
|
93
|
+
logWarning("reconcile", `Worktree directory still exists after teardown: ${wtDir}. ` +
|
|
94
|
+
`This is likely an orphaned directory consuming disk space. ` +
|
|
95
|
+
`Remove it manually with: rm -rf "${wtDir.replaceAll("\\", "/")}"`, { worktree: milestoneId });
|
|
96
|
+
// Attempt a direct filesystem removal as a fallback — but ONLY if the
|
|
97
|
+
// path is safely inside .gsd/worktrees/ to prevent #2365 data loss.
|
|
98
|
+
if (isInsideWorktreesDir(originalBasePath, wtDir)) {
|
|
99
|
+
try {
|
|
100
|
+
rmSync(wtDir, { recursive: true, force: true });
|
|
101
|
+
}
|
|
102
|
+
catch (err) {
|
|
103
|
+
// Non-fatal — the warning above tells the user how to clean up
|
|
104
|
+
logWarning("worktree", `worktree directory removal failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
console.error(`[GSD] REFUSING fallback rmSync — path is outside .gsd/worktrees/: ${wtDir}`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
finally {
|
|
113
|
+
// Clear module state when teardown completed or failed irrecoverably.
|
|
114
|
+
// When removeWorktree returns false (quarantine failure), preserve the
|
|
115
|
+
// registry so recovery logic still sees the active milestone worktree.
|
|
116
|
+
if (clearActiveWorkspace) {
|
|
117
|
+
setActiveWorkspace(null);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|