refactor: extract getErrorMessage() helper to eliminate 65 inline duplicates (#1280)
Consolidate the repeated `err instanceof Error ? err.message : String(err)` pattern into a single `getErrorMessage(err)` utility. Reduces visual noise in catch blocks across 20 files in the GSD extension. Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
922826ba8a
commit
2a2056bcd7
21 changed files with 91 additions and 65 deletions
|
|
@ -63,6 +63,7 @@ import { debugLog, enableDebug, isDebugEnabled, getDebugLogPath } from "./debug-
|
|||
import type { AutoSession } from "./auto/session.js";
|
||||
import { existsSync, mkdirSync, readdirSync, statSync, unlinkSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
import { getErrorMessage } from "./error-utils.js";
|
||||
|
||||
export interface BootstrapDeps {
|
||||
shouldUseWorktreeIsolation: () => boolean;
|
||||
|
|
@ -201,11 +202,11 @@ export async function bootstrapAutoSession(
|
|||
if (!midMatch) continue;
|
||||
const mid = midMatch[1];
|
||||
if (resolveMilestoneFile(base, mid, "SUMMARY")) {
|
||||
try { unlinkSync(join(runtimeUnitsDir, file)); } catch (e) { debugLog("stale-unit-cleanup-failed", { file, error: e instanceof Error ? e.message : String(e) }); }
|
||||
try { unlinkSync(join(runtimeUnitsDir, file)); } catch (e) { debugLog("stale-unit-cleanup-failed", { file, error: getErrorMessage(e) }); }
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) { debugLog("stale-unit-dir-cleanup-failed", { error: e instanceof Error ? e.message : String(e) }); }
|
||||
} catch (e) { debugLog("stale-unit-dir-cleanup-failed", { error: getErrorMessage(e) }); }
|
||||
|
||||
let state = await deriveState(base);
|
||||
|
||||
|
|
@ -343,7 +344,7 @@ export async function bootstrapAutoSession(
|
|||
registerSigtermHandler(s.originalBasePath);
|
||||
} catch (err) {
|
||||
ctx.ui.notify(
|
||||
`Auto-worktree setup failed: ${err instanceof Error ? err.message : String(err)}. Continuing in project root.`,
|
||||
`Auto-worktree setup failed: ${getErrorMessage(err)}. Continuing in project root.`,
|
||||
"warning",
|
||||
);
|
||||
}
|
||||
|
|
@ -435,7 +436,7 @@ export async function bootstrapAutoSession(
|
|||
}
|
||||
} catch (err) {
|
||||
ctx.ui.notify(
|
||||
`Secrets check error: ${err instanceof Error ? err.message : String(err)}. Continuing without secrets.`,
|
||||
`Secrets check error: ${getErrorMessage(err)}. Continuing without secrets.`,
|
||||
"warning",
|
||||
);
|
||||
}
|
||||
|
|
@ -453,7 +454,7 @@ export async function bootstrapAutoSession(
|
|||
ctx.ui.notify("Removed stale .git/index.lock from prior crash.", "info");
|
||||
}
|
||||
}
|
||||
} catch (e) { debugLog("git-lock-cleanup-failed", { error: e instanceof Error ? e.message : String(e) }); }
|
||||
} catch (e) { debugLog("git-lock-cleanup-failed", { error: getErrorMessage(e) }); }
|
||||
|
||||
// Pre-flight: validate milestone queue
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import { closeoutUnit, type CloseoutOptions } from "./auto-unit-closeout.js";
|
|||
import { saveActivityLog } from "./activity-log.js";
|
||||
import { recoverTimedOutUnit, type RecoveryContext } from "./auto-timeout-recovery.js";
|
||||
import type { AutoSession } from "./auto/session.js";
|
||||
import { getErrorMessage } from "./error-utils.js";
|
||||
|
||||
export interface SupervisionContext {
|
||||
s: AutoSession;
|
||||
|
|
@ -127,7 +128,7 @@ export function startUnitSupervision(sctx: SupervisionContext): void {
|
|||
);
|
||||
await pauseAuto(ctx, pi);
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : String(err);
|
||||
const message = getErrorMessage(err);
|
||||
console.error(`[idle-watchdog] Unhandled error: ${message}`);
|
||||
try {
|
||||
ctx.ui.notify(`Idle watchdog error: ${message}`, "warning");
|
||||
|
|
@ -159,7 +160,7 @@ export function startUnitSupervision(sctx: SupervisionContext): void {
|
|||
);
|
||||
await pauseAuto(ctx, pi);
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : String(err);
|
||||
const message = getErrorMessage(err);
|
||||
console.error(`[hard-timeout] Unhandled error: ${message}`);
|
||||
try {
|
||||
ctx.ui.notify(`Hard timeout error: ${message}`, "warning");
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import { writeVerificationJSON } from "./verification-evidence.js";
|
|||
import { removePersistedKey } from "./auto-recovery.js";
|
||||
import type { AutoSession, PendingVerificationRetry } from "./auto/session.js";
|
||||
import { join } from "node:path";
|
||||
import { getErrorMessage } from "./error-utils.js";
|
||||
|
||||
export interface VerificationContext {
|
||||
s: AutoSession;
|
||||
|
|
@ -204,7 +205,7 @@ export async function runPostUnitVerification(
|
|||
try {
|
||||
await dispatchNextUnit(ctx, pi);
|
||||
} catch (retryDispatchErr) {
|
||||
const msg = retryDispatchErr instanceof Error ? retryDispatchErr.message : String(retryDispatchErr);
|
||||
const msg = getErrorMessage(retryDispatchErr);
|
||||
ctx.ui.notify(`Verification retry dispatch error: ${msg}`, "error");
|
||||
startDispatchGapWatchdog(ctx, pi);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ import {
|
|||
nativeBranchDelete,
|
||||
nativeBranchExists,
|
||||
} from "./native-git-bridge.js";
|
||||
import { getErrorMessage } from "./error-utils.js";
|
||||
|
||||
// ─── Module State ──────────────────────────────────────────────────────────
|
||||
|
||||
|
|
@ -81,7 +82,7 @@ export function runWorktreePostCreateHook(sourceDir: string, worktreeDir: string
|
|||
});
|
||||
return null;
|
||||
} catch (err) {
|
||||
const msg = err instanceof Error ? err.message : String(err);
|
||||
const msg = getErrorMessage(err);
|
||||
return `Worktree post-create hook failed: ${msg}`;
|
||||
}
|
||||
}
|
||||
|
|
@ -141,7 +142,7 @@ export function createAutoWorktree(basePath: string, milestoneId: string): strin
|
|||
// Don't store originalBase -- caller can retry or clean up.
|
||||
throw new GSDError(
|
||||
GSD_IO_ERROR,
|
||||
`Auto-worktree created at ${info.path} but chdir failed: ${err instanceof Error ? err.message : String(err)}`,
|
||||
`Auto-worktree created at ${info.path} but chdir failed: ${getErrorMessage(err)}`,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -168,7 +169,7 @@ export function teardownAutoWorktree(
|
|||
} catch (err) {
|
||||
throw new GSDError(
|
||||
GSD_IO_ERROR,
|
||||
`Failed to chdir back to ${originalBasePath} during teardown: ${err instanceof Error ? err.message : String(err)}`,
|
||||
`Failed to chdir back to ${originalBasePath} during teardown: ${getErrorMessage(err)}`,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -274,7 +275,7 @@ export function enterAutoWorktree(basePath: string, milestoneId: string): string
|
|||
} catch (err) {
|
||||
throw new GSDError(
|
||||
GSD_IO_ERROR,
|
||||
`Failed to enter auto-worktree at ${p}: ${err instanceof Error ? err.message : String(err)}`,
|
||||
`Failed to enter auto-worktree at ${p}: ${getErrorMessage(err)}`,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -189,6 +189,7 @@ import {
|
|||
NEW_SESSION_TIMEOUT_MS, DISPATCH_HANG_TIMEOUT_MS,
|
||||
} from "./auto/session.js";
|
||||
import type { CompletedUnit, CurrentUnit, UnitRouting, StartModel, PendingVerificationRetry } from "./auto/session.js";
|
||||
import { getErrorMessage } from "./error-utils.js";
|
||||
|
||||
// ── ENCAPSULATION INVARIANT ─────────────────────────────────────────────────
|
||||
// ALL mutable auto-mode state lives in the AutoSession class (auto/session.ts).
|
||||
|
|
@ -428,7 +429,7 @@ function startDispatchGapWatchdog(ctx: ExtensionContext, pi: ExtensionAPI): void
|
|||
try {
|
||||
await dispatchNextUnit(ctx, pi);
|
||||
} catch (retryErr) {
|
||||
const message = retryErr instanceof Error ? retryErr.message : String(retryErr);
|
||||
const message = getErrorMessage(retryErr);
|
||||
await stopAuto(ctx, pi, `Dispatch gap recovery failed: ${message}`);
|
||||
return;
|
||||
}
|
||||
|
|
@ -458,14 +459,14 @@ export async function stopAuto(ctx?: ExtensionContext, pi?: ExtensionAPI, reason
|
|||
// ── Auto-worktree: exit worktree and reset s.basePath on stop ──
|
||||
if (s.currentMilestoneId && isInAutoWorktree(s.basePath)) {
|
||||
try {
|
||||
try { autoCommitCurrentBranch(s.basePath, "stop", s.currentMilestoneId); } catch (e) { debugLog("stop-auto-commit-failed", { error: e instanceof Error ? e.message : String(e) }); }
|
||||
try { autoCommitCurrentBranch(s.basePath, "stop", s.currentMilestoneId); } catch (e) { debugLog("stop-auto-commit-failed", { error: getErrorMessage(e) }); }
|
||||
teardownAutoWorktree(s.originalBasePath, s.currentMilestoneId, { preserveBranch: true });
|
||||
s.basePath = s.originalBasePath;
|
||||
s.gitService = createGitService(s.basePath);
|
||||
ctx?.ui.notify("Exited auto-worktree (branch preserved for resume).", "info");
|
||||
} catch (err) {
|
||||
ctx?.ui.notify(
|
||||
`Auto-worktree teardown failed: ${err instanceof Error ? err.message : String(err)}`,
|
||||
`Auto-worktree teardown failed: ${getErrorMessage(err)}`,
|
||||
"warning",
|
||||
);
|
||||
}
|
||||
|
|
@ -476,7 +477,7 @@ export async function stopAuto(ctx?: ExtensionContext, pi?: ExtensionAPI, reason
|
|||
try {
|
||||
const { closeDatabase } = await import("./gsd-db.js");
|
||||
closeDatabase();
|
||||
} catch (e) { debugLog("db-close-failed", { error: e instanceof Error ? e.message : String(e) }); }
|
||||
} catch (e) { debugLog("db-close-failed", { error: getErrorMessage(e) }); }
|
||||
}
|
||||
|
||||
if (s.originalBasePath) {
|
||||
|
|
@ -496,7 +497,7 @@ export async function stopAuto(ctx?: ExtensionContext, pi?: ExtensionAPI, reason
|
|||
}
|
||||
|
||||
if (s.basePath) {
|
||||
try { await rebuildState(s.basePath); } catch (e) { debugLog("stop-rebuild-state-failed", { error: e instanceof Error ? e.message : String(e) }); }
|
||||
try { await rebuildState(s.basePath); } catch (e) { debugLog("stop-rebuild-state-failed", { error: getErrorMessage(e) }); }
|
||||
}
|
||||
|
||||
if (isDebugEnabled()) {
|
||||
|
|
@ -635,7 +636,7 @@ export async function startAuto(
|
|||
}
|
||||
} catch (err) {
|
||||
ctx.ui.notify(
|
||||
`Auto-worktree re-entry failed: ${err instanceof Error ? err.message : String(err)}. Continuing at current path.`,
|
||||
`Auto-worktree re-entry failed: ${getErrorMessage(err)}. Continuing at current path.`,
|
||||
"warning",
|
||||
);
|
||||
}
|
||||
|
|
@ -647,13 +648,13 @@ export async function startAuto(
|
|||
ctx.ui.setFooter(hideFooter);
|
||||
ctx.ui.notify(s.stepMode ? "Step-mode resumed." : "Auto-mode resumed.", "info");
|
||||
restoreHookState(s.basePath);
|
||||
try { await rebuildState(s.basePath); } catch (e) { debugLog("resume-rebuild-state-failed", { error: e instanceof Error ? e.message : String(e) }); }
|
||||
try { await rebuildState(s.basePath); } catch (e) { debugLog("resume-rebuild-state-failed", { error: getErrorMessage(e) }); }
|
||||
try {
|
||||
const report = await runGSDDoctor(s.basePath, { fix: true });
|
||||
if (report.fixesApplied.length > 0) {
|
||||
ctx.ui.notify(`Resume: applied ${report.fixesApplied.length} fix(es) to state.`, "info");
|
||||
}
|
||||
} catch (e) { debugLog("resume-doctor-failed", { error: e instanceof Error ? e.message : String(e) }); }
|
||||
} catch (e) { debugLog("resume-doctor-failed", { error: getErrorMessage(e) }); }
|
||||
await selfHealRuntimeRecords(s.basePath, ctx, s.completedKeySet);
|
||||
invalidateAllCaches();
|
||||
|
||||
|
|
@ -700,7 +701,7 @@ export async function startAuto(
|
|||
}
|
||||
} catch (err) {
|
||||
ctx.ui.notify(
|
||||
`Secrets check error: ${err instanceof Error ? err.message : String(err)}. Continuing without secrets.`,
|
||||
`Secrets check error: ${getErrorMessage(err)}. Continuing without secrets.`,
|
||||
"warning",
|
||||
);
|
||||
}
|
||||
|
|
@ -807,7 +808,7 @@ export async function handleAgentEnd(
|
|||
try {
|
||||
await dispatchNextUnit(ctx, pi);
|
||||
} catch (dispatchErr) {
|
||||
const message = dispatchErr instanceof Error ? dispatchErr.message : String(dispatchErr);
|
||||
const message = getErrorMessage(dispatchErr);
|
||||
ctx.ui.notify(
|
||||
`Dispatch error after unit completion: ${message}. Retrying in ${DISPATCH_GAP_TIMEOUT_MS / 1000}s.`,
|
||||
"error",
|
||||
|
|
@ -838,7 +839,7 @@ export async function handleAgentEnd(
|
|||
clearDispatchGapWatchdog();
|
||||
setImmediate(() => {
|
||||
handleAgentEnd(ctx, pi).catch((err) => {
|
||||
const msg = err instanceof Error ? err.message : String(err);
|
||||
const msg = getErrorMessage(err);
|
||||
ctx.ui.notify(`Deferred agent_end retry failed: ${msg}`, "error");
|
||||
pauseAuto(ctx, pi).catch(() => {});
|
||||
});
|
||||
|
|
@ -1086,7 +1087,7 @@ async function dispatchNextUnit(
|
|||
);
|
||||
} catch (err) {
|
||||
ctx.ui.notify(
|
||||
`Report generation failed: ${err instanceof Error ? err.message : String(err)}`,
|
||||
`Report generation failed: ${getErrorMessage(err)}`,
|
||||
"warning",
|
||||
);
|
||||
}
|
||||
|
|
@ -1102,7 +1103,7 @@ async function dispatchNextUnit(
|
|||
atomicWriteSync(file, JSON.stringify([]));
|
||||
}
|
||||
s.completedKeySet.clear();
|
||||
} catch (e) { debugLog("completed-keys-reset-failed", { error: e instanceof Error ? e.message : String(e) }); }
|
||||
} catch (e) { debugLog("completed-keys-reset-failed", { error: getErrorMessage(e) }); }
|
||||
|
||||
// ── Worktree lifecycle on milestone transition (#616) ──
|
||||
if (isInAutoWorktree(s.basePath) && s.originalBasePath && shouldUseWorktreeIsolation()) {
|
||||
|
|
@ -1121,7 +1122,7 @@ async function dispatchNextUnit(
|
|||
}
|
||||
} catch (err) {
|
||||
ctx.ui.notify(
|
||||
`Milestone merge failed during transition: ${err instanceof Error ? err.message : String(err)}`,
|
||||
`Milestone merge failed during transition: ${getErrorMessage(err)}`,
|
||||
"warning",
|
||||
);
|
||||
if (s.originalBasePath) {
|
||||
|
|
@ -1146,7 +1147,7 @@ async function dispatchNextUnit(
|
|||
ctx.ui.notify(`Created auto-worktree for ${mid} at ${wtPath}`, "info");
|
||||
} catch (err) {
|
||||
ctx.ui.notify(
|
||||
`Auto-worktree creation for ${mid} failed: ${err instanceof Error ? err.message : String(err)}. Continuing in project root.`,
|
||||
`Auto-worktree creation for ${mid} failed: ${getErrorMessage(err)}. Continuing in project root.`,
|
||||
"warning",
|
||||
);
|
||||
}
|
||||
|
|
@ -1190,7 +1191,7 @@ async function dispatchNextUnit(
|
|||
}
|
||||
} catch (err) {
|
||||
ctx.ui.notify(
|
||||
`Milestone merge failed: ${err instanceof Error ? err.message : String(err)}`,
|
||||
`Milestone merge failed: ${getErrorMessage(err)}`,
|
||||
"warning",
|
||||
);
|
||||
if (s.originalBasePath) {
|
||||
|
|
@ -1216,7 +1217,7 @@ async function dispatchNextUnit(
|
|||
}
|
||||
} catch (err) {
|
||||
ctx.ui.notify(
|
||||
`Milestone merge failed (branch mode): ${err instanceof Error ? err.message : String(err)}`,
|
||||
`Milestone merge failed (branch mode): ${getErrorMessage(err)}`,
|
||||
"warning",
|
||||
);
|
||||
}
|
||||
|
|
@ -1276,7 +1277,7 @@ async function dispatchNextUnit(
|
|||
atomicWriteSync(file, JSON.stringify([]));
|
||||
}
|
||||
s.completedKeySet.clear();
|
||||
} catch (e) { debugLog("completed-keys-reset-failed", { error: e instanceof Error ? e.message : String(e) }); }
|
||||
} catch (e) { debugLog("completed-keys-reset-failed", { error: getErrorMessage(e) }); }
|
||||
// ── Milestone merge ──
|
||||
if (s.currentMilestoneId && isInAutoWorktree(s.basePath) && s.originalBasePath) {
|
||||
try {
|
||||
|
|
@ -1292,7 +1293,7 @@ async function dispatchNextUnit(
|
|||
);
|
||||
} catch (err) {
|
||||
ctx.ui.notify(
|
||||
`Milestone merge failed: ${err instanceof Error ? err.message : String(err)}`,
|
||||
`Milestone merge failed: ${getErrorMessage(err)}`,
|
||||
"warning",
|
||||
);
|
||||
if (s.originalBasePath) {
|
||||
|
|
@ -1318,7 +1319,7 @@ async function dispatchNextUnit(
|
|||
}
|
||||
} catch (err) {
|
||||
ctx.ui.notify(
|
||||
`Milestone merge failed (branch mode): ${err instanceof Error ? err.message : String(err)}`,
|
||||
`Milestone merge failed (branch mode): ${getErrorMessage(err)}`,
|
||||
"warning",
|
||||
);
|
||||
}
|
||||
|
|
@ -1417,7 +1418,7 @@ async function dispatchNextUnit(
|
|||
}
|
||||
} catch (err) {
|
||||
ctx.ui.notify(
|
||||
`Secrets collection error: ${err instanceof Error ? err.message : String(err)}. Continuing with next task.`,
|
||||
`Secrets collection error: ${getErrorMessage(err)}. Continuing with next task.`,
|
||||
"warning",
|
||||
);
|
||||
}
|
||||
|
|
@ -1628,7 +1629,7 @@ async function dispatchNextUnit(
|
|||
);
|
||||
result = await Promise.race([sessionPromise, timeoutPromise]);
|
||||
} catch (sessionErr) {
|
||||
const msg = sessionErr instanceof Error ? sessionErr.message : String(sessionErr);
|
||||
const msg = getErrorMessage(sessionErr);
|
||||
ctx.ui.notify(`Session creation failed: ${msg}. Retrying via watchdog.`, "error");
|
||||
throw new Error(`newSession() failed: ${msg}`);
|
||||
}
|
||||
|
|
@ -1704,7 +1705,7 @@ async function dispatchNextUnit(
|
|||
const { reorderForCaching } = await import("./prompt-ordering.js");
|
||||
finalPrompt = reorderForCaching(finalPrompt);
|
||||
} catch (reorderErr) {
|
||||
const msg = reorderErr instanceof Error ? reorderErr.message : String(reorderErr);
|
||||
const msg = getErrorMessage(reorderErr);
|
||||
process.stderr.write(`[gsd] prompt reorder failed (non-fatal): ${msg}\n`);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
*/
|
||||
|
||||
import type { ExtensionCommandContext } from "@gsd/pi-coding-agent";
|
||||
import { getErrorMessage } from "./error-utils.js";
|
||||
|
||||
export interface InspectData {
|
||||
schemaVersion: number | null;
|
||||
|
|
@ -84,7 +85,7 @@ export async function handleInspect(ctx: ExtensionCommandContext): Promise<void>
|
|||
|
||||
ctx.ui.notify(formatInspectOutput(data), "info");
|
||||
} catch (err) {
|
||||
process.stderr.write(`gsd-db: /gsd inspect failed: ${err instanceof Error ? err.message : String(err)}\n`);
|
||||
process.stderr.write(`gsd-db: /gsd inspect failed: ${getErrorMessage(err)}\n`);
|
||||
ctx.ui.notify("Failed to inspect GSD database. Check stderr for details.", "error");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import { loadPrompt } from "./prompt-loader.js";
|
|||
import { gsdRoot } from "./paths.js";
|
||||
import { createGitService, runGit } from "./git-service.js";
|
||||
import { isAutoActive, isAutoPaused } from "./auto.js";
|
||||
import { getErrorMessage } from "./error-utils.js";
|
||||
|
||||
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
||||
|
||||
|
|
@ -439,7 +440,7 @@ export async function handleStart(
|
|||
branchCreated = true;
|
||||
}
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : String(err);
|
||||
const message = getErrorMessage(err);
|
||||
ctx.ui.notify(
|
||||
`Could not create branch ${branchName}: ${message}. Working on current branch.`,
|
||||
"warning",
|
||||
|
|
|
|||
6
src/resources/extensions/gsd/error-utils.ts
Normal file
6
src/resources/extensions/gsd/error-utils.ts
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
/**
|
||||
* Extract a human-readable message from an unknown caught value.
|
||||
*/
|
||||
export function getErrorMessage(err: unknown): string {
|
||||
return err instanceof Error ? err.message : String(err);
|
||||
}
|
||||
|
|
@ -12,6 +12,7 @@ import {
|
|||
import type { UnitMetrics } from "./metrics.js";
|
||||
import { gsdRoot } from "./paths.js";
|
||||
import { formatDuration, fileLink } from "../shared/mod.js";
|
||||
import { getErrorMessage } from "./error-utils.js";
|
||||
|
||||
/**
|
||||
* Open a file in the user's default browser.
|
||||
|
|
@ -226,7 +227,7 @@ export async function handleExport(args: string, ctx: ExtensionCommandContext, b
|
|||
}
|
||||
} catch (err) {
|
||||
ctx.ui.notify(
|
||||
`HTML export failed: ${err instanceof Error ? err.message : String(err)}`,
|
||||
`HTML export failed: ${getErrorMessage(err)}`,
|
||||
"error",
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ import {
|
|||
nativeAddPaths,
|
||||
} from "./native-git-bridge.js";
|
||||
import { GSDError, GSD_MERGE_CONFLICT, GSD_GIT_ERROR } from "./errors.js";
|
||||
import { getErrorMessage } from "./error-utils.js";
|
||||
|
||||
// ─── Types ─────────────────────────────────────────────────────────────────
|
||||
|
||||
|
|
@ -281,7 +282,7 @@ export function runGit(basePath: string, args: string[], options: { allowFailure
|
|||
}).trim();
|
||||
} catch (error) {
|
||||
if (options.allowFailure) return "";
|
||||
const message = error instanceof Error ? error.message : String(error);
|
||||
const message = getErrorMessage(error);
|
||||
throw new GSDError(GSD_GIT_ERROR, `git ${args.join(" ")} failed in ${basePath}: ${filterGitSvnNoise(message)}`);
|
||||
}
|
||||
}
|
||||
|
|
@ -533,7 +534,7 @@ export class GitServiceImpl {
|
|||
execSync(command, { cwd: this.basePath, stdio: "pipe", encoding: "utf-8" });
|
||||
return { passed: true, skipped: false, command };
|
||||
} catch (err) {
|
||||
const msg = err instanceof Error ? err.message : String(err);
|
||||
const msg = getErrorMessage(err);
|
||||
return { passed: false, skipped: false, command, error: msg };
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ export {
|
|||
showQueue, handleQueueReorder, showQueueAdd,
|
||||
buildExistingMilestonesContext,
|
||||
} from "./guided-flow-queue.js";
|
||||
import { getErrorMessage } from "./error-utils.js";
|
||||
|
||||
// ─── Commit Instruction Helpers ──────────────────────────────────────────────
|
||||
|
||||
|
|
@ -158,9 +159,9 @@ export function checkAutoStartAfterDiscuss(): boolean {
|
|||
|
||||
pendingAutoStart = null;
|
||||
startAuto(ctx, pi, basePath, false, { step }).catch((err) => {
|
||||
ctx.ui.notify(`Auto-start failed: ${err instanceof Error ? err.message : String(err)}`, "error");
|
||||
ctx.ui.notify(`Auto-start failed: ${getErrorMessage(err)}`, "error");
|
||||
if (process.env.GSD_DEBUG) console.error('[gsd] auto start error:', err);
|
||||
debugLog("auto-start-failed", { error: err instanceof Error ? err.message : String(err) });
|
||||
debugLog("auto-start-failed", { error: getErrorMessage(err) });
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@ import { pauseAutoForProviderError, classifyProviderError } from "./provider-err
|
|||
import { toPosixPath } from "../shared/mod.js";
|
||||
import { isParallelActive, shutdownParallel } from "./parallel-orchestrator.js";
|
||||
import { DEFAULT_BASH_TIMEOUT_SECS } from "./constants.js";
|
||||
import { getErrorMessage } from "./error-utils.js";
|
||||
|
||||
/**
|
||||
* Ensure the GSD database is available, auto-initializing if needed.
|
||||
|
|
@ -374,7 +375,7 @@ export default function (pi: ExtensionAPI) {
|
|||
details: { operation: "save_decision", id },
|
||||
};
|
||||
} catch (err) {
|
||||
const msg = err instanceof Error ? err.message : String(err);
|
||||
const msg = getErrorMessage(err);
|
||||
process.stderr.write(`gsd-db: gsd_save_decision tool failed: ${msg}\n`);
|
||||
return {
|
||||
content: [{ type: "text" as const, text: `Error saving decision: ${msg}` }],
|
||||
|
|
@ -445,7 +446,7 @@ export default function (pi: ExtensionAPI) {
|
|||
details: { operation: "update_requirement", id: params.id },
|
||||
};
|
||||
} catch (err) {
|
||||
const msg = err instanceof Error ? err.message : String(err);
|
||||
const msg = getErrorMessage(err);
|
||||
process.stderr.write(`gsd-db: gsd_update_requirement tool failed: ${msg}\n`);
|
||||
return {
|
||||
content: [{ type: "text" as const, text: `Error updating requirement: ${msg}` }],
|
||||
|
|
@ -525,7 +526,7 @@ export default function (pi: ExtensionAPI) {
|
|||
details: { operation: "save_summary", path: relativePath, artifact_type: params.artifact_type },
|
||||
};
|
||||
} catch (err) {
|
||||
const msg = err instanceof Error ? err.message : String(err);
|
||||
const msg = getErrorMessage(err);
|
||||
process.stderr.write(`gsd-db: gsd_save_summary tool failed: ${msg}\n`);
|
||||
return {
|
||||
content: [{ type: "text" as const, text: `Error saving artifact: ${msg}` }],
|
||||
|
|
@ -574,7 +575,7 @@ export default function (pi: ExtensionAPI) {
|
|||
details: { operation: "generate_milestone_id", id: newId, existingCount: existingIds.length, reservedCount: reservedMilestoneIds.size, uniqueEnabled },
|
||||
};
|
||||
} catch (err) {
|
||||
const msg = err instanceof Error ? err.message : String(err);
|
||||
const msg = getErrorMessage(err);
|
||||
return {
|
||||
content: [{ type: "text" as const, text: `Error generating milestone ID: ${msg}` }],
|
||||
isError: true,
|
||||
|
|
@ -993,7 +994,7 @@ export default function (pi: ExtensionAPI) {
|
|||
} catch (err) {
|
||||
// Safety net: if handleAgentEnd throws despite its internal try-catch,
|
||||
// ensure auto-mode stops gracefully instead of silently stalling (#381).
|
||||
const message = err instanceof Error ? err.message : String(err);
|
||||
const message = getErrorMessage(err);
|
||||
ctx.ui.notify(
|
||||
`Auto-mode error in agent_end handler: ${message}. Stopping auto-mode.`,
|
||||
"error",
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import { getEnvApiKey } from "@gsd/pi-ai";
|
|||
import { existsSync, statSync, chmodSync } from "node:fs";
|
||||
import { join, dirname } from "node:path";
|
||||
import { mkdirSync } from "node:fs";
|
||||
import { getErrorMessage } from "./error-utils.js";
|
||||
|
||||
// ─── Provider Registry ─────────────────────────────────────────────────────────
|
||||
|
||||
|
|
@ -552,7 +553,7 @@ export async function testProviderKey(
|
|||
return { provider, status: "error", message: `HTTP ${res.status}`, latencyMs };
|
||||
} catch (err) {
|
||||
const latencyMs = Date.now() - start;
|
||||
const msg = err instanceof Error ? err.message : String(err);
|
||||
const msg = getErrorMessage(err);
|
||||
if (msg.includes("timeout") || msg.includes("AbortError")) {
|
||||
return { provider, status: "error", message: "timeout (15s)", latencyMs };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
import * as fs from 'node:fs';
|
||||
import * as path from 'node:path';
|
||||
import { getErrorMessage } from "./error-utils.js";
|
||||
|
||||
// ============================================================================
|
||||
// Type Definitions
|
||||
|
|
@ -194,7 +195,7 @@ export function parseMarketplaceJson(repoRoot: string):
|
|||
} catch (err) {
|
||||
return {
|
||||
success: false,
|
||||
error: `Failed to read marketplace.json: ${err instanceof Error ? err.message : String(err)}`
|
||||
error: `Failed to read marketplace.json: ${getErrorMessage(err)}`
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -204,7 +205,7 @@ export function parseMarketplaceJson(repoRoot: string):
|
|||
} catch (err) {
|
||||
return {
|
||||
success: false,
|
||||
error: `Failed to parse marketplace.json: ${err instanceof Error ? err.message : String(err)}`
|
||||
error: `Failed to parse marketplace.json: ${getErrorMessage(err)}`
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -293,7 +294,7 @@ export function inspectPlugin(
|
|||
}
|
||||
} catch (err) {
|
||||
// Fall back to marketplace inline or derived
|
||||
result.error = `Failed to parse plugin.json: ${err instanceof Error ? err.message : String(err)}`;
|
||||
result.error = `Failed to parse plugin.json: ${getErrorMessage(err)}`;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
import { existsSync, lstatSync, mkdirSync, readdirSync, renameSync, cpSync, rmSync, symlinkSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
import { externalGsdRoot } from "./repo-identity.js";
|
||||
import { getErrorMessage } from "./error-utils.js";
|
||||
|
||||
export interface MigrationResult {
|
||||
migrated: boolean;
|
||||
|
|
@ -47,7 +48,7 @@ export function migrateToExternalState(basePath: string): MigrationResult {
|
|||
return { migrated: false, error: ".gsd exists but is not a directory or symlink" };
|
||||
}
|
||||
} catch (err) {
|
||||
return { migrated: false, error: `Cannot stat .gsd: ${err instanceof Error ? err.message : String(err)}` };
|
||||
return { migrated: false, error: `Cannot stat .gsd: ${getErrorMessage(err)}` };
|
||||
}
|
||||
|
||||
const externalPath = externalGsdRoot(basePath);
|
||||
|
|
@ -114,7 +115,7 @@ export function migrateToExternalState(basePath: string): MigrationResult {
|
|||
|
||||
return {
|
||||
migrated: false,
|
||||
error: `Migration failed: ${err instanceof Error ? err.message : String(err)}`,
|
||||
error: `Migration failed: ${getErrorMessage(err)}`,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import { randomInt } from "node:crypto";
|
|||
import { readdirSync, existsSync } from "node:fs";
|
||||
import { milestonesDir } from "./paths.js";
|
||||
import { loadQueueOrder, sortByQueueOrder } from "./queue-order.js";
|
||||
import { getErrorMessage } from "./error-utils.js";
|
||||
|
||||
// ─── Regex ──────────────────────────────────────────────────────────────────
|
||||
|
||||
|
|
@ -88,7 +89,7 @@ export function findMilestoneIds(basePath: string): string[] {
|
|||
} catch (err) {
|
||||
// Log why milestone scanning failed — silent [] here causes infinite loops (#456)
|
||||
if (existsSync(dir)) {
|
||||
console.error(`[gsd] findMilestoneIds: .gsd/milestones/ exists but readdirSync failed — ${err instanceof Error ? err.message : String(err)}`);
|
||||
console.error(`[gsd] findMilestoneIds: .gsd/milestones/ exists but readdirSync failed — ${getErrorMessage(err)}`);
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import { existsSync, readFileSync, unlinkSync, rmSync } from "node:fs";
|
|||
import { join } from "node:path";
|
||||
import { GSDError, GSD_GIT_ERROR } from "./errors.js";
|
||||
import { GIT_NO_PROMPT_ENV } from "./git-constants.js";
|
||||
import { getErrorMessage } from "./error-utils.js";
|
||||
|
||||
// Issue #453: keep auto-mode bookkeeping on the stable git CLI path unless a
|
||||
// caller explicitly opts into the native helper.
|
||||
|
|
@ -716,7 +717,7 @@ export function nativeCommit(
|
|||
try {
|
||||
return native.gitCommit(basePath, message, options?.allowEmpty);
|
||||
} catch (e) {
|
||||
const msg = e instanceof Error ? e.message : String(e);
|
||||
const msg = getErrorMessage(e);
|
||||
if (msg.includes("nothing to commit")) return null;
|
||||
throw e;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import { mergeMilestoneToMain } from "./auto-worktree.js";
|
|||
import { MergeConflictError } from "./git-service.js";
|
||||
import { removeSessionStatus } from "./session-status-io.js";
|
||||
import type { WorkerInfo } from "./parallel-orchestrator.js";
|
||||
import { getErrorMessage } from "./error-utils.js";
|
||||
|
||||
// ─── Types ─────────────────────────────────────────────────────────────────
|
||||
|
||||
|
|
@ -99,7 +100,7 @@ export async function mergeCompletedMilestone(
|
|||
return {
|
||||
milestoneId,
|
||||
success: false,
|
||||
error: err instanceof Error ? err.message : String(err),
|
||||
error: getErrorMessage(err),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ import {
|
|||
analyzeParallelEligibility,
|
||||
type ParallelCandidates,
|
||||
} from "./parallel-eligibility.js";
|
||||
import { getErrorMessage } from "./error-utils.js";
|
||||
|
||||
// ─── Types ─────────────────────────────────────────────────────────────────
|
||||
|
||||
|
|
@ -363,7 +364,7 @@ export async function startParallel(
|
|||
|
||||
started.push(mid);
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : String(err);
|
||||
const message = getErrorMessage(err);
|
||||
errors.push({ mid, error: message });
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import { join } from "node:path";
|
|||
import { loadPrompt } from "./prompt-loader.js";
|
||||
import { gsdRoot } from "./paths.js";
|
||||
import { createGitService, runGit } from "./git-service.js";
|
||||
import { getErrorMessage } from "./error-utils.js";
|
||||
|
||||
// ─── Quick Task Helpers ───────────────────────────────────────────────────────
|
||||
|
||||
|
|
@ -122,7 +123,7 @@ export async function handleQuick(
|
|||
}
|
||||
} catch (err) {
|
||||
// Branch creation failed — continue on current branch
|
||||
const message = err instanceof Error ? err.message : String(err);
|
||||
const message = getErrorMessage(err);
|
||||
ctx.ui.notify(`Could not create branch ${branchName}: ${message}. Working on current branch.`, "warning");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ import type { FileLineStat } from "./worktree-manager.js";
|
|||
import { existsSync, realpathSync, readdirSync, rmSync, unlinkSync } from "node:fs";
|
||||
import { nativeMergeAbort } from "./native-git-bridge.js";
|
||||
import { join, sep } from "node:path";
|
||||
import { getErrorMessage } from "./error-utils.js";
|
||||
|
||||
/**
|
||||
* Tracks the original project root so we can switch back.
|
||||
|
|
@ -370,7 +371,7 @@ async function handleCreate(
|
|||
"info",
|
||||
);
|
||||
} catch (error) {
|
||||
const msg = error instanceof Error ? error.message : String(error);
|
||||
const msg = getErrorMessage(error);
|
||||
ctx.ui.notify(`Failed to create worktree: ${msg}`, "error");
|
||||
}
|
||||
}
|
||||
|
|
@ -418,7 +419,7 @@ async function handleSwitch(
|
|||
"info",
|
||||
);
|
||||
} catch (error) {
|
||||
const msg = error instanceof Error ? error.message : String(error);
|
||||
const msg = getErrorMessage(error);
|
||||
ctx.ui.notify(`Failed to switch to worktree: ${msg}`, "error");
|
||||
}
|
||||
}
|
||||
|
|
@ -528,7 +529,7 @@ async function handleList(
|
|||
|
||||
ctx.ui.notify(lines.join("\n"), "info");
|
||||
} catch (error) {
|
||||
const msg = error instanceof Error ? error.message : String(error);
|
||||
const msg = getErrorMessage(error);
|
||||
ctx.ui.notify(`Failed to list worktrees: ${msg}`, "error");
|
||||
}
|
||||
}
|
||||
|
|
@ -646,7 +647,7 @@ async function handleMerge(
|
|||
);
|
||||
return;
|
||||
} catch (mergeErr) {
|
||||
const mergeMsg = mergeErr instanceof Error ? mergeErr.message : String(mergeErr);
|
||||
const mergeMsg = getErrorMessage(mergeErr);
|
||||
const isConflict = /conflict/i.test(mergeMsg);
|
||||
|
||||
if (isConflict) {
|
||||
|
|
@ -703,7 +704,7 @@ async function handleMerge(
|
|||
"info",
|
||||
);
|
||||
} catch (error) {
|
||||
const msg = error instanceof Error ? error.message : String(error);
|
||||
const msg = getErrorMessage(error);
|
||||
ctx.ui.notify(`Failed to start merge: ${msg}`, "error");
|
||||
}
|
||||
}
|
||||
|
|
@ -746,7 +747,7 @@ async function handleRemove(
|
|||
|
||||
ctx.ui.notify(`${CLR.ok("✓")} Worktree ${CLR.name(name)} removed ${CLR.muted("(branch deleted)")}.`, "info");
|
||||
} catch (error) {
|
||||
const msg = error instanceof Error ? error.message : String(error);
|
||||
const msg = getErrorMessage(error);
|
||||
ctx.ui.notify(`Failed to remove worktree: ${msg}`, "error");
|
||||
}
|
||||
}
|
||||
|
|
@ -800,7 +801,7 @@ async function handleRemoveAll(
|
|||
if (failed.length > 0) lines.push(`${CLR.warn("✗")} Failed: ${failed.map(n => CLR.name(n)).join(", ")}`);
|
||||
ctx.ui.notify(lines.join("\n"), failed.length > 0 ? "warning" : "info");
|
||||
} catch (error) {
|
||||
const msg = error instanceof Error ? error.message : String(error);
|
||||
const msg = getErrorMessage(error);
|
||||
ctx.ui.notify(`Failed to remove worktrees: ${msg}`, "error");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue