gsd-pi 2.78.1-dev.b0759e59b → 2.78.1-dev.e9d88a536
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 +8 -5
- package/dist/headless-recover.d.ts +23 -0
- package/dist/headless-recover.js +93 -0
- package/dist/headless.js +9 -0
- package/dist/help-text.js +1 -0
- package/dist/resources/.managed-resources-content-hash +1 -1
- package/dist/resources/extensions/browser-tools/tools/intent.js +8 -1
- package/dist/resources/extensions/gsd/auto-dispatch.js +4 -56
- package/dist/resources/extensions/gsd/auto-post-unit.js +7 -27
- package/dist/resources/extensions/gsd/auto-start.js +1 -8
- package/dist/resources/extensions/gsd/auto-worktree.js +59 -176
- package/dist/resources/extensions/gsd/auto.js +24 -6
- package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +9 -77
- package/dist/resources/extensions/gsd/commands-codebase.js +2 -2
- package/dist/resources/extensions/gsd/commands-handlers.js +5 -5
- package/dist/resources/extensions/gsd/commands-logs.js +2 -2
- package/dist/resources/extensions/gsd/commands-scan.js +2 -2
- package/dist/resources/extensions/gsd/commands-ship.js +2 -2
- package/dist/resources/extensions/gsd/commands-workflow-templates.js +5 -5
- package/dist/resources/extensions/gsd/db-writer.js +16 -85
- package/dist/resources/extensions/gsd/dispatch-guard.js +6 -10
- package/dist/resources/extensions/gsd/doctor-engine-checks.js +2 -2
- package/dist/resources/extensions/gsd/gsd-db.js +74 -8
- package/dist/resources/extensions/gsd/guided-flow.js +31 -8
- package/dist/resources/extensions/gsd/markdown-renderer.js +14 -51
- package/dist/resources/extensions/gsd/parallel-merge.js +14 -13
- package/dist/resources/extensions/gsd/parallel-monitor-overlay.js +5 -2
- package/dist/resources/extensions/gsd/paths.js +35 -1
- package/dist/resources/extensions/gsd/prompts/plan-milestone.md +6 -0
- package/dist/resources/extensions/gsd/queue-order.js +6 -1
- package/dist/resources/extensions/gsd/rethink.js +2 -2
- package/dist/resources/extensions/gsd/state.js +91 -372
- package/dist/resources/extensions/gsd/tools/complete-milestone.js +6 -5
- package/dist/resources/extensions/gsd/tools/complete-slice.js +7 -12
- package/dist/resources/extensions/gsd/tools/complete-task.js +19 -31
- package/dist/resources/extensions/gsd/tools/validate-milestone.js +7 -5
- package/dist/resources/extensions/gsd/workflow-manifest.js +2 -1
- package/dist/resources/extensions/gsd/workflow-mcp-auto-prep.js +3 -21
- package/dist/resources/extensions/gsd/workflow-reconcile.js +3 -3
- package/dist/resources/extensions/gsd/worktree-command.js +4 -3
- 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 +12 -12
- 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/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/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/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/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 +12 -12
- package/dist/web/standalone/.next/server/chunks/6336.js +1 -0
- package/dist/web/standalone/.next/server/chunks/6897.js +1 -1
- 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/package.json +1 -1
- package/packages/mcp-server/dist/workflow-tools.d.ts +6 -0
- package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.js +56 -2
- package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
- package/packages/mcp-server/src/parse-workflow-args.test.ts +80 -0
- package/packages/mcp-server/src/workflow-tools.ts +61 -2
- package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
- package/src/resources/extensions/browser-tools/tools/intent.ts +13 -2
- package/src/resources/extensions/gsd/auto-dispatch.ts +4 -60
- package/src/resources/extensions/gsd/auto-post-unit.ts +7 -26
- package/src/resources/extensions/gsd/auto-start.ts +1 -8
- package/src/resources/extensions/gsd/auto-worktree.ts +61 -204
- package/src/resources/extensions/gsd/auto.ts +23 -6
- package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +9 -84
- package/src/resources/extensions/gsd/commands-codebase.ts +2 -2
- package/src/resources/extensions/gsd/commands-handlers.ts +5 -5
- package/src/resources/extensions/gsd/commands-logs.ts +2 -2
- package/src/resources/extensions/gsd/commands-scan.ts +2 -2
- package/src/resources/extensions/gsd/commands-ship.ts +2 -2
- package/src/resources/extensions/gsd/commands-workflow-templates.ts +5 -5
- package/src/resources/extensions/gsd/db-writer.ts +16 -83
- package/src/resources/extensions/gsd/dispatch-guard.ts +6 -11
- package/src/resources/extensions/gsd/doctor-engine-checks.ts +2 -2
- package/src/resources/extensions/gsd/gsd-db.ts +85 -8
- package/src/resources/extensions/gsd/guided-flow.ts +35 -8
- package/src/resources/extensions/gsd/markdown-renderer.ts +13 -64
- package/src/resources/extensions/gsd/parallel-merge.ts +14 -13
- package/src/resources/extensions/gsd/parallel-monitor-overlay.ts +5 -2
- package/src/resources/extensions/gsd/paths.ts +55 -1
- package/src/resources/extensions/gsd/prompts/plan-milestone.md +6 -0
- package/src/resources/extensions/gsd/queue-order.ts +6 -1
- package/src/resources/extensions/gsd/rethink.ts +2 -2
- package/src/resources/extensions/gsd/state.ts +91 -389
- package/src/resources/extensions/gsd/tests/artifact-corruption-2630.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/auto-paused-session-validation.test.ts +6 -0
- package/src/resources/extensions/gsd/tests/auto-remediate-slice-status.test.ts +21 -34
- package/src/resources/extensions/gsd/tests/complete-task-rollback-evidence.test.ts +6 -7
- package/src/resources/extensions/gsd/tests/complete-task.test.ts +8 -6
- package/src/resources/extensions/gsd/tests/completed-at-reconcile.test.ts +12 -27
- package/src/resources/extensions/gsd/tests/completed-units-metrics-sync.test.ts +18 -5
- package/src/resources/extensions/gsd/tests/db-path-worktree-symlink.test.ts +4 -4
- package/src/resources/extensions/gsd/tests/db-writer.test.ts +14 -16
- package/src/resources/extensions/gsd/tests/derive-state-crossval.test.ts +6 -5
- package/src/resources/extensions/gsd/tests/derive-state-db-disk-reconcile.test.ts +10 -38
- package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +136 -56
- package/src/resources/extensions/gsd/tests/derive-state-draft.test.ts +3 -0
- package/src/resources/extensions/gsd/tests/derive-state-helpers.test.ts +119 -61
- package/src/resources/extensions/gsd/tests/derive-state.test.ts +4 -0
- package/src/resources/extensions/gsd/tests/dispatch-complete-milestone-guard.test.ts +6 -20
- package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +4 -5
- package/src/resources/extensions/gsd/tests/dispatcher-stuck-planning.test.ts +14 -15
- package/src/resources/extensions/gsd/tests/ensure-db-open.test.ts +11 -16
- package/src/resources/extensions/gsd/tests/escalation.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/gsdroot-worktree-detection.test.ts +15 -36
- package/src/resources/extensions/gsd/tests/handler-worktree-write-isolation.test.ts +57 -0
- package/src/resources/extensions/gsd/tests/integration/parallel-merge.test.ts +15 -15
- package/src/resources/extensions/gsd/tests/integration/state-machine-edge-cases.test.ts +15 -5
- package/src/resources/extensions/gsd/tests/markdown-renderer.test.ts +14 -8
- package/src/resources/extensions/gsd/tests/md-importer.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/memory-store.test.ts +3 -2
- package/src/resources/extensions/gsd/tests/park-milestone.test.ts +2 -0
- package/src/resources/extensions/gsd/tests/progressive-planning.test.ts +25 -16
- package/src/resources/extensions/gsd/tests/projection-regression.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/ready-phrase-no-files-4573.test.ts +184 -0
- package/src/resources/extensions/gsd/tests/register-hooks-compaction-checkpoint.test.ts +6 -1
- package/src/resources/extensions/gsd/tests/replan-slice.test.ts +3 -0
- package/src/resources/extensions/gsd/tests/resolve-ts.mjs +4 -0
- package/src/resources/extensions/gsd/tests/rogue-file-detection.test.ts +3 -4
- package/src/resources/extensions/gsd/tests/slice-disk-reconcile.test.ts +10 -56
- package/src/resources/extensions/gsd/tests/stale-slice-rows.test.ts +15 -16
- package/src/resources/extensions/gsd/tests/state-corruption-2945.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/state-machine-full-walkthrough.test.ts +23 -27
- package/src/resources/extensions/gsd/tests/steer-worktree-path.test.ts +13 -14
- package/src/resources/extensions/gsd/tests/stop-auto-remote.test.ts +4 -3
- package/src/resources/extensions/gsd/tests/sync-worktree-skip-current.test.ts +10 -33
- package/src/resources/extensions/gsd/tests/validate-milestone-write-order.test.ts +7 -8
- package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +9 -15
- package/src/resources/extensions/gsd/tests/workflow-logger-wiring.test.ts +12 -7
- package/src/resources/extensions/gsd/tests/workflow-mcp-auto-prep.test.ts +4 -4
- package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +24 -1
- package/src/resources/extensions/gsd/tests/worktree-db-same-file.test.ts +13 -0
- package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +65 -71
- package/src/resources/extensions/gsd/tests/worktree-sync-tasks.test.ts +26 -151
- package/src/resources/extensions/gsd/tools/complete-milestone.ts +7 -5
- package/src/resources/extensions/gsd/tools/complete-slice.ts +7 -14
- package/src/resources/extensions/gsd/tools/complete-task.ts +19 -34
- package/src/resources/extensions/gsd/tools/validate-milestone.ts +7 -5
- package/src/resources/extensions/gsd/workflow-manifest.ts +4 -1
- package/src/resources/extensions/gsd/workflow-mcp-auto-prep.ts +2 -18
- package/src/resources/extensions/gsd/workflow-reconcile.ts +3 -3
- package/src/resources/extensions/gsd/worktree-command.ts +4 -3
- package/dist/web/standalone/.next/server/chunks/8527.js +0 -1
- /package/dist/web/standalone/.next/static/{rk1EN3FQTE6Z1yalkW_GE → oZGTPvJBQX_IDKKnuV8Bt}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{rk1EN3FQTE6Z1yalkW_GE → oZGTPvJBQX_IDKKnuV8Bt}/_ssgManifest.js +0 -0
|
@@ -12,7 +12,7 @@ import { reconcileWorktreeDb, isDbAvailable, getMilestone, getMilestoneSlices, c
|
|
|
12
12
|
import { atomicWriteSync } from "./atomic-write.js";
|
|
13
13
|
import { execFileSync } from "node:child_process";
|
|
14
14
|
import { safeCopy, safeCopyRecursive } from "./safe-fs.js";
|
|
15
|
-
import { gsdRoot } from "./paths.js";
|
|
15
|
+
import { gsdRoot, resolveGsdPathContract } from "./paths.js";
|
|
16
16
|
import { createWorktree, removeWorktree, resolveGitDir, worktreePath, isInsideWorktreesDir, } from "./worktree-manager.js";
|
|
17
17
|
import { detectWorktreeName, nudgeGitBranchCache, } from "./worktree.js";
|
|
18
18
|
import { isGsdWorktreePath, normalizeWorktreePathForCompare, resolveWorktreeProjectRoot, } from "./worktree-root.js";
|
|
@@ -34,9 +34,8 @@ const LEGACY_DEEP_SETUP_RUNTIME_UNIT_FILES = new Set([
|
|
|
34
34
|
]);
|
|
35
35
|
// ─── Shared Constants & Helpers ─────────────────────────────────────────────
|
|
36
36
|
/**
|
|
37
|
-
* Root-level .gsd/
|
|
38
|
-
*
|
|
39
|
-
* and the dispatch-level sync functions.
|
|
37
|
+
* Root-level .gsd/ projections copied from project root into worktrees for
|
|
38
|
+
* compatibility. Project root remains the canonical state/projection root.
|
|
40
39
|
*/
|
|
41
40
|
const ROOT_STATE_FILES = [
|
|
42
41
|
"DECISIONS.md",
|
|
@@ -53,6 +52,10 @@ const ROOT_STATE_FILES = [
|
|
|
53
52
|
// Back-sync (worktree → main) must NEVER overwrite the project root's copy
|
|
54
53
|
// because the project root is authoritative for preferences (#2684).
|
|
55
54
|
];
|
|
55
|
+
const ROOT_DIAGNOSTIC_FILES = [
|
|
56
|
+
"completed-units.json",
|
|
57
|
+
"metrics.json",
|
|
58
|
+
];
|
|
56
59
|
/**
|
|
57
60
|
* Pop a stash entry by tracking the unique marker embedded in its message so
|
|
58
61
|
* concurrent stash operations against the same project root cannot cause us to
|
|
@@ -137,7 +140,7 @@ const VERDICT_RE = /verdict:\s*[\w-]+/i;
|
|
|
137
140
|
* destination when the source copy contains a `verdict:` field.
|
|
138
141
|
*
|
|
139
142
|
* This is the targeted fix for the UAT stuck-loop (#2821): the main
|
|
140
|
-
* safeCopyRecursive uses force:false to protect worktree-
|
|
143
|
+
* safeCopyRecursive uses force:false to protect worktree-local projection
|
|
141
144
|
* files (#1886), but ASSESSMENT files written by run-uat must be
|
|
142
145
|
* forward-synced when the project root has a verdict. Without this,
|
|
143
146
|
* the worktree retains a stale FAIL or missing ASSESSMENT and
|
|
@@ -212,9 +215,9 @@ function clearProjectRootStateFiles(basePath, milestoneId) {
|
|
|
212
215
|
}
|
|
213
216
|
}
|
|
214
217
|
}
|
|
215
|
-
// Clean up
|
|
216
|
-
//
|
|
217
|
-
//
|
|
218
|
+
// Clean up legacy synced milestone directories and runtime/units.
|
|
219
|
+
// Older versions copied these into the project root during execution.
|
|
220
|
+
// If they remain as untracked files when we attempt
|
|
218
221
|
// `git merge --squash`, git rejects the merge with "local changes would
|
|
219
222
|
// be overwritten", causing silent data loss (#1738).
|
|
220
223
|
const syncedDirs = [
|
|
@@ -277,8 +280,9 @@ export function syncProjectRootToWorktree(projectRoot, worktreePath_, milestoneI
|
|
|
277
280
|
return;
|
|
278
281
|
if (!milestoneId)
|
|
279
282
|
return;
|
|
280
|
-
const
|
|
281
|
-
const
|
|
283
|
+
const contract = resolveGsdPathContract(worktreePath_, projectRoot);
|
|
284
|
+
const prGsd = contract.projectGsd;
|
|
285
|
+
const wtGsd = contract.worktreeGsd ?? join(worktreePath_, ".gsd");
|
|
282
286
|
// When .gsd is a symlink to the same external directory in both locations,
|
|
283
287
|
// cpSync rejects the copy because source === destination (ERR_FS_CP_EINVAL).
|
|
284
288
|
// Compare realpaths and skip when they resolve to the same physical path (#2184).
|
|
@@ -286,12 +290,12 @@ export function syncProjectRootToWorktree(projectRoot, worktreePath_, milestoneI
|
|
|
286
290
|
return;
|
|
287
291
|
// Copy milestone directory from project root to worktree — additive only.
|
|
288
292
|
// force:false prevents cpSync from overwriting existing worktree files.
|
|
289
|
-
// Without this, worktree-
|
|
293
|
+
// Without this, worktree-local files (e.g. VALIDATION.md written
|
|
290
294
|
// by validate-milestone) get clobbered by stale project root copies,
|
|
291
295
|
// causing an infinite re-validation loop (#1886).
|
|
292
296
|
safeCopyRecursive(join(prGsd, "milestones", milestoneId), join(wtGsd, "milestones", milestoneId), { force: false });
|
|
293
297
|
// Force-sync ASSESSMENT files that have a verdict from project root (#2821).
|
|
294
|
-
// The additive-only copy above preserves worktree-
|
|
298
|
+
// The additive-only copy above preserves worktree-local files, but
|
|
295
299
|
// ASSESSMENT files are special: after run-uat writes a verdict and post-unit
|
|
296
300
|
// syncs it to the project root, the worktree may retain a stale copy (e.g.
|
|
297
301
|
// verdict:fail while the project root has verdict:pass from a retry). On
|
|
@@ -303,11 +307,9 @@ export function syncProjectRootToWorktree(projectRoot, worktreePath_, milestoneI
|
|
|
303
307
|
// Project root is authoritative for completion state after crash recovery;
|
|
304
308
|
// without this, the worktree re-dispatches already-completed units (#1886).
|
|
305
309
|
safeCopy(join(prGsd, "completed-units.json"), join(wtGsd, "completed-units.json"), { force: true });
|
|
306
|
-
// Delete worktree gsd.db ONLY if it is empty (0 bytes).
|
|
307
|
-
//
|
|
308
|
-
//
|
|
309
|
-
// preserved — deleting it truncates the file to 0 bytes when
|
|
310
|
-
// openDatabase re-creates it, causing "no such table" failures (#2815).
|
|
310
|
+
// Delete a legacy worktree-local gsd.db ONLY if it is empty (0 bytes).
|
|
311
|
+
// Runtime opens contract.projectDb; this cleanup only removes corrupt
|
|
312
|
+
// pre-upgrade local DB projections.
|
|
311
313
|
try {
|
|
312
314
|
const wtDb = join(wtGsd, "gsd.db");
|
|
313
315
|
let deleteSidecars = false;
|
|
@@ -342,9 +344,10 @@ export function syncProjectRootToWorktree(projectRoot, worktreePath_, milestoneI
|
|
|
342
344
|
}
|
|
343
345
|
}
|
|
344
346
|
/**
|
|
345
|
-
* Sync
|
|
347
|
+
* Sync worktree diagnostics from worktree to project root.
|
|
346
348
|
* Only runs when inside an auto-worktree (worktreePath differs from projectRoot).
|
|
347
|
-
*
|
|
349
|
+
* DB/project-root state remains authoritative; markdown projections are not
|
|
350
|
+
* copied from the worktree back to the project root.
|
|
348
351
|
* Non-fatal — sync failure should never block dispatch.
|
|
349
352
|
*/
|
|
350
353
|
export function syncStateToProjectRoot(worktreePath_, projectRoot, milestoneId) {
|
|
@@ -352,23 +355,22 @@ export function syncStateToProjectRoot(worktreePath_, projectRoot, milestoneId)
|
|
|
352
355
|
return;
|
|
353
356
|
if (!milestoneId)
|
|
354
357
|
return;
|
|
355
|
-
const
|
|
356
|
-
const
|
|
358
|
+
const contract = resolveGsdPathContract(worktreePath_, projectRoot);
|
|
359
|
+
const wtGsd = contract.worktreeGsd ?? join(worktreePath_, ".gsd");
|
|
360
|
+
const prGsd = contract.projectGsd;
|
|
357
361
|
// When .gsd is a symlink to the same external directory in both locations,
|
|
358
362
|
// cpSync rejects the copy because source === destination (ERR_FS_CP_EINVAL).
|
|
359
363
|
// Compare realpaths and skip when they resolve to the same physical path (#2184).
|
|
360
364
|
if (isSamePath(wtGsd, prGsd))
|
|
361
365
|
return;
|
|
362
|
-
//
|
|
363
|
-
safeCopy(join(wtGsd, "STATE.md"), join(prGsd, "STATE.md"), { force: true });
|
|
364
|
-
// 2. Milestone directory — ROADMAP, slice PLANs, task summaries
|
|
365
|
-
// Copy the entire milestone .gsd subtree so deriveState reads current checkboxes
|
|
366
|
-
safeCopyRecursive(join(wtGsd, "milestones", milestoneId), join(prGsd, "milestones", milestoneId), { force: true });
|
|
367
|
-
// 3. metrics.json — session cost/token tracking (#2313).
|
|
366
|
+
// metrics.json — session cost/token tracking (#2313).
|
|
368
367
|
// Without this, metrics accumulated in the worktree are invisible from the
|
|
369
368
|
// project root and never appear in the dashboard or skill-health reports.
|
|
370
369
|
safeCopy(join(wtGsd, "metrics.json"), join(prGsd, "metrics.json"), { force: true });
|
|
371
|
-
//
|
|
370
|
+
// completed-units.json — runtime completion diagnostics used to avoid
|
|
371
|
+
// re-dispatching work already completed in an isolated worktree.
|
|
372
|
+
safeCopy(join(wtGsd, "completed-units.json"), join(prGsd, "completed-units.json"), { force: true });
|
|
373
|
+
// Runtime records — unit dispatch diagnostics used by selfHealRuntimeRecords().
|
|
372
374
|
// Without this, a crash during a unit leaves the runtime record only in the
|
|
373
375
|
// worktree. If the next session resolves basePath before worktree re-entry,
|
|
374
376
|
// selfHeal can't find or clear the stale record (#769).
|
|
@@ -529,12 +531,14 @@ export function cleanStaleRuntimeUnits(gsdRootPath, hasMilestoneSummary) {
|
|
|
529
531
|
* missing milestones, CONTEXT, ROADMAP, DECISIONS, REQUIREMENTS, and
|
|
530
532
|
* PROJECT files from the main repo's .gsd/ into the worktree's .gsd/.
|
|
531
533
|
*
|
|
532
|
-
* Only adds missing content — never overwrites existing files in the worktree
|
|
533
|
-
*
|
|
534
|
+
* Only adds missing content — never overwrites existing files in the worktree.
|
|
535
|
+
* Worktree files are compatibility projections; DB/project root remains
|
|
536
|
+
* authoritative for runtime state.
|
|
534
537
|
*/
|
|
535
538
|
export function syncGsdStateToWorktree(mainBasePath, worktreePath_) {
|
|
536
|
-
const
|
|
537
|
-
const
|
|
539
|
+
const contract = resolveGsdPathContract(worktreePath_, mainBasePath);
|
|
540
|
+
const mainGsd = contract.projectGsd;
|
|
541
|
+
const wtGsd = contract.worktreeGsd ?? join(worktreePath_, ".gsd");
|
|
538
542
|
const synced = [];
|
|
539
543
|
// If both resolve to the same directory (symlink), no sync needed
|
|
540
544
|
if (isSamePath(mainGsd, wtGsd))
|
|
@@ -678,28 +682,22 @@ export function syncGsdStateToWorktree(mainBasePath, worktreePath_) {
|
|
|
678
682
|
return { synced };
|
|
679
683
|
}
|
|
680
684
|
/**
|
|
681
|
-
* Sync
|
|
682
|
-
*
|
|
683
|
-
*
|
|
685
|
+
* Sync compatibility artifacts from worktree back to the main external state
|
|
686
|
+
* directory. Canonical workflow state lives in the project DB; worktree .gsd
|
|
687
|
+
* content is legacy projection/diagnostic data only.
|
|
684
688
|
*
|
|
685
689
|
* Syncs:
|
|
686
|
-
* 1.
|
|
687
|
-
*
|
|
688
|
-
* worktree is the authoritative execution context.
|
|
689
|
-
* 2. ALL milestone directories found in the worktree — not just the
|
|
690
|
-
* current milestoneId. The complete-milestone unit may create artifacts
|
|
691
|
-
* for the *next* milestone (CONTEXT, ROADMAP, new requirements) which
|
|
692
|
-
* must survive worktree teardown.
|
|
690
|
+
* 1. Legacy worktree DBs are reconciled into the canonical project DB.
|
|
691
|
+
* 2. Runtime diagnostic files may be copied for operator visibility.
|
|
693
692
|
*
|
|
694
|
-
*
|
|
695
|
-
*
|
|
696
|
-
*
|
|
697
|
-
* squash merge carries nothing. This caused next-milestone artifacts and
|
|
698
|
-
* updated REQUIREMENTS/PROJECT to be silently lost on teardown.
|
|
693
|
+
* Markdown milestone directories are projections and are not copied from
|
|
694
|
+
* worktrees into the project root. Current workflow state must arrive through
|
|
695
|
+
* the shared project DB or the pre-upgrade DB reconciliation path above.
|
|
699
696
|
*/
|
|
700
697
|
export function syncWorktreeStateBack(mainBasePath, worktreePath, milestoneId) {
|
|
701
|
-
const
|
|
702
|
-
const
|
|
698
|
+
const contract = resolveGsdPathContract(worktreePath, mainBasePath);
|
|
699
|
+
const mainGsd = contract.projectGsd;
|
|
700
|
+
const wtGsd = contract.worktreeGsd ?? join(worktreePath, ".gsd");
|
|
703
701
|
const synced = [];
|
|
704
702
|
// If both resolve to the same directory (symlink), no sync needed
|
|
705
703
|
if (isSamePath(mainGsd, wtGsd))
|
|
@@ -712,7 +710,7 @@ export function syncWorktreeStateBack(mainBasePath, worktreePath, milestoneId) {
|
|
|
712
710
|
// files. This handles in-flight worktrees that were created before the
|
|
713
711
|
// upgrade to shared WAL mode.
|
|
714
712
|
const wtLocalDb = join(wtGsd, "gsd.db");
|
|
715
|
-
const mainDb =
|
|
713
|
+
const mainDb = contract.projectDb;
|
|
716
714
|
if (existsSync(wtLocalDb) && existsSync(mainDb)) {
|
|
717
715
|
try {
|
|
718
716
|
reconcileWorktreeDb(mainDb, wtLocalDb);
|
|
@@ -723,13 +721,10 @@ export function syncWorktreeStateBack(mainBasePath, worktreePath, milestoneId) {
|
|
|
723
721
|
logError("worktree", `DB reconciliation failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
724
722
|
}
|
|
725
723
|
}
|
|
726
|
-
// ── 1. Sync root-level
|
|
727
|
-
//
|
|
728
|
-
//
|
|
729
|
-
|
|
730
|
-
// written during milestone closeout and lost on teardown without explicit sync
|
|
731
|
-
// (#1787, #2313).
|
|
732
|
-
for (const f of ROOT_STATE_FILES) {
|
|
724
|
+
// ── 1. Sync root-level diagnostic files back ─────────────────────────
|
|
725
|
+
// Markdown/JSON state projections remain project-root/DB authoritative.
|
|
726
|
+
// These diagnostic files are copied for observability only.
|
|
727
|
+
for (const f of ROOT_DIAGNOSTIC_FILES) {
|
|
733
728
|
const src = join(wtGsd, f);
|
|
734
729
|
const dst = join(mainGsd, f);
|
|
735
730
|
if (existsSync(src)) {
|
|
@@ -743,103 +738,8 @@ export function syncWorktreeStateBack(mainBasePath, worktreePath, milestoneId) {
|
|
|
743
738
|
}
|
|
744
739
|
}
|
|
745
740
|
}
|
|
746
|
-
// ── 2. Sync ALL milestone directories ────────────────────────────────
|
|
747
|
-
// The complete-milestone unit may create next-milestone artifacts (e.g.
|
|
748
|
-
// M007 setup while closing M006). We must sync every milestone directory
|
|
749
|
-
// in the worktree, not just the current one.
|
|
750
|
-
const wtMilestonesDir = join(wtGsd, "milestones");
|
|
751
|
-
if (!existsSync(wtMilestonesDir))
|
|
752
|
-
return { synced };
|
|
753
|
-
try {
|
|
754
|
-
const wtMilestones = readdirSync(wtMilestonesDir, { withFileTypes: true })
|
|
755
|
-
.filter((d) => d.isDirectory())
|
|
756
|
-
.map((d) => d.name);
|
|
757
|
-
for (const mid of wtMilestones) {
|
|
758
|
-
// Skip the current milestone being merged — its files are already in the
|
|
759
|
-
// milestone branch and would conflict with the squash merge (#3641).
|
|
760
|
-
if (mid === milestoneId)
|
|
761
|
-
continue;
|
|
762
|
-
syncMilestoneDir(wtGsd, mainGsd, mid, synced);
|
|
763
|
-
}
|
|
764
|
-
}
|
|
765
|
-
catch (err) {
|
|
766
|
-
/* non-fatal */
|
|
767
|
-
logWarning("worktree", `milestone sync-back failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
768
|
-
}
|
|
769
741
|
return { synced };
|
|
770
742
|
}
|
|
771
|
-
function syncCurrentMilestoneStateAfterMerge(mainBasePath, worktreePath, milestoneId) {
|
|
772
|
-
const mainGsd = gsdRoot(mainBasePath);
|
|
773
|
-
const wtGsd = gsdRoot(worktreePath);
|
|
774
|
-
const synced = [];
|
|
775
|
-
if (isSamePath(mainGsd, wtGsd))
|
|
776
|
-
return { synced };
|
|
777
|
-
if (!existsSync(wtGsd) || !existsSync(mainGsd))
|
|
778
|
-
return { synced };
|
|
779
|
-
syncMilestoneDir(wtGsd, mainGsd, milestoneId, synced);
|
|
780
|
-
return { synced };
|
|
781
|
-
}
|
|
782
|
-
/**
|
|
783
|
-
* Sync a single milestone directory from worktree to main.
|
|
784
|
-
* Copies milestone-level .md files, slice-level files, and task summaries.
|
|
785
|
-
*/
|
|
786
|
-
/** Copy matching files from srcDir to dstDir (non-fatal per file). */
|
|
787
|
-
function syncDirFiles(srcDir, dstDir, filter, synced, prefix) {
|
|
788
|
-
try {
|
|
789
|
-
for (const entry of readdirSync(srcDir, { withFileTypes: true })) {
|
|
790
|
-
if (!entry.isFile() || !filter(entry.name))
|
|
791
|
-
continue;
|
|
792
|
-
try {
|
|
793
|
-
cpSync(join(srcDir, entry.name), join(dstDir, entry.name), { force: true });
|
|
794
|
-
synced.push(`${prefix}${entry.name}`);
|
|
795
|
-
}
|
|
796
|
-
catch (err) {
|
|
797
|
-
/* non-fatal */
|
|
798
|
-
logWarning("worktree", `file copy failed (${prefix}${entry.name}): ${err instanceof Error ? err.message : String(err)}`);
|
|
799
|
-
}
|
|
800
|
-
}
|
|
801
|
-
}
|
|
802
|
-
catch (err) {
|
|
803
|
-
/* non-fatal — srcDir may not be readable */
|
|
804
|
-
logWarning("worktree", `directory read failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
805
|
-
}
|
|
806
|
-
}
|
|
807
|
-
function syncMilestoneDir(wtGsd, mainGsd, mid, synced) {
|
|
808
|
-
const wtMilestoneDir = join(wtGsd, "milestones", mid);
|
|
809
|
-
const mainMilestoneDir = join(mainGsd, "milestones", mid);
|
|
810
|
-
if (!existsSync(wtMilestoneDir))
|
|
811
|
-
return;
|
|
812
|
-
mkdirSync(mainMilestoneDir, { recursive: true });
|
|
813
|
-
const isMd = (name) => name.endsWith(".md");
|
|
814
|
-
// Sync milestone-level files (SUMMARY, VALIDATION, ROADMAP, CONTEXT)
|
|
815
|
-
syncDirFiles(wtMilestoneDir, mainMilestoneDir, isMd, synced, `milestones/${mid}/`);
|
|
816
|
-
// Sync slice-level files (summaries, UATs) and task summaries (#1678)
|
|
817
|
-
const wtSlicesDir = join(wtMilestoneDir, "slices");
|
|
818
|
-
const mainSlicesDir = join(mainMilestoneDir, "slices");
|
|
819
|
-
if (!existsSync(wtSlicesDir))
|
|
820
|
-
return;
|
|
821
|
-
try {
|
|
822
|
-
for (const sliceEntry of readdirSync(wtSlicesDir, { withFileTypes: true })) {
|
|
823
|
-
if (!sliceEntry.isDirectory())
|
|
824
|
-
continue;
|
|
825
|
-
const sid = sliceEntry.name;
|
|
826
|
-
const wtSliceDir = join(wtSlicesDir, sid);
|
|
827
|
-
const mainSliceDir = join(mainSlicesDir, sid);
|
|
828
|
-
mkdirSync(mainSliceDir, { recursive: true });
|
|
829
|
-
syncDirFiles(wtSliceDir, mainSliceDir, isMd, synced, `milestones/${mid}/slices/${sid}/`);
|
|
830
|
-
const wtTasksDir = join(wtSliceDir, "tasks");
|
|
831
|
-
const mainTasksDir = join(mainSliceDir, "tasks");
|
|
832
|
-
if (existsSync(wtTasksDir)) {
|
|
833
|
-
mkdirSync(mainTasksDir, { recursive: true });
|
|
834
|
-
syncDirFiles(wtTasksDir, mainTasksDir, isMd, synced, `milestones/${mid}/slices/${sid}/tasks/`);
|
|
835
|
-
}
|
|
836
|
-
}
|
|
837
|
-
}
|
|
838
|
-
catch (err) {
|
|
839
|
-
/* non-fatal */
|
|
840
|
-
logWarning("worktree", `milestone slice sync failed (${mid}): ${err instanceof Error ? err.message : String(err)}`);
|
|
841
|
-
}
|
|
842
|
-
}
|
|
843
743
|
// ─── Worktree Post-Create Hook (#597) ────────────────────────────────────────
|
|
844
744
|
/**
|
|
845
745
|
* Run the user-configured post-create hook script after worktree creation.
|
|
@@ -1002,10 +902,9 @@ export function enterBranchModeForMilestone(basePath, milestoneId) {
|
|
|
1002
902
|
* directory at the project root and apply any [x] checkbox states that are
|
|
1003
903
|
* ahead of the worktree version (forward-only: never downgrade [x] → [ ]).
|
|
1004
904
|
*
|
|
1005
|
-
* This is
|
|
1006
|
-
*
|
|
1007
|
-
*
|
|
1008
|
-
* filesystem copy is still valid and correct.
|
|
905
|
+
* This is forward-only compatibility for legacy projection copies. The DB
|
|
906
|
+
* remains authoritative; this never downgrades checked boxes in a local
|
|
907
|
+
* worktree projection.
|
|
1009
908
|
*/
|
|
1010
909
|
function reconcilePlanCheckboxes(projectRoot, wtPath, milestoneId) {
|
|
1011
910
|
const srcMilestone = join(projectRoot, ".gsd", "milestones", milestoneId);
|
|
@@ -1457,9 +1356,10 @@ export function mergeMilestoneToMain(originalBasePath_, milestoneId, roadmapCont
|
|
|
1457
1356
|
// database (#2823).
|
|
1458
1357
|
if (isDbAvailable()) {
|
|
1459
1358
|
try {
|
|
1460
|
-
const
|
|
1461
|
-
const
|
|
1462
|
-
|
|
1359
|
+
const contract = resolveGsdPathContract(worktreeCwd, originalBasePath_);
|
|
1360
|
+
const worktreeDbPath = join(contract.worktreeGsd ?? join(worktreeCwd, ".gsd"), "gsd.db");
|
|
1361
|
+
const mainDbPath = contract.projectDb;
|
|
1362
|
+
if (existsSync(worktreeDbPath) && !isSamePath(worktreeDbPath, mainDbPath)) {
|
|
1463
1363
|
reconcileWorktreeDb(mainDbPath, worktreeDbPath);
|
|
1464
1364
|
}
|
|
1465
1365
|
}
|
|
@@ -1964,23 +1864,6 @@ export function mergeMilestoneToMain(originalBasePath_, milestoneId, roadmapCont
|
|
|
1964
1864
|
}
|
|
1965
1865
|
// 9a-iii. Restore sheltered queued milestone directories (#2505).
|
|
1966
1866
|
restoreShelter();
|
|
1967
|
-
// 9a-iv. Preserve current milestone artifacts that may be untracked in git.
|
|
1968
|
-
// syncWorktreeStateBack intentionally skips the current milestone before the
|
|
1969
|
-
// squash merge to avoid conflicting with the merge content. Once the squash
|
|
1970
|
-
// commit is complete, copy those files back so summaries, validation, and
|
|
1971
|
-
// task outputs survive worktree teardown in external/.gitignored .gsd setups.
|
|
1972
|
-
try {
|
|
1973
|
-
const { synced } = syncCurrentMilestoneStateAfterMerge(originalBasePath_, worktreeCwd, milestoneId);
|
|
1974
|
-
if (synced.length > 0) {
|
|
1975
|
-
debugLog("mergeMilestoneToMain", {
|
|
1976
|
-
phase: "current-milestone-sync-after-merge",
|
|
1977
|
-
synced: synced.length,
|
|
1978
|
-
});
|
|
1979
|
-
}
|
|
1980
|
-
}
|
|
1981
|
-
catch (err) {
|
|
1982
|
-
logWarning("worktree", `current milestone sync after merge failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1983
|
-
}
|
|
1984
1867
|
// 9b. Safety check (#1792): if nothing was committed, verify the milestone
|
|
1985
1868
|
// work is already on the integration branch before allowing teardown.
|
|
1986
1869
|
// Compare only non-.gsd/ paths — .gsd/ state files diverge normally and
|
|
@@ -56,11 +56,13 @@ import { getErrorMessage } from "./error-utils.js";
|
|
|
56
56
|
import { recoverFailedMigration } from "./migrate-external.js";
|
|
57
57
|
import { initRegistry, convertDispatchRules } from "./rule-registry.js";
|
|
58
58
|
import { emitJournalEvent as _emitJournalEvent } from "./journal.js";
|
|
59
|
+
import { isClosedStatus } from "./status-guards.js";
|
|
59
60
|
import { updateProgressWidget as _updateProgressWidget, updateSliceProgressCache, clearSliceProgressCache, } from "./auto-dashboard.js";
|
|
60
61
|
import { registerSigtermHandler as _registerSigtermHandler, deregisterSigtermHandler as _deregisterSigtermHandler, } from "./auto-supervisor.js";
|
|
61
62
|
import { isDbAvailable, getMilestone } from "./gsd-db.js";
|
|
62
63
|
import { countPendingCaptures } from "./captures.js";
|
|
63
64
|
import { CMUX_CHANNELS } from "../shared/cmux-events.js";
|
|
65
|
+
import { ensureDbOpen } from "./bootstrap/dynamic-tools.js";
|
|
64
66
|
function makeCmuxEmitters(pi) {
|
|
65
67
|
return {
|
|
66
68
|
syncCmuxSidebar: (preferences, state) => pi.events.emit(CMUX_CHANNELS.SIDEBAR, { action: "sync", preferences, state }),
|
|
@@ -1154,15 +1156,31 @@ export async function startAuto(ctx, pi, base, verboseMode, options) {
|
|
|
1154
1156
|
|| !!freshStartAssessment.lock);
|
|
1155
1157
|
if (shouldResumePausedSession) {
|
|
1156
1158
|
// Validate the milestone still exists and isn't already complete (#1664).
|
|
1159
|
+
// DB status is authoritative when available; SUMMARY.md is a legacy
|
|
1160
|
+
// fallback only for unmigrated/offline projects.
|
|
1157
1161
|
const mDir = resolveMilestonePath(base, meta.milestoneId);
|
|
1158
|
-
const summaryFile = resolveMilestoneFile(base, meta.milestoneId, "SUMMARY");
|
|
1159
1162
|
let summaryIsTerminal = false;
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
+
let dbAvailable = isDbAvailable();
|
|
1164
|
+
let milestoneRow = dbAvailable ? getMilestone(meta.milestoneId) : null;
|
|
1165
|
+
if (!milestoneRow) {
|
|
1166
|
+
const opened = await ensureDbOpen(base);
|
|
1167
|
+
dbAvailable = opened || isDbAvailable();
|
|
1168
|
+
if (dbAvailable) {
|
|
1169
|
+
milestoneRow = getMilestone(meta.milestoneId);
|
|
1163
1170
|
}
|
|
1164
|
-
|
|
1165
|
-
|
|
1171
|
+
}
|
|
1172
|
+
if (dbAvailable) {
|
|
1173
|
+
summaryIsTerminal = !!milestoneRow && isClosedStatus(milestoneRow.status);
|
|
1174
|
+
}
|
|
1175
|
+
else {
|
|
1176
|
+
const summaryFile = resolveMilestoneFile(base, meta.milestoneId, "SUMMARY");
|
|
1177
|
+
if (summaryFile) {
|
|
1178
|
+
try {
|
|
1179
|
+
summaryIsTerminal = classifyMilestoneSummaryContent(readFileSync(summaryFile, "utf-8")) !== "failure";
|
|
1180
|
+
}
|
|
1181
|
+
catch {
|
|
1182
|
+
summaryIsTerminal = false;
|
|
1183
|
+
}
|
|
1166
1184
|
}
|
|
1167
1185
|
}
|
|
1168
1186
|
if (!mDir || summaryIsTerminal) {
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
|
-
import {
|
|
2
|
+
import { dirname } from "node:path";
|
|
3
3
|
import { createBashTool, createEditTool, createReadTool, createWriteTool } from "@gsd/pi-coding-agent";
|
|
4
4
|
import { DEFAULT_BASH_TIMEOUT_SECS } from "../constants.js";
|
|
5
5
|
import { setLogBasePath, logWarning } from "../workflow-logger.js";
|
|
6
|
+
import { resolveGsdPathContract } from "../paths.js";
|
|
6
7
|
/**
|
|
7
8
|
* Resolve the correct DB path for the current working directory.
|
|
8
9
|
* If `basePath` is inside a `.gsd/worktrees/<MID>/` directory, returns
|
|
@@ -10,67 +11,15 @@ import { setLogBasePath, logWarning } from "../workflow-logger.js";
|
|
|
10
11
|
* returns `<basePath>/.gsd/gsd.db`.
|
|
11
12
|
*/
|
|
12
13
|
export function resolveProjectRootDbPath(basePath) {
|
|
13
|
-
|
|
14
|
-
// A worktree path looks like: /project/root/.gsd/worktrees/M001/...
|
|
15
|
-
// We need to resolve back to /project/root/.gsd/gsd.db
|
|
16
|
-
const marker = `${sep}.gsd${sep}worktrees${sep}`;
|
|
17
|
-
const idx = basePath.indexOf(marker);
|
|
18
|
-
if (idx !== -1) {
|
|
19
|
-
const projectRoot = basePath.slice(0, idx);
|
|
20
|
-
return join(projectRoot, ".gsd", "gsd.db");
|
|
21
|
-
}
|
|
22
|
-
// Also handle forward-slash paths on all platforms
|
|
23
|
-
const fwdMarker = "/.gsd/worktrees/";
|
|
24
|
-
const fwdIdx = basePath.indexOf(fwdMarker);
|
|
25
|
-
if (fwdIdx !== -1) {
|
|
26
|
-
const projectRoot = basePath.slice(0, fwdIdx);
|
|
27
|
-
return join(projectRoot, ".gsd", "gsd.db");
|
|
28
|
-
}
|
|
29
|
-
// External-state layout: ~/.gsd/projects/<hash>/worktrees/<MID>/...
|
|
30
|
-
// Resolve to ~/.gsd/projects/<hash>/gsd.db (the canonical project DB) (#2952).
|
|
31
|
-
// Must be checked before the generic symlink-resolved handler: both match
|
|
32
|
-
// /.gsd/projects/<hash>/worktrees/ but require different resolution targets.
|
|
33
|
-
const extRe = /[/\\]\.gsd[/\\]projects[/\\][a-f0-9]+[/\\]worktrees(?:[/\\]|$)/;
|
|
34
|
-
const extMatch = extRe.exec(basePath);
|
|
35
|
-
if (extMatch) {
|
|
36
|
-
const matchStr = extMatch[0];
|
|
37
|
-
// Find the "/worktrees" portion within the match and slice up to it
|
|
38
|
-
const wtIdx = matchStr.search(/[/\\]worktrees(?:[/\\]|$)/);
|
|
39
|
-
const projectStateRoot = basePath.slice(0, extMatch.index + wtIdx);
|
|
40
|
-
return join(projectStateRoot, "gsd.db");
|
|
41
|
-
}
|
|
42
|
-
// Symlink-resolved layout: /.gsd/projects/<hash>/worktrees/M001/...
|
|
43
|
-
// The project root is everything before /.gsd/projects/ (#2517)
|
|
44
|
-
const symlinkMarker = `${sep}.gsd${sep}projects${sep}`;
|
|
45
|
-
const symlinkIdx = basePath.indexOf(symlinkMarker);
|
|
46
|
-
if (symlinkIdx !== -1) {
|
|
47
|
-
const afterProjects = basePath.slice(symlinkIdx + symlinkMarker.length);
|
|
48
|
-
// Expect: <hash>/worktrees/...
|
|
49
|
-
const worktreeSeg = `${sep}worktrees${sep}`;
|
|
50
|
-
if (afterProjects.includes(worktreeSeg)) {
|
|
51
|
-
const projectRoot = basePath.slice(0, symlinkIdx);
|
|
52
|
-
return join(projectRoot, ".gsd", "gsd.db");
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
// Forward-slash variant for symlink-resolved layout
|
|
56
|
-
const fwdSymlinkMarker = "/.gsd/projects/";
|
|
57
|
-
const fwdSymlinkIdx = basePath.indexOf(fwdSymlinkMarker);
|
|
58
|
-
if (fwdSymlinkIdx !== -1) {
|
|
59
|
-
const afterProjects = basePath.slice(fwdSymlinkIdx + fwdSymlinkMarker.length);
|
|
60
|
-
if (afterProjects.includes("/worktrees/")) {
|
|
61
|
-
const projectRoot = basePath.slice(0, fwdSymlinkIdx);
|
|
62
|
-
return join(projectRoot, ".gsd", "gsd.db");
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
return join(basePath, ".gsd", "gsd.db");
|
|
14
|
+
return resolveGsdPathContract(basePath).projectDb;
|
|
66
15
|
}
|
|
67
16
|
export async function ensureDbOpen(basePath = process.cwd()) {
|
|
68
17
|
try {
|
|
69
18
|
const db = await import("../gsd-db.js");
|
|
70
|
-
const
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
const projectRoot =
|
|
19
|
+
const contract = resolveGsdPathContract(basePath);
|
|
20
|
+
const dbPath = contract.projectDb;
|
|
21
|
+
const gsdDir = contract.projectGsd;
|
|
22
|
+
const projectRoot = dirname(dirname(dbPath));
|
|
74
23
|
// Open existing DB file (may be at project root for worktrees)
|
|
75
24
|
if (existsSync(dbPath)) {
|
|
76
25
|
const opened = db.openDatabase(dbPath);
|
|
@@ -78,26 +27,9 @@ export async function ensureDbOpen(basePath = process.cwd()) {
|
|
|
78
27
|
setLogBasePath(projectRoot);
|
|
79
28
|
return opened;
|
|
80
29
|
}
|
|
81
|
-
// No DB file — create
|
|
30
|
+
// No DB file — create an empty authoritative DB. Markdown migration is
|
|
31
|
+
// explicit-only; runtime startup must not import projections into state.
|
|
82
32
|
if (existsSync(gsdDir)) {
|
|
83
|
-
const hasDecisions = existsSync(join(gsdDir, "DECISIONS.md"));
|
|
84
|
-
const hasRequirements = existsSync(join(gsdDir, "REQUIREMENTS.md"));
|
|
85
|
-
const hasMilestones = existsSync(join(gsdDir, "milestones"));
|
|
86
|
-
if (hasDecisions || hasRequirements || hasMilestones) {
|
|
87
|
-
const opened = db.openDatabase(dbPath);
|
|
88
|
-
if (opened) {
|
|
89
|
-
setLogBasePath(projectRoot);
|
|
90
|
-
try {
|
|
91
|
-
const { migrateFromMarkdown } = await import("../md-importer.js");
|
|
92
|
-
migrateFromMarkdown(basePath);
|
|
93
|
-
}
|
|
94
|
-
catch (err) {
|
|
95
|
-
logWarning("bootstrap", `ensureDbOpen auto-migration failed: ${err.message}`);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
return opened;
|
|
99
|
-
}
|
|
100
|
-
// .gsd/ exists but has no Markdown content (fresh project) — create empty DB
|
|
101
33
|
const opened = db.openDatabase(dbPath);
|
|
102
34
|
if (opened)
|
|
103
35
|
setLogBasePath(projectRoot);
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { generateCodebaseMap, updateCodebaseMap, writeCodebaseMap, getCodebaseMapStats, readCodebaseMap, } from "./codebase-generator.js";
|
|
8
8
|
import { loadEffectiveGSDPreferences } from "./preferences.js";
|
|
9
|
-
import {
|
|
9
|
+
import { currentDirectoryRoot } from "./commands/context.js";
|
|
10
10
|
const USAGE = "Usage: /gsd codebase [generate|update|stats]\n\n" +
|
|
11
11
|
" generate [--max-files N] [--collapse-threshold N] — Generate or regenerate CODEBASE.md\n" +
|
|
12
12
|
" update [--max-files N] [--collapse-threshold N] — Refresh the CODEBASE.md cache immediately\n" +
|
|
@@ -20,7 +20,7 @@ const USAGE = "Usage: /gsd codebase [generate|update|stats]\n\n" +
|
|
|
20
20
|
" max_files: 1000\n" +
|
|
21
21
|
" collapse_threshold: 15";
|
|
22
22
|
export async function handleCodebase(args, ctx, _pi) {
|
|
23
|
-
const basePath =
|
|
23
|
+
const basePath = currentDirectoryRoot();
|
|
24
24
|
const parts = args.trim().split(/\s+/);
|
|
25
25
|
const sub = parts[0] ?? "";
|
|
26
26
|
switch (sub) {
|
|
@@ -15,7 +15,7 @@ import { appendOverride, appendKnowledge } from "./files.js";
|
|
|
15
15
|
import { formatDoctorIssuesForPrompt, formatDoctorReport, formatDoctorReportJson, runGSDDoctor, selectDoctorScope, filterDoctorIssues, } from "./doctor.js";
|
|
16
16
|
import { isAutoActive, checkRemoteAutoSession } from "./auto.js";
|
|
17
17
|
import { getAutoWorktreePath } from "./auto-worktree.js";
|
|
18
|
-
import { projectRoot } from "./commands/context.js";
|
|
18
|
+
import { currentDirectoryRoot, projectRoot } from "./commands/context.js";
|
|
19
19
|
import { loadPrompt } from "./prompt-loader.js";
|
|
20
20
|
const UPDATE_REGISTRY_URL = "https://registry.npmjs.org/gsd-pi/latest";
|
|
21
21
|
const UPDATE_FETCH_TIMEOUT_MS = 5000;
|
|
@@ -169,7 +169,7 @@ export async function handleCapture(args, ctx) {
|
|
|
169
169
|
ctx.ui.notify('Usage: /gsd capture "your thought here"', "warning");
|
|
170
170
|
return;
|
|
171
171
|
}
|
|
172
|
-
const basePath =
|
|
172
|
+
const basePath = currentDirectoryRoot();
|
|
173
173
|
// Ensure .gsd/ exists — capture should work even without a milestone
|
|
174
174
|
const gsdDir = gsdRoot(basePath);
|
|
175
175
|
if (!existsSync(gsdDir)) {
|
|
@@ -220,7 +220,7 @@ export async function handleTriage(ctx, pi, basePath) {
|
|
|
220
220
|
}, { triggerTurn: true });
|
|
221
221
|
}
|
|
222
222
|
export async function handleSteer(change, ctx, pi) {
|
|
223
|
-
const basePath =
|
|
223
|
+
const basePath = currentDirectoryRoot();
|
|
224
224
|
const state = await deriveState(basePath);
|
|
225
225
|
const mid = state.activeMilestone?.id ?? "none";
|
|
226
226
|
const sid = state.activeSlice?.id ?? "none";
|
|
@@ -284,7 +284,7 @@ export async function handleKnowledge(args, ctx) {
|
|
|
284
284
|
return;
|
|
285
285
|
}
|
|
286
286
|
const type = typeArg;
|
|
287
|
-
const basePath =
|
|
287
|
+
const basePath = currentDirectoryRoot();
|
|
288
288
|
const state = await deriveState(basePath);
|
|
289
289
|
const scope = state.activeMilestone?.id
|
|
290
290
|
? `${state.activeMilestone.id}${state.activeSlice ? `/${state.activeSlice.id}` : ""}`
|
|
@@ -310,7 +310,7 @@ Examples:
|
|
|
310
310
|
return;
|
|
311
311
|
}
|
|
312
312
|
const [hookName, unitType, unitId] = parts;
|
|
313
|
-
const basePath =
|
|
313
|
+
const basePath = currentDirectoryRoot();
|
|
314
314
|
// Import the hook trigger function
|
|
315
315
|
const { triggerHookManually, formatHookStatus, getHookStatus } = await import("./post-unit-hooks.js");
|
|
316
316
|
const { dispatchHookUnit } = await import("./auto.js");
|
|
@@ -13,7 +13,7 @@ import { existsSync, readdirSync, readFileSync, statSync, unlinkSync } from "nod
|
|
|
13
13
|
import { join } from "node:path";
|
|
14
14
|
import { gsdRoot } from "./paths.js";
|
|
15
15
|
import { loadJsonFileOrNull } from "./json-persistence.js";
|
|
16
|
-
import {
|
|
16
|
+
import { currentDirectoryRoot } from "./commands/context.js";
|
|
17
17
|
// ─── Helpers ────────────────────────────────────────────────────────────────
|
|
18
18
|
function activityDir(basePath) {
|
|
19
19
|
return join(gsdRoot(basePath), "activity");
|
|
@@ -227,7 +227,7 @@ function summarizeDebugLog(filePath) {
|
|
|
227
227
|
}
|
|
228
228
|
// ─── Main Handler ───────────────────────────────────────────────────────────
|
|
229
229
|
export async function handleLogs(args, ctx) {
|
|
230
|
-
const basePath =
|
|
230
|
+
const basePath = currentDirectoryRoot();
|
|
231
231
|
const parts = args.trim().split(/\s+/).filter(Boolean);
|
|
232
232
|
const subCmd = parts[0] ?? "";
|
|
233
233
|
// /gsd logs clear
|