gsd-pi 2.32.0-dev.1e39869 → 2.32.0-dev.3d7932c
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 +27 -20
- package/dist/resource-loader.js +13 -3
- package/dist/resources/extensions/gsd/auto-dashboard.ts +3 -1
- 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-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 +5 -4
- package/dist/resources/extensions/gsd/auto.ts +28 -27
- 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/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 +12 -1
- package/dist/resources/extensions/gsd/tests/context-compression.test.ts +1 -1
- 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 +1 -1
- package/src/resources/extensions/gsd/auto-dashboard.ts +3 -1
- 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-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 +5 -4
- package/src/resources/extensions/gsd/auto.ts +28 -27
- 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/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 +12 -1
- package/src/resources/extensions/gsd/tests/context-compression.test.ts +1 -1
- 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
|
@@ -38,6 +38,7 @@ 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
|
|
|
@@ -81,7 +82,7 @@ export function runWorktreePostCreateHook(sourceDir: string, worktreeDir: string
|
|
|
81
82
|
});
|
|
82
83
|
return null;
|
|
83
84
|
} catch (err) {
|
|
84
|
-
const msg =
|
|
85
|
+
const msg = getErrorMessage(err);
|
|
85
86
|
return `Worktree post-create hook failed: ${msg}`;
|
|
86
87
|
}
|
|
87
88
|
}
|
|
@@ -141,7 +142,7 @@ export function createAutoWorktree(basePath: string, milestoneId: string): strin
|
|
|
141
142
|
// Don't store originalBase -- caller can retry or clean up.
|
|
142
143
|
throw new GSDError(
|
|
143
144
|
GSD_IO_ERROR,
|
|
144
|
-
`Auto-worktree created at ${info.path} but chdir failed: ${
|
|
145
|
+
`Auto-worktree created at ${info.path} but chdir failed: ${getErrorMessage(err)}`,
|
|
145
146
|
);
|
|
146
147
|
}
|
|
147
148
|
|
|
@@ -168,7 +169,7 @@ export function teardownAutoWorktree(
|
|
|
168
169
|
} catch (err) {
|
|
169
170
|
throw new GSDError(
|
|
170
171
|
GSD_IO_ERROR,
|
|
171
|
-
`Failed to chdir back to ${originalBasePath} during teardown: ${
|
|
172
|
+
`Failed to chdir back to ${originalBasePath} during teardown: ${getErrorMessage(err)}`,
|
|
172
173
|
);
|
|
173
174
|
}
|
|
174
175
|
|
|
@@ -274,7 +275,7 @@ export function enterAutoWorktree(basePath: string, milestoneId: string): string
|
|
|
274
275
|
} catch (err) {
|
|
275
276
|
throw new GSDError(
|
|
276
277
|
GSD_IO_ERROR,
|
|
277
|
-
`Failed to enter auto-worktree at ${p}: ${
|
|
278
|
+
`Failed to enter auto-worktree at ${p}: ${getErrorMessage(err)}`,
|
|
278
279
|
);
|
|
279
280
|
}
|
|
280
281
|
|
|
@@ -105,6 +105,7 @@ import { computeBudgets, resolveExecutorContextWindow } from "./context-budget.j
|
|
|
105
105
|
import { GSDError, GSD_ARTIFACT_MISSING } from "./errors.js";
|
|
106
106
|
import { join } from "node:path";
|
|
107
107
|
import { sep as pathSep } from "node:path";
|
|
108
|
+
import { parseUnitId } from "./unit-id.js";
|
|
108
109
|
import { readdirSync, readFileSync, existsSync, mkdirSync, writeFileSync, unlinkSync, statSync } from "node:fs";
|
|
109
110
|
import { atomicWriteSync } from "./atomic-write.js";
|
|
110
111
|
import { nativeIsRepo, nativeInit, nativeAddAll, nativeCommit } from "./native-git-bridge.js";
|
|
@@ -189,6 +190,7 @@ import {
|
|
|
189
190
|
NEW_SESSION_TIMEOUT_MS, DISPATCH_HANG_TIMEOUT_MS,
|
|
190
191
|
} from "./auto/session.js";
|
|
191
192
|
import type { CompletedUnit, CurrentUnit, UnitRouting, StartModel, PendingVerificationRetry } from "./auto/session.js";
|
|
193
|
+
import { getErrorMessage } from "./error-utils.js";
|
|
192
194
|
|
|
193
195
|
// ── ENCAPSULATION INVARIANT ─────────────────────────────────────────────────
|
|
194
196
|
// ALL mutable auto-mode state lives in the AutoSession class (auto/session.ts).
|
|
@@ -428,7 +430,7 @@ function startDispatchGapWatchdog(ctx: ExtensionContext, pi: ExtensionAPI): void
|
|
|
428
430
|
try {
|
|
429
431
|
await dispatchNextUnit(ctx, pi);
|
|
430
432
|
} catch (retryErr) {
|
|
431
|
-
const message =
|
|
433
|
+
const message = getErrorMessage(retryErr);
|
|
432
434
|
await stopAuto(ctx, pi, `Dispatch gap recovery failed: ${message}`);
|
|
433
435
|
return;
|
|
434
436
|
}
|
|
@@ -458,14 +460,14 @@ export async function stopAuto(ctx?: ExtensionContext, pi?: ExtensionAPI, reason
|
|
|
458
460
|
// ── Auto-worktree: exit worktree and reset s.basePath on stop ──
|
|
459
461
|
if (s.currentMilestoneId && isInAutoWorktree(s.basePath)) {
|
|
460
462
|
try {
|
|
461
|
-
try { autoCommitCurrentBranch(s.basePath, "stop", s.currentMilestoneId); } catch (e) { debugLog("stop-auto-commit-failed", { error:
|
|
463
|
+
try { autoCommitCurrentBranch(s.basePath, "stop", s.currentMilestoneId); } catch (e) { debugLog("stop-auto-commit-failed", { error: getErrorMessage(e) }); }
|
|
462
464
|
teardownAutoWorktree(s.originalBasePath, s.currentMilestoneId, { preserveBranch: true });
|
|
463
465
|
s.basePath = s.originalBasePath;
|
|
464
466
|
s.gitService = createGitService(s.basePath);
|
|
465
467
|
ctx?.ui.notify("Exited auto-worktree (branch preserved for resume).", "info");
|
|
466
468
|
} catch (err) {
|
|
467
469
|
ctx?.ui.notify(
|
|
468
|
-
`Auto-worktree teardown failed: ${
|
|
470
|
+
`Auto-worktree teardown failed: ${getErrorMessage(err)}`,
|
|
469
471
|
"warning",
|
|
470
472
|
);
|
|
471
473
|
}
|
|
@@ -476,7 +478,7 @@ export async function stopAuto(ctx?: ExtensionContext, pi?: ExtensionAPI, reason
|
|
|
476
478
|
try {
|
|
477
479
|
const { closeDatabase } = await import("./gsd-db.js");
|
|
478
480
|
closeDatabase();
|
|
479
|
-
} catch (e) { debugLog("db-close-failed", { error:
|
|
481
|
+
} catch (e) { debugLog("db-close-failed", { error: getErrorMessage(e) }); }
|
|
480
482
|
}
|
|
481
483
|
|
|
482
484
|
if (s.originalBasePath) {
|
|
@@ -496,7 +498,7 @@ export async function stopAuto(ctx?: ExtensionContext, pi?: ExtensionAPI, reason
|
|
|
496
498
|
}
|
|
497
499
|
|
|
498
500
|
if (s.basePath) {
|
|
499
|
-
try { await rebuildState(s.basePath); } catch (e) { debugLog("stop-rebuild-state-failed", { error:
|
|
501
|
+
try { await rebuildState(s.basePath); } catch (e) { debugLog("stop-rebuild-state-failed", { error: getErrorMessage(e) }); }
|
|
500
502
|
}
|
|
501
503
|
|
|
502
504
|
if (isDebugEnabled()) {
|
|
@@ -635,7 +637,7 @@ export async function startAuto(
|
|
|
635
637
|
}
|
|
636
638
|
} catch (err) {
|
|
637
639
|
ctx.ui.notify(
|
|
638
|
-
`Auto-worktree re-entry failed: ${
|
|
640
|
+
`Auto-worktree re-entry failed: ${getErrorMessage(err)}. Continuing at current path.`,
|
|
639
641
|
"warning",
|
|
640
642
|
);
|
|
641
643
|
}
|
|
@@ -647,13 +649,13 @@ export async function startAuto(
|
|
|
647
649
|
ctx.ui.setFooter(hideFooter);
|
|
648
650
|
ctx.ui.notify(s.stepMode ? "Step-mode resumed." : "Auto-mode resumed.", "info");
|
|
649
651
|
restoreHookState(s.basePath);
|
|
650
|
-
try { await rebuildState(s.basePath); } catch (e) { debugLog("resume-rebuild-state-failed", { error:
|
|
652
|
+
try { await rebuildState(s.basePath); } catch (e) { debugLog("resume-rebuild-state-failed", { error: getErrorMessage(e) }); }
|
|
651
653
|
try {
|
|
652
654
|
const report = await runGSDDoctor(s.basePath, { fix: true });
|
|
653
655
|
if (report.fixesApplied.length > 0) {
|
|
654
656
|
ctx.ui.notify(`Resume: applied ${report.fixesApplied.length} fix(es) to state.`, "info");
|
|
655
657
|
}
|
|
656
|
-
} catch (e) { debugLog("resume-doctor-failed", { error:
|
|
658
|
+
} catch (e) { debugLog("resume-doctor-failed", { error: getErrorMessage(e) }); }
|
|
657
659
|
await selfHealRuntimeRecords(s.basePath, ctx, s.completedKeySet);
|
|
658
660
|
invalidateAllCaches();
|
|
659
661
|
|
|
@@ -700,7 +702,7 @@ export async function startAuto(
|
|
|
700
702
|
}
|
|
701
703
|
} catch (err) {
|
|
702
704
|
ctx.ui.notify(
|
|
703
|
-
`Secrets check error: ${
|
|
705
|
+
`Secrets check error: ${getErrorMessage(err)}. Continuing without secrets.`,
|
|
704
706
|
"warning",
|
|
705
707
|
);
|
|
706
708
|
}
|
|
@@ -807,7 +809,7 @@ export async function handleAgentEnd(
|
|
|
807
809
|
try {
|
|
808
810
|
await dispatchNextUnit(ctx, pi);
|
|
809
811
|
} catch (dispatchErr) {
|
|
810
|
-
const message =
|
|
812
|
+
const message = getErrorMessage(dispatchErr);
|
|
811
813
|
ctx.ui.notify(
|
|
812
814
|
`Dispatch error after unit completion: ${message}. Retrying in ${DISPATCH_GAP_TIMEOUT_MS / 1000}s.`,
|
|
813
815
|
"error",
|
|
@@ -838,7 +840,7 @@ export async function handleAgentEnd(
|
|
|
838
840
|
clearDispatchGapWatchdog();
|
|
839
841
|
setImmediate(() => {
|
|
840
842
|
handleAgentEnd(ctx, pi).catch((err) => {
|
|
841
|
-
const msg =
|
|
843
|
+
const msg = getErrorMessage(err);
|
|
842
844
|
ctx.ui.notify(`Deferred agent_end retry failed: ${msg}`, "error");
|
|
843
845
|
pauseAuto(ctx, pi).catch(() => {});
|
|
844
846
|
});
|
|
@@ -1086,7 +1088,7 @@ async function dispatchNextUnit(
|
|
|
1086
1088
|
);
|
|
1087
1089
|
} catch (err) {
|
|
1088
1090
|
ctx.ui.notify(
|
|
1089
|
-
`Report generation failed: ${
|
|
1091
|
+
`Report generation failed: ${getErrorMessage(err)}`,
|
|
1090
1092
|
"warning",
|
|
1091
1093
|
);
|
|
1092
1094
|
}
|
|
@@ -1102,7 +1104,7 @@ async function dispatchNextUnit(
|
|
|
1102
1104
|
atomicWriteSync(file, JSON.stringify([]));
|
|
1103
1105
|
}
|
|
1104
1106
|
s.completedKeySet.clear();
|
|
1105
|
-
} catch (e) { debugLog("completed-keys-reset-failed", { error:
|
|
1107
|
+
} catch (e) { debugLog("completed-keys-reset-failed", { error: getErrorMessage(e) }); }
|
|
1106
1108
|
|
|
1107
1109
|
// ── Worktree lifecycle on milestone transition (#616) ──
|
|
1108
1110
|
if (isInAutoWorktree(s.basePath) && s.originalBasePath && shouldUseWorktreeIsolation()) {
|
|
@@ -1121,7 +1123,7 @@ async function dispatchNextUnit(
|
|
|
1121
1123
|
}
|
|
1122
1124
|
} catch (err) {
|
|
1123
1125
|
ctx.ui.notify(
|
|
1124
|
-
`Milestone merge failed during transition: ${
|
|
1126
|
+
`Milestone merge failed during transition: ${getErrorMessage(err)}`,
|
|
1125
1127
|
"warning",
|
|
1126
1128
|
);
|
|
1127
1129
|
if (s.originalBasePath) {
|
|
@@ -1146,7 +1148,7 @@ async function dispatchNextUnit(
|
|
|
1146
1148
|
ctx.ui.notify(`Created auto-worktree for ${mid} at ${wtPath}`, "info");
|
|
1147
1149
|
} catch (err) {
|
|
1148
1150
|
ctx.ui.notify(
|
|
1149
|
-
`Auto-worktree creation for ${mid} failed: ${
|
|
1151
|
+
`Auto-worktree creation for ${mid} failed: ${getErrorMessage(err)}. Continuing in project root.`,
|
|
1150
1152
|
"warning",
|
|
1151
1153
|
);
|
|
1152
1154
|
}
|
|
@@ -1190,7 +1192,7 @@ async function dispatchNextUnit(
|
|
|
1190
1192
|
}
|
|
1191
1193
|
} catch (err) {
|
|
1192
1194
|
ctx.ui.notify(
|
|
1193
|
-
`Milestone merge failed: ${
|
|
1195
|
+
`Milestone merge failed: ${getErrorMessage(err)}`,
|
|
1194
1196
|
"warning",
|
|
1195
1197
|
);
|
|
1196
1198
|
if (s.originalBasePath) {
|
|
@@ -1216,7 +1218,7 @@ async function dispatchNextUnit(
|
|
|
1216
1218
|
}
|
|
1217
1219
|
} catch (err) {
|
|
1218
1220
|
ctx.ui.notify(
|
|
1219
|
-
`Milestone merge failed (branch mode): ${
|
|
1221
|
+
`Milestone merge failed (branch mode): ${getErrorMessage(err)}`,
|
|
1220
1222
|
"warning",
|
|
1221
1223
|
);
|
|
1222
1224
|
}
|
|
@@ -1276,7 +1278,7 @@ async function dispatchNextUnit(
|
|
|
1276
1278
|
atomicWriteSync(file, JSON.stringify([]));
|
|
1277
1279
|
}
|
|
1278
1280
|
s.completedKeySet.clear();
|
|
1279
|
-
} catch (e) { debugLog("completed-keys-reset-failed", { error:
|
|
1281
|
+
} catch (e) { debugLog("completed-keys-reset-failed", { error: getErrorMessage(e) }); }
|
|
1280
1282
|
// ── Milestone merge ──
|
|
1281
1283
|
if (s.currentMilestoneId && isInAutoWorktree(s.basePath) && s.originalBasePath) {
|
|
1282
1284
|
try {
|
|
@@ -1292,7 +1294,7 @@ async function dispatchNextUnit(
|
|
|
1292
1294
|
);
|
|
1293
1295
|
} catch (err) {
|
|
1294
1296
|
ctx.ui.notify(
|
|
1295
|
-
`Milestone merge failed: ${
|
|
1297
|
+
`Milestone merge failed: ${getErrorMessage(err)}`,
|
|
1296
1298
|
"warning",
|
|
1297
1299
|
);
|
|
1298
1300
|
if (s.originalBasePath) {
|
|
@@ -1318,7 +1320,7 @@ async function dispatchNextUnit(
|
|
|
1318
1320
|
}
|
|
1319
1321
|
} catch (err) {
|
|
1320
1322
|
ctx.ui.notify(
|
|
1321
|
-
`Milestone merge failed (branch mode): ${
|
|
1323
|
+
`Milestone merge failed (branch mode): ${getErrorMessage(err)}`,
|
|
1322
1324
|
"warning",
|
|
1323
1325
|
);
|
|
1324
1326
|
}
|
|
@@ -1417,7 +1419,7 @@ async function dispatchNextUnit(
|
|
|
1417
1419
|
}
|
|
1418
1420
|
} catch (err) {
|
|
1419
1421
|
ctx.ui.notify(
|
|
1420
|
-
`Secrets collection error: ${
|
|
1422
|
+
`Secrets collection error: ${getErrorMessage(err)}. Continuing with next task.`,
|
|
1421
1423
|
"warning",
|
|
1422
1424
|
);
|
|
1423
1425
|
}
|
|
@@ -1628,7 +1630,7 @@ async function dispatchNextUnit(
|
|
|
1628
1630
|
);
|
|
1629
1631
|
result = await Promise.race([sessionPromise, timeoutPromise]);
|
|
1630
1632
|
} catch (sessionErr) {
|
|
1631
|
-
const msg =
|
|
1633
|
+
const msg = getErrorMessage(sessionErr);
|
|
1632
1634
|
ctx.ui.notify(`Session creation failed: ${msg}. Retrying via watchdog.`, "error");
|
|
1633
1635
|
throw new Error(`newSession() failed: ${msg}`);
|
|
1634
1636
|
}
|
|
@@ -1704,7 +1706,7 @@ async function dispatchNextUnit(
|
|
|
1704
1706
|
const { reorderForCaching } = await import("./prompt-ordering.js");
|
|
1705
1707
|
finalPrompt = reorderForCaching(finalPrompt);
|
|
1706
1708
|
} catch (reorderErr) {
|
|
1707
|
-
const msg =
|
|
1709
|
+
const msg = getErrorMessage(reorderErr);
|
|
1708
1710
|
process.stderr.write(`[gsd] prompt reorder failed (non-fatal): ${msg}\n`);
|
|
1709
1711
|
}
|
|
1710
1712
|
|
|
@@ -1747,8 +1749,7 @@ async function dispatchNextUnit(
|
|
|
1747
1749
|
function ensurePreconditions(
|
|
1748
1750
|
unitType: string, unitId: string, base: string, state: GSDState,
|
|
1749
1751
|
): void {
|
|
1750
|
-
const
|
|
1751
|
-
const mid = parts[0]!;
|
|
1752
|
+
const { milestone: mid } = parseUnitId(unitId);
|
|
1752
1753
|
|
|
1753
1754
|
const mDir = resolveMilestonePath(base, mid);
|
|
1754
1755
|
if (!mDir) {
|
|
@@ -1756,8 +1757,8 @@ function ensurePreconditions(
|
|
|
1756
1757
|
mkdirSync(join(newDir, "slices"), { recursive: true });
|
|
1757
1758
|
}
|
|
1758
1759
|
|
|
1759
|
-
|
|
1760
|
-
|
|
1760
|
+
const sid = parseUnitId(unitId).slice;
|
|
1761
|
+
if (sid) {
|
|
1761
1762
|
|
|
1762
1763
|
const mDirResolved = resolveMilestonePath(base, mid);
|
|
1763
1764
|
if (mDirResolved) {
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import type { ExtensionCommandContext } from "@gsd/pi-coding-agent";
|
|
8
|
+
import { getErrorMessage } from "./error-utils.js";
|
|
8
9
|
|
|
9
10
|
export interface InspectData {
|
|
10
11
|
schemaVersion: number | null;
|
|
@@ -84,7 +85,7 @@ export async function handleInspect(ctx: ExtensionCommandContext): Promise<void>
|
|
|
84
85
|
|
|
85
86
|
ctx.ui.notify(formatInspectOutput(data), "info");
|
|
86
87
|
} catch (err) {
|
|
87
|
-
process.stderr.write(`gsd-db: /gsd inspect failed: ${
|
|
88
|
+
process.stderr.write(`gsd-db: /gsd inspect failed: ${getErrorMessage(err)}\n`);
|
|
88
89
|
ctx.ui.notify("Failed to inspect GSD database. Check stderr for details.", "error");
|
|
89
90
|
}
|
|
90
91
|
}
|
|
@@ -21,6 +21,7 @@ import { loadPrompt } from "./prompt-loader.js";
|
|
|
21
21
|
import { gsdRoot } from "./paths.js";
|
|
22
22
|
import { createGitService, runGit } from "./git-service.js";
|
|
23
23
|
import { isAutoActive, isAutoPaused } from "./auto.js";
|
|
24
|
+
import { getErrorMessage } from "./error-utils.js";
|
|
24
25
|
|
|
25
26
|
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
26
27
|
|
|
@@ -439,7 +440,7 @@ export async function handleStart(
|
|
|
439
440
|
branchCreated = true;
|
|
440
441
|
}
|
|
441
442
|
} catch (err) {
|
|
442
|
-
const message =
|
|
443
|
+
const message = getErrorMessage(err);
|
|
443
444
|
ctx.ui.notify(
|
|
444
445
|
`Could not create branch ${branchName}: ${message}. Working on current branch.`,
|
|
445
446
|
"warning",
|
|
@@ -6,6 +6,7 @@ import { existsSync, readFileSync } from "node:fs";
|
|
|
6
6
|
import { join } from "node:path";
|
|
7
7
|
import { gsdRoot } from "./paths.js";
|
|
8
8
|
import { getAdaptiveTierAdjustment } from "./routing-history.js";
|
|
9
|
+
import { parseUnitId } from "./unit-id.js";
|
|
9
10
|
|
|
10
11
|
// ─── Types ───────────────────────────────────────────────────────────────────
|
|
11
12
|
|
|
@@ -180,15 +181,14 @@ function analyzePlanComplexity(
|
|
|
180
181
|
basePath: string,
|
|
181
182
|
): TaskAnalysis | null {
|
|
182
183
|
// Check if this is a milestone-level plan (more complex) vs single slice
|
|
183
|
-
const
|
|
184
|
-
if (
|
|
184
|
+
const { milestone: mid, slice: sid } = parseUnitId(unitId);
|
|
185
|
+
if (!sid) {
|
|
185
186
|
// Milestone-level planning is always at least standard
|
|
186
187
|
return { tier: "standard", reason: "milestone-level planning" };
|
|
187
188
|
}
|
|
188
189
|
|
|
189
190
|
// For slice planning, try to read the context/research to gauge complexity
|
|
190
191
|
// If research exists and is large, bump to heavy
|
|
191
|
-
const [mid, sid] = parts;
|
|
192
192
|
const researchPath = join(gsdRoot(basePath), mid, "slices", sid, "RESEARCH.md");
|
|
193
193
|
try {
|
|
194
194
|
if (existsSync(researchPath)) {
|
|
@@ -210,10 +210,8 @@ function analyzePlanComplexity(
|
|
|
210
210
|
*/
|
|
211
211
|
function extractTaskMetadata(unitId: string, basePath: string): TaskMetadata {
|
|
212
212
|
const meta: TaskMetadata = {};
|
|
213
|
-
const
|
|
214
|
-
if (
|
|
215
|
-
|
|
216
|
-
const [mid, sid, tid] = parts;
|
|
213
|
+
const { milestone: mid, slice: sid, task: tid } = parseUnitId(unitId);
|
|
214
|
+
if (!mid || !sid || !tid) return meta;
|
|
217
215
|
const taskPlanPath = join(gsdRoot(basePath), mid, "slices", sid, "tasks", `${tid}-PLAN.md`);
|
|
218
216
|
|
|
219
217
|
try {
|
|
@@ -98,11 +98,24 @@ export function isLockProcessAlive(lock: LockData): boolean {
|
|
|
98
98
|
|
|
99
99
|
/** Format crash info for display or injection into a prompt. */
|
|
100
100
|
export function formatCrashInfo(lock: LockData): string {
|
|
101
|
-
|
|
101
|
+
const lines = [
|
|
102
102
|
`Previous auto-mode session was interrupted.`,
|
|
103
103
|
` Was executing: ${lock.unitType} (${lock.unitId})`,
|
|
104
104
|
` Started at: ${lock.unitStartedAt}`,
|
|
105
105
|
` Units completed before crash: ${lock.completedUnits}`,
|
|
106
106
|
` PID: ${lock.pid}`,
|
|
107
|
-
]
|
|
107
|
+
];
|
|
108
|
+
|
|
109
|
+
// Add recovery guidance based on what was happening when it crashed
|
|
110
|
+
if (lock.unitType === "starting" && lock.unitId === "bootstrap" && lock.completedUnits === 0) {
|
|
111
|
+
lines.push(`No work was lost. Run /gsd auto to restart.`);
|
|
112
|
+
} else if (lock.unitType.includes("research") || lock.unitType.includes("plan")) {
|
|
113
|
+
lines.push(`The ${lock.unitType} unit may be incomplete. Run /gsd auto to re-run it.`);
|
|
114
|
+
} else if (lock.unitType.includes("execute")) {
|
|
115
|
+
lines.push(`Task execution was interrupted. Run /gsd auto to resume — completed work is preserved.`);
|
|
116
|
+
} else if (lock.unitType.includes("complete")) {
|
|
117
|
+
lines.push(`Slice/milestone completion was interrupted. Run /gsd auto to finish.`);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return lines.join("\n");
|
|
108
121
|
}
|
|
@@ -5,6 +5,7 @@ import { readdirSync } from "node:fs";
|
|
|
5
5
|
import { resolveMilestoneFile, milestonesDir } from "./paths.js";
|
|
6
6
|
import { parseRoadmapSlices } from "./roadmap-slices.js";
|
|
7
7
|
import { findMilestoneIds } from "./guided-flow.js";
|
|
8
|
+
import { parseUnitId } from "./unit-id.js";
|
|
8
9
|
|
|
9
10
|
const SLICE_DISPATCH_TYPES = new Set([
|
|
10
11
|
"research-slice",
|
|
@@ -39,7 +40,7 @@ function readRoadmapFromDisk(base: string, milestoneId: string): string | null {
|
|
|
39
40
|
export function getPriorSliceCompletionBlocker(base: string, _mainBranch: string, unitType: string, unitId: string): string | null {
|
|
40
41
|
if (!SLICE_DISPATCH_TYPES.has(unitType)) return null;
|
|
41
42
|
|
|
42
|
-
const
|
|
43
|
+
const { milestone: targetMid, slice: targetSid } = parseUnitId(unitId);
|
|
43
44
|
if (!targetMid || !targetSid) return null;
|
|
44
45
|
|
|
45
46
|
// Use findMilestoneIds to respect custom queue order.
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
import type { UnitMetrics } from "./metrics.js";
|
|
13
13
|
import { gsdRoot } from "./paths.js";
|
|
14
14
|
import { formatDuration, fileLink } from "../shared/mod.js";
|
|
15
|
+
import { getErrorMessage } from "./error-utils.js";
|
|
15
16
|
|
|
16
17
|
/**
|
|
17
18
|
* Open a file in the user's default browser.
|
|
@@ -226,7 +227,7 @@ export async function handleExport(args: string, ctx: ExtensionCommandContext, b
|
|
|
226
227
|
}
|
|
227
228
|
} catch (err) {
|
|
228
229
|
ctx.ui.notify(
|
|
229
|
-
`HTML export failed: ${
|
|
230
|
+
`HTML export failed: ${getErrorMessage(err)}`,
|
|
230
231
|
"error",
|
|
231
232
|
);
|
|
232
233
|
}
|
|
@@ -33,6 +33,7 @@ import {
|
|
|
33
33
|
nativeAddPaths,
|
|
34
34
|
} from "./native-git-bridge.js";
|
|
35
35
|
import { GSDError, GSD_MERGE_CONFLICT, GSD_GIT_ERROR } from "./errors.js";
|
|
36
|
+
import { getErrorMessage } from "./error-utils.js";
|
|
36
37
|
|
|
37
38
|
// ─── Types ─────────────────────────────────────────────────────────────────
|
|
38
39
|
|
|
@@ -281,7 +282,7 @@ export function runGit(basePath: string, args: string[], options: { allowFailure
|
|
|
281
282
|
}).trim();
|
|
282
283
|
} catch (error) {
|
|
283
284
|
if (options.allowFailure) return "";
|
|
284
|
-
const message =
|
|
285
|
+
const message = getErrorMessage(error);
|
|
285
286
|
throw new GSDError(GSD_GIT_ERROR, `git ${args.join(" ")} failed in ${basePath}: ${filterGitSvnNoise(message)}`);
|
|
286
287
|
}
|
|
287
288
|
}
|
|
@@ -533,7 +534,7 @@ export class GitServiceImpl {
|
|
|
533
534
|
execSync(command, { cwd: this.basePath, stdio: "pipe", encoding: "utf-8" });
|
|
534
535
|
return { passed: true, skipped: false, command };
|
|
535
536
|
} catch (err) {
|
|
536
|
-
const msg =
|
|
537
|
+
const msg = getErrorMessage(err);
|
|
537
538
|
return { passed: false, skipped: false, command, error: msg };
|
|
538
539
|
}
|
|
539
540
|
}
|
|
@@ -44,6 +44,7 @@ export {
|
|
|
44
44
|
showQueue, handleQueueReorder, showQueueAdd,
|
|
45
45
|
buildExistingMilestonesContext,
|
|
46
46
|
} from "./guided-flow-queue.js";
|
|
47
|
+
import { getErrorMessage } from "./error-utils.js";
|
|
47
48
|
|
|
48
49
|
// ─── Commit Instruction Helpers ──────────────────────────────────────────────
|
|
49
50
|
|
|
@@ -158,9 +159,9 @@ export function checkAutoStartAfterDiscuss(): boolean {
|
|
|
158
159
|
|
|
159
160
|
pendingAutoStart = null;
|
|
160
161
|
startAuto(ctx, pi, basePath, false, { step }).catch((err) => {
|
|
161
|
-
ctx.ui.notify(`Auto-start failed: ${
|
|
162
|
+
ctx.ui.notify(`Auto-start failed: ${getErrorMessage(err)}`, "error");
|
|
162
163
|
if (process.env.GSD_DEBUG) console.error('[gsd] auto start error:', err);
|
|
163
|
-
debugLog("auto-start-failed", { error:
|
|
164
|
+
debugLog("auto-start-failed", { error: getErrorMessage(err) });
|
|
164
165
|
});
|
|
165
166
|
return true;
|
|
166
167
|
}
|
|
@@ -64,6 +64,7 @@ import { pauseAutoForProviderError, classifyProviderError } from "./provider-err
|
|
|
64
64
|
import { toPosixPath } from "../shared/mod.js";
|
|
65
65
|
import { isParallelActive, shutdownParallel } from "./parallel-orchestrator.js";
|
|
66
66
|
import { DEFAULT_BASH_TIMEOUT_SECS } from "./constants.js";
|
|
67
|
+
import { getErrorMessage } from "./error-utils.js";
|
|
67
68
|
|
|
68
69
|
/**
|
|
69
70
|
* Ensure the GSD database is available, auto-initializing if needed.
|
|
@@ -374,7 +375,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
374
375
|
details: { operation: "save_decision", id },
|
|
375
376
|
};
|
|
376
377
|
} catch (err) {
|
|
377
|
-
const msg =
|
|
378
|
+
const msg = getErrorMessage(err);
|
|
378
379
|
process.stderr.write(`gsd-db: gsd_save_decision tool failed: ${msg}\n`);
|
|
379
380
|
return {
|
|
380
381
|
content: [{ type: "text" as const, text: `Error saving decision: ${msg}` }],
|
|
@@ -445,7 +446,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
445
446
|
details: { operation: "update_requirement", id: params.id },
|
|
446
447
|
};
|
|
447
448
|
} catch (err) {
|
|
448
|
-
const msg =
|
|
449
|
+
const msg = getErrorMessage(err);
|
|
449
450
|
process.stderr.write(`gsd-db: gsd_update_requirement tool failed: ${msg}\n`);
|
|
450
451
|
return {
|
|
451
452
|
content: [{ type: "text" as const, text: `Error updating requirement: ${msg}` }],
|
|
@@ -525,7 +526,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
525
526
|
details: { operation: "save_summary", path: relativePath, artifact_type: params.artifact_type },
|
|
526
527
|
};
|
|
527
528
|
} catch (err) {
|
|
528
|
-
const msg =
|
|
529
|
+
const msg = getErrorMessage(err);
|
|
529
530
|
process.stderr.write(`gsd-db: gsd_save_summary tool failed: ${msg}\n`);
|
|
530
531
|
return {
|
|
531
532
|
content: [{ type: "text" as const, text: `Error saving artifact: ${msg}` }],
|
|
@@ -574,7 +575,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
574
575
|
details: { operation: "generate_milestone_id", id: newId, existingCount: existingIds.length, reservedCount: reservedMilestoneIds.size, uniqueEnabled },
|
|
575
576
|
};
|
|
576
577
|
} catch (err) {
|
|
577
|
-
const msg =
|
|
578
|
+
const msg = getErrorMessage(err);
|
|
578
579
|
return {
|
|
579
580
|
content: [{ type: "text" as const, text: `Error generating milestone ID: ${msg}` }],
|
|
580
581
|
isError: true,
|
|
@@ -795,6 +796,12 @@ export default function (pi: ExtensionAPI) {
|
|
|
795
796
|
|
|
796
797
|
// ── agent_end: auto-mode advancement or auto-start after discuss ───────────
|
|
797
798
|
pi.on("agent_end", async (event, ctx: ExtensionContext) => {
|
|
799
|
+
// Clean up quick-task branch if one just completed (#1269)
|
|
800
|
+
try {
|
|
801
|
+
const { cleanupQuickBranch } = await import("./quick.js");
|
|
802
|
+
cleanupQuickBranch();
|
|
803
|
+
} catch { /* non-fatal */ }
|
|
804
|
+
|
|
798
805
|
// If discuss phase just finished, start auto-mode
|
|
799
806
|
if (checkAutoStartAfterDiscuss()) {
|
|
800
807
|
depthVerifiedMilestones.clear();
|
|
@@ -987,7 +994,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
987
994
|
} catch (err) {
|
|
988
995
|
// Safety net: if handleAgentEnd throws despite its internal try-catch,
|
|
989
996
|
// ensure auto-mode stops gracefully instead of silently stalling (#381).
|
|
990
|
-
const message =
|
|
997
|
+
const message = getErrorMessage(err);
|
|
991
998
|
ctx.ui.notify(
|
|
992
999
|
`Auto-mode error in agent_end handler: ${message}. Stopping auto-mode.`,
|
|
993
1000
|
"error",
|
|
@@ -16,6 +16,7 @@ import { getEnvApiKey } from "@gsd/pi-ai";
|
|
|
16
16
|
import { existsSync, statSync, chmodSync } from "node:fs";
|
|
17
17
|
import { join, dirname } from "node:path";
|
|
18
18
|
import { mkdirSync } from "node:fs";
|
|
19
|
+
import { getErrorMessage } from "./error-utils.js";
|
|
19
20
|
|
|
20
21
|
// ─── Provider Registry ─────────────────────────────────────────────────────────
|
|
21
22
|
|
|
@@ -552,7 +553,7 @@ export async function testProviderKey(
|
|
|
552
553
|
return { provider, status: "error", message: `HTTP ${res.status}`, latencyMs };
|
|
553
554
|
} catch (err) {
|
|
554
555
|
const latencyMs = Date.now() - start;
|
|
555
|
-
const msg =
|
|
556
|
+
const msg = getErrorMessage(err);
|
|
556
557
|
if (msg.includes("timeout") || msg.includes("AbortError")) {
|
|
557
558
|
return { provider, status: "error", message: "timeout (15s)", latencyMs };
|
|
558
559
|
}
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
|
|
17
17
|
import * as fs from 'node:fs';
|
|
18
18
|
import * as path from 'node:path';
|
|
19
|
+
import { getErrorMessage } from "./error-utils.js";
|
|
19
20
|
|
|
20
21
|
// ============================================================================
|
|
21
22
|
// Type Definitions
|
|
@@ -194,7 +195,7 @@ export function parseMarketplaceJson(repoRoot: string):
|
|
|
194
195
|
} catch (err) {
|
|
195
196
|
return {
|
|
196
197
|
success: false,
|
|
197
|
-
error: `Failed to read marketplace.json: ${
|
|
198
|
+
error: `Failed to read marketplace.json: ${getErrorMessage(err)}`
|
|
198
199
|
};
|
|
199
200
|
}
|
|
200
201
|
|
|
@@ -204,7 +205,7 @@ export function parseMarketplaceJson(repoRoot: string):
|
|
|
204
205
|
} catch (err) {
|
|
205
206
|
return {
|
|
206
207
|
success: false,
|
|
207
|
-
error: `Failed to parse marketplace.json: ${
|
|
208
|
+
error: `Failed to parse marketplace.json: ${getErrorMessage(err)}`
|
|
208
209
|
};
|
|
209
210
|
}
|
|
210
211
|
|
|
@@ -293,7 +294,7 @@ export function inspectPlugin(
|
|
|
293
294
|
}
|
|
294
295
|
} catch (err) {
|
|
295
296
|
// Fall back to marketplace inline or derived
|
|
296
|
-
result.error = `Failed to parse plugin.json: ${
|
|
297
|
+
result.error = `Failed to parse plugin.json: ${getErrorMessage(err)}`;
|
|
297
298
|
}
|
|
298
299
|
}
|
|
299
300
|
|
|
@@ -18,6 +18,7 @@ import type { ExtensionContext } from "@gsd/pi-coding-agent";
|
|
|
18
18
|
import { gsdRoot } from "./paths.js";
|
|
19
19
|
import { getAndClearSkills } from "./skill-telemetry.js";
|
|
20
20
|
import { loadJsonFile, loadJsonFileOrNull, saveJsonFile } from "./json-persistence.js";
|
|
21
|
+
import { parseUnitId } from "./unit-id.js";
|
|
21
22
|
|
|
22
23
|
// Re-export from shared — canonical implementation lives in format-utils.
|
|
23
24
|
export { formatTokenCount } from "../shared/mod.js";
|
|
@@ -290,9 +291,8 @@ export function aggregateByPhase(units: UnitMetrics[]): PhaseAggregate[] {
|
|
|
290
291
|
export function aggregateBySlice(units: UnitMetrics[]): SliceAggregate[] {
|
|
291
292
|
const map = new Map<string, SliceAggregate>();
|
|
292
293
|
for (const u of units) {
|
|
293
|
-
const
|
|
294
|
-
|
|
295
|
-
const sliceId = parts.length >= 2 ? `${parts[0]}/${parts[1]}` : parts[0];
|
|
294
|
+
const { milestone, slice } = parseUnitId(u.id);
|
|
295
|
+
const sliceId = slice ? `${milestone}/${slice}` : milestone;
|
|
296
296
|
let agg = map.get(sliceId);
|
|
297
297
|
if (!agg) {
|
|
298
298
|
agg = { sliceId, units: 0, tokens: emptyTokens(), cost: 0, duration: 0 };
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
import { existsSync, lstatSync, mkdirSync, readdirSync, renameSync, cpSync, rmSync, symlinkSync } from "node:fs";
|
|
10
10
|
import { join } from "node:path";
|
|
11
11
|
import { externalGsdRoot } from "./repo-identity.js";
|
|
12
|
+
import { getErrorMessage } from "./error-utils.js";
|
|
12
13
|
|
|
13
14
|
export interface MigrationResult {
|
|
14
15
|
migrated: boolean;
|
|
@@ -47,7 +48,7 @@ export function migrateToExternalState(basePath: string): MigrationResult {
|
|
|
47
48
|
return { migrated: false, error: ".gsd exists but is not a directory or symlink" };
|
|
48
49
|
}
|
|
49
50
|
} catch (err) {
|
|
50
|
-
return { migrated: false, error: `Cannot stat .gsd: ${
|
|
51
|
+
return { migrated: false, error: `Cannot stat .gsd: ${getErrorMessage(err)}` };
|
|
51
52
|
}
|
|
52
53
|
|
|
53
54
|
const externalPath = externalGsdRoot(basePath);
|
|
@@ -57,8 +58,24 @@ export function migrateToExternalState(basePath: string): MigrationResult {
|
|
|
57
58
|
// mkdir -p the external dir
|
|
58
59
|
mkdirSync(externalPath, { recursive: true });
|
|
59
60
|
|
|
60
|
-
// Rename .gsd -> .gsd.migrating (atomic lock)
|
|
61
|
-
|
|
61
|
+
// Rename .gsd -> .gsd.migrating (atomic lock).
|
|
62
|
+
// On Windows, NTFS may reject rename with EPERM if file descriptors are
|
|
63
|
+
// open (VS Code watchers, antivirus on-access scan). Fall back to
|
|
64
|
+
// copy+delete (#1292).
|
|
65
|
+
try {
|
|
66
|
+
renameSync(localGsd, migratingPath);
|
|
67
|
+
} catch (renameErr: any) {
|
|
68
|
+
if (renameErr?.code === "EPERM" || renameErr?.code === "EBUSY") {
|
|
69
|
+
try {
|
|
70
|
+
cpSync(localGsd, migratingPath, { recursive: true, force: true });
|
|
71
|
+
rmSync(localGsd, { recursive: true, force: true });
|
|
72
|
+
} catch (copyErr) {
|
|
73
|
+
return { migrated: false, error: `Migration rename/copy failed: ${copyErr instanceof Error ? copyErr.message : String(copyErr)}` };
|
|
74
|
+
}
|
|
75
|
+
} else {
|
|
76
|
+
throw renameErr;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
62
79
|
|
|
63
80
|
// Copy contents to external dir, skipping worktrees/
|
|
64
81
|
const entries = readdirSync(migratingPath, { withFileTypes: true });
|
|
@@ -98,7 +115,7 @@ export function migrateToExternalState(basePath: string): MigrationResult {
|
|
|
98
115
|
|
|
99
116
|
return {
|
|
100
117
|
migrated: false,
|
|
101
|
-
error: `Migration failed: ${
|
|
118
|
+
error: `Migration failed: ${getErrorMessage(err)}`,
|
|
102
119
|
};
|
|
103
120
|
}
|
|
104
121
|
}
|