From 4574749cde2f233d31e7985efd9ae98757f2d9e6 Mon Sep 17 00:00:00 2001 From: Chris Wanstrath Date: Fri, 20 Mar 2026 22:30:14 -0700 Subject: [PATCH] Fix prompt truncation edge case and harden global state handling Correct list truncation logic to avoid eliding prompts that already fit, and gracefully handle narrow terminals. Sanitize project entries on load, deduplicate registration through a shared registerPaths helper, and simplify scanAndRegister by reusing it. --- src/commands/list.ts | 2 +- src/state.ts | 42 ++++++++++++++++++------------------------ 2 files changed, 19 insertions(+), 25 deletions(-) diff --git a/src/commands/list.ts b/src/commands/list.ts index d19a6b1..c96567d 100644 --- a/src/commands/list.ts +++ b/src/commands/list.ts @@ -31,7 +31,7 @@ function renderSessions( const status = statusMap.get(s) ?? "idle" const { icon, color: bc } = styles[status] const maxPrompt = cols - prefixWidth - const truncated = maxPrompt < 1 ? "" : maxPrompt > 3 && prompt.length > maxPrompt ? prompt.slice(0, maxPrompt - 3) + "..." : prompt + const truncated = maxPrompt < 1 ? "" : prompt.length <= maxPrompt ? prompt : maxPrompt > 3 ? prompt.slice(0, maxPrompt - 3) + "..." : prompt.slice(0, maxPrompt) console.log(`${icon} ${bc}${s.branch.padEnd(branchWidth)}${reset} ${dim}${truncated}${reset}`) } } diff --git a/src/state.ts b/src/state.ts index bd174a5..0e3944e 100644 --- a/src/state.ts +++ b/src/state.ts @@ -101,7 +101,7 @@ async function loadGlobal(): Promise { if (await file.exists()) { try { const data = await file.json() - if (data && Array.isArray(data.projects)) return data + if (data && Array.isArray(data.projects)) return { projects: data.projects.filter((p: unknown) => typeof p === "string") } } catch {} } return { projects: [] } @@ -117,18 +117,27 @@ export function normalizePath(dir: string): string { return resolve(dir.replace(/^~(?=\/|$)/, homedir())) } -/** Register a project directory in the global state. */ -export async function registerProject(repoRoot: string): Promise { - const normalized = normalizePath(repoRoot) +async function registerPaths(paths: string[]): Promise { + if (paths.length === 0) return await withGlobalLock(async () => { const gs = await loadGlobal() - if (!gs.projects.includes(normalized)) { - gs.projects.push(normalized) - await saveGlobal(gs) + const existing = new Set(gs.projects) + let changed = false + for (const p of paths) { + if (!existing.has(p)) { + gs.projects.push(p) + changed = true + } } + if (changed) await saveGlobal(gs) }) } +/** Register a project directory in the global state. */ +export async function registerProject(repoRoot: string): Promise { + await registerPaths([normalizePath(repoRoot)]) +} + /** Remove a project directory from the global state. */ export async function unregisterProject(dir: string): Promise { const target = normalizePath(dir) @@ -165,26 +174,11 @@ export async function scanAndRegister(dir: string, maxDepth = 5): Promise e.isDirectory() && !e.isSymbolicLink() && !e.name.startsWith(".") && e.name !== "node_modules") - await Promise.all(children.map(entry => walk(join(d, entry.name), depth + 1))) + for (const entry of children) await walk(join(d, entry.name), depth + 1) } await walk(root, 0) - - if (found.length > 0) { - await withGlobalLock(async () => { - const gs = await loadGlobal() - const existing = new Set(gs.projects) - let changed = false - for (const p of found) { - if (!existing.has(p)) { - gs.projects.push(p) - changed = true - } - } - if (changed) await saveGlobal(gs) - }) - } - + await registerPaths(found) return found }