refactor: remove unnecessary 'as any' casts, dead exports, and duplicate code (#786)
Issues addressed: 1. guided-flow.ts: Remove 12 unnecessary 'ctx as any' casts - ctx is already ExtensionCommandContext, matching showNextAction/showConfirm signatures - The casts masked type-checking with no benefit 2. triage-ui.ts: Remove 1 unnecessary 'ctx as any' cast (same issue as #1) 3. migrate/command.ts: Remove 2 unnecessary 'ctx as any' casts (same issue as #1) 4. models-resolver.ts: Remove dead exports hasBothModelsFiles() and getModelsPaths() - Never imported outside the module or in any test file - resolveModelsJsonPath() (the only consumer) remains 5. resource-loader.ts: Remove dead export readManagedResourceSyncedAt() - Exported but never imported anywhere in the entire codebase 6. bg-shell/overlay.ts: Extract processStatusHeader() helper - DRYs the duplicated status icon + name + uptime + tab indicator construction shared between renderOutput() and renderEvents() 7. get-secrets-from-user.ts: Merge duplicate vercel/convex deployment blocks - Both had identical exec → check result code → push applied/errors pattern - Merged into single conditional with destination-specific command string Documented but not changed (boundary constraints): - src/mcp-server.ts ↔ src/resources/extensions/gsd/mcp-server.ts (compiled/jiti boundary prevents sharing) - src/remote-questions-config.ts ↔ remote-questions/remote-command.ts (same compiled/jiti boundary per #592) - cli.ts internal duplication of session setup (structural, different resource loader configs)
This commit is contained in:
parent
ad4e68551d
commit
7712abe7d1
7 changed files with 38 additions and 78 deletions
|
|
@ -37,19 +37,4 @@ export function resolveModelsJsonPath(): string {
|
|||
return GSD_MODELS_PATH
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if both GSD and PI models.json files exist.
|
||||
*/
|
||||
export function hasBothModelsFiles(): boolean {
|
||||
return existsSync(GSD_MODELS_PATH) && existsSync(PI_MODELS_PATH)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the paths to both models.json files.
|
||||
*/
|
||||
export function getModelsPaths(): { gsd: string; pi: string } {
|
||||
return {
|
||||
gsd: GSD_MODELS_PATH,
|
||||
pi: PI_MODELS_PATH,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -116,14 +116,6 @@ export function readManagedResourceVersion(agentDir: string): string | null {
|
|||
}
|
||||
}
|
||||
|
||||
export function readManagedResourceSyncedAt(agentDir: string): number | null {
|
||||
try {
|
||||
const manifest = JSON.parse(readFileSync(getManagedResourceManifestPath(agentDir), 'utf-8')) as ManagedResourceManifest
|
||||
return typeof manifest?.syncedAt === 'number' ? manifest.syncedAt : null
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
export function getNewerManagedResourceVersion(agentDir: string, currentVersion: string): string | null {
|
||||
const managedVersion = readManagedResourceVersion(agentDir)
|
||||
|
|
|
|||
|
|
@ -328,12 +328,9 @@ export class BgManagerOverlay {
|
|||
return this.box(inner, width);
|
||||
}
|
||||
|
||||
private renderOutput(width: number): string[] {
|
||||
private processStatusHeader(p: typeof this.viewingProcess, activeTab: "output" | "events"): { statusIcon: string; headerLine: string } {
|
||||
const th = this.theme;
|
||||
const p = this.viewingProcess;
|
||||
if (!p) return [""];
|
||||
const inner: string[] = [];
|
||||
|
||||
if (!p) return { statusIcon: "", headerLine: "" };
|
||||
const statusIcon = p.alive
|
||||
? (p.status === "ready" ? th.fg("success", "●")
|
||||
: p.status === "error" ? th.fg("error", "●")
|
||||
|
|
@ -343,9 +340,21 @@ export class BgManagerOverlay {
|
|||
const uptime = th.fg("dim", formatUptime(Date.now() - p.startedAt));
|
||||
const typeTag = th.fg("dim", `[${p.processType}]`);
|
||||
const portInfo = p.ports.length > 0 ? th.fg("dim", ` :${p.ports.join(",")}`) : "";
|
||||
const tabIndicator = th.fg("accent", "[Output]") + " " + th.fg("dim", "Events");
|
||||
const tabIndicator = activeTab === "output"
|
||||
? th.fg("accent", "[Output]") + " " + th.fg("dim", "Events")
|
||||
: th.fg("dim", "Output") + " " + th.fg("accent", "[Events]");
|
||||
const headerLine = `${statusIcon} ${name} ${typeTag} ${uptime}${portInfo} ${tabIndicator}`;
|
||||
return { statusIcon, headerLine };
|
||||
}
|
||||
|
||||
inner.push(`${statusIcon} ${name} ${typeTag} ${uptime}${portInfo} ${tabIndicator}`);
|
||||
private renderOutput(width: number): string[] {
|
||||
const th = this.theme;
|
||||
const p = this.viewingProcess;
|
||||
if (!p) return [""];
|
||||
const inner: string[] = [];
|
||||
|
||||
const { headerLine } = this.processStatusHeader(p, "output");
|
||||
inner.push(headerLine);
|
||||
inner.push("");
|
||||
|
||||
// Unified buffer is already chronologically interleaved
|
||||
|
|
@ -384,16 +393,8 @@ export class BgManagerOverlay {
|
|||
if (!p) return [""];
|
||||
const inner: string[] = [];
|
||||
|
||||
const statusIcon = p.alive
|
||||
? (p.status === "ready" ? th.fg("success", "●")
|
||||
: p.status === "error" ? th.fg("error", "●")
|
||||
: th.fg("warning", "●"))
|
||||
: th.fg("dim", "○");
|
||||
const name = th.fg("muted", p.label);
|
||||
const uptime = th.fg("dim", formatUptime(Date.now() - p.startedAt));
|
||||
const tabIndicator = th.fg("dim", "Output") + " " + th.fg("accent", "[Events]");
|
||||
|
||||
inner.push(`${statusIcon} ${name} ${uptime} ${tabIndicator}`);
|
||||
const { headerLine } = this.processStatusHeader(p, "events");
|
||||
inner.push(headerLine);
|
||||
inner.push("");
|
||||
|
||||
if (p.events.length === 0) {
|
||||
|
|
|
|||
|
|
@ -369,32 +369,14 @@ async function applySecrets(
|
|||
}
|
||||
}
|
||||
|
||||
if (destination === "vercel" && opts.exec) {
|
||||
if ((destination === "vercel" || destination === "convex") && opts.exec) {
|
||||
const env = opts.environment ?? "development";
|
||||
for (const { key, value } of provided) {
|
||||
const cmd = destination === "vercel"
|
||||
? `printf %s ${shellEscapeSingle(value)} | vercel env add ${key} ${env}`
|
||||
: `npx convex env set ${key} ${shellEscapeSingle(value)}`;
|
||||
try {
|
||||
const result = await opts.exec("sh", [
|
||||
"-c",
|
||||
`printf %s ${shellEscapeSingle(value)} | vercel env add ${key} ${env}`,
|
||||
]);
|
||||
if (result.code !== 0) {
|
||||
errors.push(`${key}: ${result.stderr.slice(0, 200)}`);
|
||||
} else {
|
||||
applied.push(key);
|
||||
}
|
||||
} catch (err: any) {
|
||||
errors.push(`${key}: ${err.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (destination === "convex" && opts.exec) {
|
||||
for (const { key, value } of provided) {
|
||||
try {
|
||||
const result = await opts.exec("sh", [
|
||||
"-c",
|
||||
`npx convex env set ${key} ${shellEscapeSingle(value)}`,
|
||||
]);
|
||||
const result = await opts.exec("sh", ["-c", cmd]);
|
||||
if (result.code !== 0) {
|
||||
errors.push(`${key}: ${result.stderr.slice(0, 200)}`);
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -870,7 +870,7 @@ export async function showDiscuss(
|
|||
const draftFile = resolveMilestoneFile(basePath, mid, "CONTEXT-DRAFT");
|
||||
const draftContent = draftFile ? await loadFile(draftFile) : null;
|
||||
|
||||
const choice = await showNextAction(ctx as any, {
|
||||
const choice = await showNextAction(ctx, {
|
||||
title: `GSD — ${mid}: ${milestoneTitle}`,
|
||||
summary: ["This milestone has a draft context from a prior discussion.", "It needs a dedicated discussion before auto-planning can begin."],
|
||||
actions: [
|
||||
|
|
@ -947,7 +947,7 @@ export async function showDiscuss(
|
|||
recommended: i === 0,
|
||||
}));
|
||||
|
||||
const choice = await showNextAction(ctx as any, {
|
||||
const choice = await showNextAction(ctx, {
|
||||
title: "GSD — Discuss a slice",
|
||||
summary: [
|
||||
`${mid}: ${milestoneTitle}`,
|
||||
|
|
@ -1056,7 +1056,7 @@ export async function showSmartEntry(
|
|||
const crashLock = readCrashLock(basePath);
|
||||
if (crashLock) {
|
||||
clearLock(basePath);
|
||||
const resume = await showNextAction(ctx as any, {
|
||||
const resume = await showNextAction(ctx, {
|
||||
title: "GSD — Interrupted Session Detected",
|
||||
summary: [formatCrashInfo(crashLock)],
|
||||
actions: [
|
||||
|
|
@ -1116,7 +1116,7 @@ export async function showSmartEntry(
|
|||
basePath
|
||||
));
|
||||
} else {
|
||||
const choice = await showNextAction(ctx as any, {
|
||||
const choice = await showNextAction(ctx, {
|
||||
title: "GSD — Get Shit Done",
|
||||
summary: ["No active milestone."],
|
||||
actions: [
|
||||
|
|
@ -1146,7 +1146,7 @@ export async function showSmartEntry(
|
|||
|
||||
// ── All milestones complete → New milestone ──────────────────────────
|
||||
if (state.phase === "complete") {
|
||||
const choice = await showNextAction(ctx as any, {
|
||||
const choice = await showNextAction(ctx, {
|
||||
title: `GSD — ${milestoneId}: ${milestoneTitle}`,
|
||||
summary: ["All milestones complete."],
|
||||
actions: [
|
||||
|
|
@ -1187,7 +1187,7 @@ export async function showSmartEntry(
|
|||
const draftFile = resolveMilestoneFile(basePath, milestoneId, "CONTEXT-DRAFT");
|
||||
const draftContent = draftFile ? await loadFile(draftFile) : null;
|
||||
|
||||
const choice = await showNextAction(ctx as any, {
|
||||
const choice = await showNextAction(ctx, {
|
||||
title: `GSD — ${milestoneId}: ${milestoneTitle}`,
|
||||
summary: ["This milestone has a draft context from a prior discussion.", "It needs a dedicated discussion before auto-planning can begin."],
|
||||
actions: [
|
||||
|
|
@ -1278,7 +1278,7 @@ export async function showSmartEntry(
|
|||
},
|
||||
];
|
||||
|
||||
const choice = await showNextAction(ctx as any, {
|
||||
const choice = await showNextAction(ctx, {
|
||||
title: `GSD — ${milestoneId}: ${milestoneTitle}`,
|
||||
summary: [hasContext ? "Context captured. Ready to create roadmap." : "New milestone — no roadmap yet."],
|
||||
actions,
|
||||
|
|
@ -1315,7 +1315,7 @@ export async function showSmartEntry(
|
|||
} else if (choice === "discard_milestone") {
|
||||
const mDir = resolveMilestonePath(basePath, milestoneId);
|
||||
if (!mDir) return;
|
||||
const confirmed = await showConfirm(ctx as any, {
|
||||
const confirmed = await showConfirm(ctx, {
|
||||
title: "Discard milestone?",
|
||||
message: `This will permanently delete ${milestoneId} and all its contents.`,
|
||||
confirmLabel: "Discard",
|
||||
|
|
@ -1342,7 +1342,7 @@ export async function showSmartEntry(
|
|||
},
|
||||
];
|
||||
|
||||
const choice = await showNextAction(ctx as any, {
|
||||
const choice = await showNextAction(ctx, {
|
||||
title: `GSD — ${milestoneId}: ${milestoneTitle}`,
|
||||
summary: ["Roadmap exists. Ready to execute."],
|
||||
actions,
|
||||
|
|
@ -1400,7 +1400,7 @@ export async function showSmartEntry(
|
|||
? `${sliceId}: ${sliceTitle} (${summaryParts.join(", ")})`
|
||||
: `${sliceId}: ${sliceTitle} — ready for planning.`;
|
||||
|
||||
const choice = await showNextAction(ctx as any, {
|
||||
const choice = await showNextAction(ctx, {
|
||||
title: `GSD — ${milestoneId} / ${sliceId}: ${sliceTitle}`,
|
||||
summary: [summaryLine],
|
||||
actions,
|
||||
|
|
@ -1431,7 +1431,7 @@ export async function showSmartEntry(
|
|||
|
||||
// ── All tasks done → Complete slice ──────────────────────────────────
|
||||
if (state.phase === "summarizing") {
|
||||
const choice = await showNextAction(ctx as any, {
|
||||
const choice = await showNextAction(ctx, {
|
||||
title: `GSD — ${milestoneId} / ${sliceId}: ${sliceTitle}`,
|
||||
summary: ["All tasks complete. Ready for slice summary."],
|
||||
actions: [
|
||||
|
|
@ -1475,7 +1475,7 @@ export async function showSmartEntry(
|
|||
const hasInterrupted = !!(continueFile && await loadFile(continueFile)) ||
|
||||
!!(sDir && await loadFile(join(sDir, "continue.md")));
|
||||
|
||||
const choice = await showNextAction(ctx as any, {
|
||||
const choice = await showNextAction(ctx, {
|
||||
title: `GSD — ${milestoneId} / ${sliceId}: ${sliceTitle}`,
|
||||
summary: [
|
||||
hasInterrupted
|
||||
|
|
|
|||
|
|
@ -151,7 +151,7 @@ export async function handleMigrate(
|
|||
}
|
||||
|
||||
// ── Confirmation via showNextAction ────────────────────────────────────────
|
||||
const choice = await showNextAction(ctx as any, {
|
||||
const choice = await showNextAction(ctx, {
|
||||
title: "Migration preview",
|
||||
summary: lines,
|
||||
actions: [
|
||||
|
|
@ -187,7 +187,7 @@ export async function handleMigrate(
|
|||
);
|
||||
|
||||
// ── Post-write review offer ────────────────────────────────────────────────
|
||||
const reviewChoice = await showNextAction(ctx as any, {
|
||||
const reviewChoice = await showNextAction(ctx, {
|
||||
title: "Migration written",
|
||||
summary: [
|
||||
`${result.paths.length} files written to .gsd/`,
|
||||
|
|
|
|||
|
|
@ -135,7 +135,7 @@ export async function showTriageConfirmation(
|
|||
recommended: cls === proposed,
|
||||
}));
|
||||
|
||||
const choice = await showNextAction(ctx as any, {
|
||||
const choice = await showNextAction(ctx, {
|
||||
title: `Triage: ${result.captureId}`,
|
||||
summary,
|
||||
actions,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue