gsd-pi 2.32.0 → 2.33.0-dev.69bff0f
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 +22 -20
- package/dist/resource-loader.js +13 -3
- package/dist/resources/extensions/gsd/auto-dashboard.ts +3 -1
- package/dist/resources/extensions/gsd/auto-dispatch.ts +40 -12
- package/dist/resources/extensions/gsd/auto-idempotency.ts +3 -2
- package/dist/resources/extensions/gsd/auto-observability.ts +2 -4
- package/dist/resources/extensions/gsd/auto-post-unit.ts +5 -5
- package/dist/resources/extensions/gsd/auto-prompts.ts +46 -44
- package/dist/resources/extensions/gsd/auto-recovery.ts +8 -22
- package/dist/resources/extensions/gsd/auto-start.ts +8 -6
- package/dist/resources/extensions/gsd/auto-stuck-detection.ts +3 -2
- package/dist/resources/extensions/gsd/auto-supervisor.ts +10 -5
- package/dist/resources/extensions/gsd/auto-timeout-recovery.ts +2 -1
- package/dist/resources/extensions/gsd/auto-timers.ts +3 -2
- package/dist/resources/extensions/gsd/auto-verification.ts +6 -6
- package/dist/resources/extensions/gsd/auto-worktree.ts +140 -5
- package/dist/resources/extensions/gsd/auto.ts +108 -182
- package/dist/resources/extensions/gsd/commands-inspect.ts +2 -1
- package/dist/resources/extensions/gsd/commands-workflow-templates.ts +2 -1
- package/dist/resources/extensions/gsd/commands.ts +14 -2
- package/dist/resources/extensions/gsd/complexity-classifier.ts +5 -7
- package/dist/resources/extensions/gsd/crash-recovery.ts +15 -2
- package/dist/resources/extensions/gsd/dispatch-guard.ts +2 -1
- package/dist/resources/extensions/gsd/error-utils.ts +6 -0
- package/dist/resources/extensions/gsd/export.ts +2 -1
- package/dist/resources/extensions/gsd/git-service.ts +3 -2
- package/dist/resources/extensions/gsd/guided-flow.ts +3 -2
- package/dist/resources/extensions/gsd/index.ts +12 -5
- package/dist/resources/extensions/gsd/key-manager.ts +2 -1
- package/dist/resources/extensions/gsd/marketplace-discovery.ts +4 -3
- package/dist/resources/extensions/gsd/metrics.ts +3 -3
- package/dist/resources/extensions/gsd/migrate-external.ts +21 -4
- package/dist/resources/extensions/gsd/milestone-ids.ts +2 -1
- package/dist/resources/extensions/gsd/native-git-bridge.ts +2 -1
- package/dist/resources/extensions/gsd/parallel-merge.ts +2 -1
- package/dist/resources/extensions/gsd/parallel-orchestrator.ts +2 -1
- package/dist/resources/extensions/gsd/post-unit-hooks.ts +8 -9
- package/dist/resources/extensions/gsd/quick.ts +58 -3
- package/dist/resources/extensions/gsd/repo-identity.ts +22 -1
- package/dist/resources/extensions/gsd/session-lock.ts +86 -11
- package/dist/resources/extensions/gsd/tests/all-milestones-complete-merge.test.ts +14 -11
- package/dist/resources/extensions/gsd/tests/auto-dispatch-loop.test.ts +691 -0
- package/dist/resources/extensions/gsd/tests/cache-staleness-regression.test.ts +317 -0
- package/dist/resources/extensions/gsd/tests/context-compression.test.ts +1 -1
- package/dist/resources/extensions/gsd/tests/loop-regression.test.ts +877 -0
- package/dist/resources/extensions/gsd/tests/roadmap-parse-regression.test.ts +358 -0
- package/dist/resources/extensions/gsd/tests/session-lock-regression.test.ts +216 -0
- package/dist/resources/extensions/gsd/tests/session-lock.test.ts +119 -0
- package/dist/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +206 -0
- package/dist/resources/extensions/gsd/undo.ts +5 -7
- package/dist/resources/extensions/gsd/unit-id.ts +14 -0
- package/dist/resources/extensions/gsd/unit-runtime.ts +2 -1
- package/dist/resources/extensions/gsd/worktree-command.ts +8 -7
- package/package.json +3 -2
- package/packages/pi-coding-agent/package.json +1 -1
- package/pkg/package.json +1 -1
- package/src/resources/extensions/gsd/auto-dashboard.ts +3 -1
- package/src/resources/extensions/gsd/auto-dispatch.ts +40 -12
- package/src/resources/extensions/gsd/auto-idempotency.ts +3 -2
- package/src/resources/extensions/gsd/auto-observability.ts +2 -4
- package/src/resources/extensions/gsd/auto-post-unit.ts +5 -5
- package/src/resources/extensions/gsd/auto-prompts.ts +46 -44
- package/src/resources/extensions/gsd/auto-recovery.ts +8 -22
- package/src/resources/extensions/gsd/auto-start.ts +8 -6
- package/src/resources/extensions/gsd/auto-stuck-detection.ts +3 -2
- package/src/resources/extensions/gsd/auto-supervisor.ts +10 -5
- package/src/resources/extensions/gsd/auto-timeout-recovery.ts +2 -1
- package/src/resources/extensions/gsd/auto-timers.ts +3 -2
- package/src/resources/extensions/gsd/auto-verification.ts +6 -6
- package/src/resources/extensions/gsd/auto-worktree.ts +140 -5
- package/src/resources/extensions/gsd/auto.ts +108 -182
- package/src/resources/extensions/gsd/commands-inspect.ts +2 -1
- package/src/resources/extensions/gsd/commands-workflow-templates.ts +2 -1
- package/src/resources/extensions/gsd/commands.ts +14 -2
- package/src/resources/extensions/gsd/complexity-classifier.ts +5 -7
- package/src/resources/extensions/gsd/crash-recovery.ts +15 -2
- package/src/resources/extensions/gsd/dispatch-guard.ts +2 -1
- package/src/resources/extensions/gsd/error-utils.ts +6 -0
- package/src/resources/extensions/gsd/export.ts +2 -1
- package/src/resources/extensions/gsd/git-service.ts +3 -2
- package/src/resources/extensions/gsd/guided-flow.ts +3 -2
- package/src/resources/extensions/gsd/index.ts +12 -5
- package/src/resources/extensions/gsd/key-manager.ts +2 -1
- package/src/resources/extensions/gsd/marketplace-discovery.ts +4 -3
- package/src/resources/extensions/gsd/metrics.ts +3 -3
- package/src/resources/extensions/gsd/migrate-external.ts +21 -4
- package/src/resources/extensions/gsd/milestone-ids.ts +2 -1
- package/src/resources/extensions/gsd/native-git-bridge.ts +2 -1
- package/src/resources/extensions/gsd/parallel-merge.ts +2 -1
- package/src/resources/extensions/gsd/parallel-orchestrator.ts +2 -1
- package/src/resources/extensions/gsd/post-unit-hooks.ts +8 -9
- package/src/resources/extensions/gsd/quick.ts +58 -3
- package/src/resources/extensions/gsd/repo-identity.ts +22 -1
- package/src/resources/extensions/gsd/session-lock.ts +86 -11
- package/src/resources/extensions/gsd/tests/all-milestones-complete-merge.test.ts +14 -11
- package/src/resources/extensions/gsd/tests/auto-dispatch-loop.test.ts +691 -0
- package/src/resources/extensions/gsd/tests/cache-staleness-regression.test.ts +317 -0
- package/src/resources/extensions/gsd/tests/context-compression.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/loop-regression.test.ts +877 -0
- package/src/resources/extensions/gsd/tests/roadmap-parse-regression.test.ts +358 -0
- package/src/resources/extensions/gsd/tests/session-lock-regression.test.ts +216 -0
- package/src/resources/extensions/gsd/tests/session-lock.test.ts +119 -0
- package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +206 -0
- package/src/resources/extensions/gsd/undo.ts +5 -7
- package/src/resources/extensions/gsd/unit-id.ts +14 -0
- package/src/resources/extensions/gsd/unit-runtime.ts +2 -1
- package/src/resources/extensions/gsd/worktree-command.ts +8 -7
- package/dist/resources/extensions/mcporter/extension-manifest.json +0 -12
- package/src/resources/extensions/mcporter/extension-manifest.json +0 -12
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Auto-mode Supervisor —
|
|
2
|
+
* Auto-mode Supervisor — signal handling and working-tree activity detection.
|
|
3
3
|
*
|
|
4
4
|
* Pure functions — no module-level globals or AutoContext dependency.
|
|
5
5
|
*/
|
|
@@ -8,10 +8,10 @@ import { clearLock } from "./crash-recovery.js";
|
|
|
8
8
|
import { releaseSessionLock } from "./session-lock.js";
|
|
9
9
|
import { nativeHasChanges } from "./native-git-bridge.js";
|
|
10
10
|
|
|
11
|
-
// ───
|
|
11
|
+
// ─── Signal Handling ──────────────────────────────────────────────────────────
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
|
-
* Register
|
|
14
|
+
* Register SIGTERM and SIGINT handlers that clear lock files and exit cleanly.
|
|
15
15
|
* Captures the active base path at registration time so the handler
|
|
16
16
|
* always references the correct path even if the module variable changes.
|
|
17
17
|
* Removes any previously registered handler before installing the new one.
|
|
@@ -22,20 +22,25 @@ export function registerSigtermHandler(
|
|
|
22
22
|
currentBasePath: string,
|
|
23
23
|
previousHandler: (() => void) | null,
|
|
24
24
|
): () => void {
|
|
25
|
-
if (previousHandler)
|
|
25
|
+
if (previousHandler) {
|
|
26
|
+
process.off("SIGTERM", previousHandler);
|
|
27
|
+
process.off("SIGINT", previousHandler);
|
|
28
|
+
}
|
|
26
29
|
const handler = () => {
|
|
27
30
|
releaseSessionLock(currentBasePath);
|
|
28
31
|
clearLock(currentBasePath);
|
|
29
32
|
process.exit(0);
|
|
30
33
|
};
|
|
31
34
|
process.on("SIGTERM", handler);
|
|
35
|
+
process.on("SIGINT", handler);
|
|
32
36
|
return handler;
|
|
33
37
|
}
|
|
34
38
|
|
|
35
|
-
/** Deregister
|
|
39
|
+
/** Deregister signal handlers (called on stop/pause). */
|
|
36
40
|
export function deregisterSigtermHandler(handler: (() => void) | null): void {
|
|
37
41
|
if (handler) {
|
|
38
42
|
process.off("SIGTERM", handler);
|
|
43
|
+
process.off("SIGINT", handler);
|
|
39
44
|
}
|
|
40
45
|
}
|
|
41
46
|
|
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
writeBlockerPlaceholder,
|
|
19
19
|
} from "./auto-recovery.js";
|
|
20
20
|
import { existsSync } from "node:fs";
|
|
21
|
+
import { parseUnitId } from "./unit-id.js";
|
|
21
22
|
|
|
22
23
|
export interface RecoveryContext {
|
|
23
24
|
basePath: string;
|
|
@@ -128,7 +129,7 @@ export async function recoverTimedOutUnit(
|
|
|
128
129
|
|
|
129
130
|
// Retries exhausted — write missing durable artifacts and advance.
|
|
130
131
|
const diagnostic = formatExecuteTaskRecoveryStatus(status);
|
|
131
|
-
const
|
|
132
|
+
const { milestone: mid, slice: sid, task: tid } = parseUnitId(unitId);
|
|
132
133
|
const skipped = mid && sid && tid
|
|
133
134
|
? skipExecuteTask(basePath, mid, sid, tid, status, reason, maxRecoveryAttempts)
|
|
134
135
|
: false;
|
|
@@ -20,6 +20,7 @@ import { closeoutUnit, type CloseoutOptions } from "./auto-unit-closeout.js";
|
|
|
20
20
|
import { saveActivityLog } from "./activity-log.js";
|
|
21
21
|
import { recoverTimedOutUnit, type RecoveryContext } from "./auto-timeout-recovery.js";
|
|
22
22
|
import type { AutoSession } from "./auto/session.js";
|
|
23
|
+
import { getErrorMessage } from "./error-utils.js";
|
|
23
24
|
|
|
24
25
|
export interface SupervisionContext {
|
|
25
26
|
s: AutoSession;
|
|
@@ -127,7 +128,7 @@ export function startUnitSupervision(sctx: SupervisionContext): void {
|
|
|
127
128
|
);
|
|
128
129
|
await pauseAuto(ctx, pi);
|
|
129
130
|
} catch (err) {
|
|
130
|
-
const message =
|
|
131
|
+
const message = getErrorMessage(err);
|
|
131
132
|
console.error(`[idle-watchdog] Unhandled error: ${message}`);
|
|
132
133
|
try {
|
|
133
134
|
ctx.ui.notify(`Idle watchdog error: ${message}`, "warning");
|
|
@@ -159,7 +160,7 @@ export function startUnitSupervision(sctx: SupervisionContext): void {
|
|
|
159
160
|
);
|
|
160
161
|
await pauseAuto(ctx, pi);
|
|
161
162
|
} catch (err) {
|
|
162
|
-
const message =
|
|
163
|
+
const message = getErrorMessage(err);
|
|
163
164
|
console.error(`[hard-timeout] Unhandled error: ${message}`);
|
|
164
165
|
try {
|
|
165
166
|
ctx.ui.notify(`Hard timeout error: ${message}`, "warning");
|
|
@@ -24,6 +24,8 @@ import { writeVerificationJSON } from "./verification-evidence.js";
|
|
|
24
24
|
import { removePersistedKey } from "./auto-recovery.js";
|
|
25
25
|
import type { AutoSession, PendingVerificationRetry } from "./auto/session.js";
|
|
26
26
|
import { join } from "node:path";
|
|
27
|
+
import { getErrorMessage } from "./error-utils.js";
|
|
28
|
+
import { parseUnitId } from "./unit-id.js";
|
|
27
29
|
|
|
28
30
|
export interface VerificationContext {
|
|
29
31
|
s: AutoSession;
|
|
@@ -57,10 +59,9 @@ export async function runPostUnitVerification(
|
|
|
57
59
|
const prefs = effectivePrefs?.preferences;
|
|
58
60
|
|
|
59
61
|
// Read task plan verify field
|
|
60
|
-
const
|
|
62
|
+
const { milestone: mid, slice: sid, task: tid } = parseUnitId(s.currentUnit.id);
|
|
61
63
|
let taskPlanVerify: string | undefined;
|
|
62
|
-
if (
|
|
63
|
-
const [mid, sid, tid] = parts;
|
|
64
|
+
if (mid && sid && tid) {
|
|
64
65
|
const planFile = resolveSliceFile(s.basePath, mid, sid, "PLAN");
|
|
65
66
|
if (planFile) {
|
|
66
67
|
const planContent = await loadFile(planFile);
|
|
@@ -152,9 +153,8 @@ export async function runPostUnitVerification(
|
|
|
152
153
|
|
|
153
154
|
// Write verification evidence JSON
|
|
154
155
|
const attempt = s.verificationRetryCount.get(s.currentUnit.id) ?? 0;
|
|
155
|
-
if (
|
|
156
|
+
if (mid && sid && tid) {
|
|
156
157
|
try {
|
|
157
|
-
const [mid, sid, tid] = parts;
|
|
158
158
|
const sDir = resolveSlicePath(s.basePath, mid, sid);
|
|
159
159
|
if (sDir) {
|
|
160
160
|
const tasksDir = join(sDir, "tasks");
|
|
@@ -204,7 +204,7 @@ export async function runPostUnitVerification(
|
|
|
204
204
|
try {
|
|
205
205
|
await dispatchNextUnit(ctx, pi);
|
|
206
206
|
} catch (retryDispatchErr) {
|
|
207
|
-
const msg =
|
|
207
|
+
const msg = getErrorMessage(retryDispatchErr);
|
|
208
208
|
ctx.ui.notify(`Verification retry dispatch error: ${msg}`, "error");
|
|
209
209
|
startDispatchGapWatchdog(ctx, pi);
|
|
210
210
|
}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* manages create, enter, detect, and teardown for auto-mode worktrees.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import { existsSync, readFileSync, realpathSync, unlinkSync, statSync, rmSync } from "node:fs";
|
|
9
|
+
import { existsSync, readFileSync, realpathSync, unlinkSync, statSync, rmSync, readdirSync, cpSync, lstatSync as lstatSyncFn } from "node:fs";
|
|
10
10
|
import { isAbsolute, join, sep } from "node:path";
|
|
11
11
|
import { GSDError, GSD_IO_ERROR, GSD_GIT_ERROR } from "./errors.js";
|
|
12
12
|
import { execSync, execFileSync } from "node:child_process";
|
|
@@ -38,12 +38,129 @@ import {
|
|
|
38
38
|
nativeBranchDelete,
|
|
39
39
|
nativeBranchExists,
|
|
40
40
|
} from "./native-git-bridge.js";
|
|
41
|
+
import { getErrorMessage } from "./error-utils.js";
|
|
41
42
|
|
|
42
43
|
// ─── Module State ──────────────────────────────────────────────────────────
|
|
43
44
|
|
|
44
45
|
/** Original project root before chdir into auto-worktree. */
|
|
45
46
|
let originalBase: string | null = null;
|
|
46
47
|
|
|
48
|
+
// ─── Worktree ↔ Main Repo Sync (#1311) ──────────────────────────────────────
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Sync .gsd/ state from the main repo into the worktree.
|
|
52
|
+
*
|
|
53
|
+
* When .gsd/ is a symlink to the external state directory, both the main
|
|
54
|
+
* repo and worktree share the same directory — no sync needed.
|
|
55
|
+
*
|
|
56
|
+
* When .gsd/ is a real directory (e.g., git-tracked or manage_gitignore:false),
|
|
57
|
+
* the worktree has its own copy that may be stale. This function copies
|
|
58
|
+
* missing milestones, CONTEXT, ROADMAP, DECISIONS, REQUIREMENTS, and
|
|
59
|
+
* PROJECT files from the main repo's .gsd/ into the worktree's .gsd/.
|
|
60
|
+
*
|
|
61
|
+
* Only adds missing content — never overwrites existing files in the worktree
|
|
62
|
+
* (the worktree's execution state is authoritative for in-progress work).
|
|
63
|
+
*/
|
|
64
|
+
export function syncGsdStateToWorktree(mainBasePath: string, worktreePath_: string): { synced: string[] } {
|
|
65
|
+
const mainGsd = gsdRoot(mainBasePath);
|
|
66
|
+
const wtGsd = gsdRoot(worktreePath_);
|
|
67
|
+
const synced: string[] = [];
|
|
68
|
+
|
|
69
|
+
// If both resolve to the same directory (symlink), no sync needed
|
|
70
|
+
try {
|
|
71
|
+
const mainResolved = realpathSync(mainGsd);
|
|
72
|
+
const wtResolved = realpathSync(wtGsd);
|
|
73
|
+
if (mainResolved === wtResolved) return { synced };
|
|
74
|
+
} catch {
|
|
75
|
+
// Can't resolve — proceed with sync as a safety measure
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (!existsSync(mainGsd) || !existsSync(wtGsd)) return { synced };
|
|
79
|
+
|
|
80
|
+
// Sync root-level .gsd/ files (DECISIONS, REQUIREMENTS, PROJECT, KNOWLEDGE)
|
|
81
|
+
const rootFiles = ["DECISIONS.md", "REQUIREMENTS.md", "PROJECT.md", "KNOWLEDGE.md", "OVERRIDES.md"];
|
|
82
|
+
for (const f of rootFiles) {
|
|
83
|
+
const src = join(mainGsd, f);
|
|
84
|
+
const dst = join(wtGsd, f);
|
|
85
|
+
if (existsSync(src) && !existsSync(dst)) {
|
|
86
|
+
try {
|
|
87
|
+
cpSync(src, dst);
|
|
88
|
+
synced.push(f);
|
|
89
|
+
} catch { /* non-fatal */ }
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Sync milestones: copy entire milestone directories that are missing
|
|
94
|
+
const mainMilestonesDir = join(mainGsd, "milestones");
|
|
95
|
+
const wtMilestonesDir = join(wtGsd, "milestones");
|
|
96
|
+
if (existsSync(mainMilestonesDir) && existsSync(wtMilestonesDir)) {
|
|
97
|
+
try {
|
|
98
|
+
const mainMilestones = readdirSync(mainMilestonesDir, { withFileTypes: true })
|
|
99
|
+
.filter(d => d.isDirectory() && /^M\d{3}/.test(d.name))
|
|
100
|
+
.map(d => d.name);
|
|
101
|
+
|
|
102
|
+
for (const mid of mainMilestones) {
|
|
103
|
+
const srcDir = join(mainMilestonesDir, mid);
|
|
104
|
+
const dstDir = join(wtMilestonesDir, mid);
|
|
105
|
+
|
|
106
|
+
if (!existsSync(dstDir)) {
|
|
107
|
+
// Entire milestone missing from worktree — copy it
|
|
108
|
+
try {
|
|
109
|
+
cpSync(srcDir, dstDir, { recursive: true });
|
|
110
|
+
synced.push(`milestones/${mid}/`);
|
|
111
|
+
} catch { /* non-fatal */ }
|
|
112
|
+
} else {
|
|
113
|
+
// Milestone directory exists but may be missing files (stale snapshot).
|
|
114
|
+
// Sync individual top-level milestone files (CONTEXT, ROADMAP, RESEARCH, etc.)
|
|
115
|
+
try {
|
|
116
|
+
const srcFiles = readdirSync(srcDir).filter(f => f.endsWith(".md") || f.endsWith(".json"));
|
|
117
|
+
for (const f of srcFiles) {
|
|
118
|
+
const srcFile = join(srcDir, f);
|
|
119
|
+
const dstFile = join(dstDir, f);
|
|
120
|
+
if (!existsSync(dstFile)) {
|
|
121
|
+
try {
|
|
122
|
+
const srcStat = lstatSyncFn(srcFile);
|
|
123
|
+
if (srcStat.isFile()) {
|
|
124
|
+
cpSync(srcFile, dstFile);
|
|
125
|
+
synced.push(`milestones/${mid}/${f}`);
|
|
126
|
+
}
|
|
127
|
+
} catch { /* non-fatal */ }
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Sync slices directory if it exists in main but not in worktree
|
|
132
|
+
const srcSlicesDir = join(srcDir, "slices");
|
|
133
|
+
const dstSlicesDir = join(dstDir, "slices");
|
|
134
|
+
if (existsSync(srcSlicesDir) && !existsSync(dstSlicesDir)) {
|
|
135
|
+
try {
|
|
136
|
+
cpSync(srcSlicesDir, dstSlicesDir, { recursive: true });
|
|
137
|
+
synced.push(`milestones/${mid}/slices/`);
|
|
138
|
+
} catch { /* non-fatal */ }
|
|
139
|
+
} else if (existsSync(srcSlicesDir) && existsSync(dstSlicesDir)) {
|
|
140
|
+
// Both exist — sync missing slice directories
|
|
141
|
+
const srcSlices = readdirSync(srcSlicesDir, { withFileTypes: true })
|
|
142
|
+
.filter(d => d.isDirectory())
|
|
143
|
+
.map(d => d.name);
|
|
144
|
+
for (const sid of srcSlices) {
|
|
145
|
+
const srcSlice = join(srcSlicesDir, sid);
|
|
146
|
+
const dstSlice = join(dstSlicesDir, sid);
|
|
147
|
+
if (!existsSync(dstSlice)) {
|
|
148
|
+
try {
|
|
149
|
+
cpSync(srcSlice, dstSlice, { recursive: true });
|
|
150
|
+
synced.push(`milestones/${mid}/slices/${sid}/`);
|
|
151
|
+
} catch { /* non-fatal */ }
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
} catch { /* non-fatal */ }
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
} catch { /* non-fatal */ }
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return { synced };
|
|
162
|
+
}
|
|
163
|
+
|
|
47
164
|
// ─── Worktree Post-Create Hook (#597) ────────────────────────────────────────
|
|
48
165
|
|
|
49
166
|
/**
|
|
@@ -81,7 +198,7 @@ export function runWorktreePostCreateHook(sourceDir: string, worktreeDir: string
|
|
|
81
198
|
});
|
|
82
199
|
return null;
|
|
83
200
|
} catch (err) {
|
|
84
|
-
const msg =
|
|
201
|
+
const msg = getErrorMessage(err);
|
|
85
202
|
return `Worktree post-create hook failed: ${msg}`;
|
|
86
203
|
}
|
|
87
204
|
}
|
|
@@ -124,6 +241,12 @@ export function createAutoWorktree(basePath: string, milestoneId: string): strin
|
|
|
124
241
|
// Ensure worktree shares external state via symlink
|
|
125
242
|
ensureGsdSymlink(info.path);
|
|
126
243
|
|
|
244
|
+
// Sync .gsd/ state from main repo into the worktree (#1311).
|
|
245
|
+
// Even with the symlink, the worktree may have stale git-tracked files
|
|
246
|
+
// if .gsd/ is not gitignored. And on fresh create, the milestone files
|
|
247
|
+
// created on main since the branch point won't be in the worktree.
|
|
248
|
+
syncGsdStateToWorktree(basePath, info.path);
|
|
249
|
+
|
|
127
250
|
// Run user-configured post-create hook (#597) — e.g. copy .env, symlink assets
|
|
128
251
|
const hookError = runWorktreePostCreateHook(basePath, info.path);
|
|
129
252
|
if (hookError) {
|
|
@@ -141,7 +264,7 @@ export function createAutoWorktree(basePath: string, milestoneId: string): strin
|
|
|
141
264
|
// Don't store originalBase -- caller can retry or clean up.
|
|
142
265
|
throw new GSDError(
|
|
143
266
|
GSD_IO_ERROR,
|
|
144
|
-
`Auto-worktree created at ${info.path} but chdir failed: ${
|
|
267
|
+
`Auto-worktree created at ${info.path} but chdir failed: ${getErrorMessage(err)}`,
|
|
145
268
|
);
|
|
146
269
|
}
|
|
147
270
|
|
|
@@ -168,7 +291,7 @@ export function teardownAutoWorktree(
|
|
|
168
291
|
} catch (err) {
|
|
169
292
|
throw new GSDError(
|
|
170
293
|
GSD_IO_ERROR,
|
|
171
|
-
`Failed to chdir back to ${originalBasePath} during teardown: ${
|
|
294
|
+
`Failed to chdir back to ${originalBasePath} during teardown: ${getErrorMessage(err)}`,
|
|
172
295
|
);
|
|
173
296
|
}
|
|
174
297
|
|
|
@@ -266,6 +389,18 @@ export function enterAutoWorktree(basePath: string, milestoneId: string): string
|
|
|
266
389
|
throw new GSDError(GSD_IO_ERROR, `Auto-worktree path ${p} exists but .git is unreadable`);
|
|
267
390
|
}
|
|
268
391
|
|
|
392
|
+
// Ensure worktree shares external state via symlink (#1311).
|
|
393
|
+
// On resume (enterAutoWorktree), the symlink may be missing if it was
|
|
394
|
+
// created before ensureGsdSymlink existed, or the .gsd/ directory may be
|
|
395
|
+
// a stale git-tracked copy instead of a symlink. Refreshing here ensures
|
|
396
|
+
// the worktree sees the same milestone state as the main repo.
|
|
397
|
+
ensureGsdSymlink(p);
|
|
398
|
+
|
|
399
|
+
// Sync .gsd/ state from main repo into worktree (#1311).
|
|
400
|
+
// Covers the case where .gsd/ is a real directory (not symlinked) and
|
|
401
|
+
// milestones were created on main after the worktree was last used.
|
|
402
|
+
syncGsdStateToWorktree(basePath, p);
|
|
403
|
+
|
|
269
404
|
const previousCwd = process.cwd();
|
|
270
405
|
|
|
271
406
|
try {
|
|
@@ -274,7 +409,7 @@ export function enterAutoWorktree(basePath: string, milestoneId: string): string
|
|
|
274
409
|
} catch (err) {
|
|
275
410
|
throw new GSDError(
|
|
276
411
|
GSD_IO_ERROR,
|
|
277
|
-
`Failed to enter auto-worktree at ${p}: ${
|
|
412
|
+
`Failed to enter auto-worktree at ${p}: ${getErrorMessage(err)}`,
|
|
278
413
|
);
|
|
279
414
|
}
|
|
280
415
|
|