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.
This commit is contained in:
Chris Wanstrath 2026-03-20 22:30:14 -07:00
parent da7adc674d
commit 4574749cde
2 changed files with 19 additions and 25 deletions

View File

@ -31,7 +31,7 @@ function renderSessions(
const status = statusMap.get(s) ?? "idle" const status = statusMap.get(s) ?? "idle"
const { icon, color: bc } = styles[status] const { icon, color: bc } = styles[status]
const maxPrompt = cols - prefixWidth 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}`) console.log(`${icon} ${bc}${s.branch.padEnd(branchWidth)}${reset} ${dim}${truncated}${reset}`)
} }
} }

View File

@ -101,7 +101,7 @@ async function loadGlobal(): Promise<GlobalState> {
if (await file.exists()) { if (await file.exists()) {
try { try {
const data = await file.json() 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 {} } catch {}
} }
return { projects: [] } return { projects: [] }
@ -117,18 +117,27 @@ export function normalizePath(dir: string): string {
return resolve(dir.replace(/^~(?=\/|$)/, homedir())) return resolve(dir.replace(/^~(?=\/|$)/, homedir()))
} }
/** Register a project directory in the global state. */ async function registerPaths(paths: string[]): Promise<void> {
export async function registerProject(repoRoot: string): Promise<void> { if (paths.length === 0) return
const normalized = normalizePath(repoRoot)
await withGlobalLock(async () => { await withGlobalLock(async () => {
const gs = await loadGlobal() const gs = await loadGlobal()
if (!gs.projects.includes(normalized)) { const existing = new Set(gs.projects)
gs.projects.push(normalized) let changed = false
await saveGlobal(gs) 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<void> {
await registerPaths([normalizePath(repoRoot)])
}
/** Remove a project directory from the global state. */ /** Remove a project directory from the global state. */
export async function unregisterProject(dir: string): Promise<boolean> { export async function unregisterProject(dir: string): Promise<boolean> {
const target = normalizePath(dir) const target = normalizePath(dir)
@ -165,26 +174,11 @@ export async function scanAndRegister(dir: string, maxDepth = 5): Promise<string
} }
const children = entries.filter(e => e.isDirectory() && !e.isSymbolicLink() && !e.name.startsWith(".") && e.name !== "node_modules") const children = entries.filter(e => 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) await walk(root, 0)
await registerPaths(found)
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)
})
}
return found return found
} }