This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

Fix usses with lifecycle hooks, command parsing, peek flags

- app() now calls onEnter/onLeave on screen transitions, overlay
changes, and shutdown. Fixes tick timers, PTY cleanup, and
spinner leaks across screen switches.

- Interactive create wizard spawns via sh -c instead of splitting
on whitespace. Quoted args, pipes, and env vars now work.

- Reject pty peek -f --plain — follow mode streams raw ANSI,
incompatible with plain output.

Nathan Herald (Mar 28, 2026, 11:55 AM +0100) d2b72f8b de8b3d50

+31 -18
+4
src/cli.ts
··· 195 195 else break; 196 196 pi++; 197 197 } 198 + if (follow && plain) { 199 + console.error("Cannot use --plain with -f (follow mode streams raw terminal output)."); 200 + process.exit(1); 201 + } 198 202 const peekName = args[pi]; 199 203 if (!peekName) { 200 204 console.error("Usage: pty peek [-f] [--plain] <name>");
+23 -1
src/tui/app.ts
··· 51 51 let sigintHandler: (() => void) | null = null; 52 52 let sigtermHandler: (() => void) | null = null; 53 53 let exitHandler: (() => void) | null = null; 54 + let activeScreen: Screen | null = null; 55 + let activeOverlay: Screen | null = null; 54 56 55 57 function getSize(): [number, number] { 56 58 return [(stdout as any).rows ?? 35, (stdout as any).columns ?? 120]; ··· 90 92 const [rows, cols] = getSize(); 91 93 const ctx = createContext(rows, cols); 92 94 const scr = resolveScreen(); 95 + 96 + // Detect screen transition 97 + if (scr !== activeScreen) { 98 + activeScreen?.onLeave?.(ctx); 99 + activeScreen = scr; 100 + scr.onEnter?.(ctx); 101 + } 102 + 93 103 const buf = scr.renderToBuffer(ctx); 94 104 95 105 // Composite overlay if present 96 106 if (config.overlay) { 97 - const ov = config.overlay(); 107 + const ov = config.overlay() ?? null; 108 + // Detect overlay transition 109 + if (ov !== activeOverlay) { 110 + activeOverlay?.onLeave?.(ctx); 111 + activeOverlay = ov; 112 + ov?.onEnter?.(ctx); 113 + } 98 114 if (ov) { 99 115 const overlayBuf = ov.renderToBuffer(ctx); 100 116 // Bounding-box composite: find non-empty region, copy all cells within ··· 219 235 running = false; 220 236 if (effectDispose) { effectDispose(); effectDispose = null; } 221 237 removeListeners(); 238 + // Call onLeave for active overlay and screen 239 + const ctx = createContext(...getSize()); 240 + activeOverlay?.onLeave?.(ctx); 241 + activeScreen?.onLeave?.(ctx); 242 + activeOverlay = null; 243 + activeScreen = null; 222 244 leaveTerminal(true); 223 245 }, 224 246
+4 -17
src/tui/interactive.ts
··· 558 558 cleanupAll(name); 559 559 } 560 560 561 - const parts = command.split(/\s+/); 562 - const cmd = parts[0]; 563 - const args = parts.slice(1); 561 + // Spawn via sh -c so the command field supports quotes, pipes, env vars, etc. 562 + const shellCmd = "/bin/sh"; 563 + const shellArgs = ["-c", command]; 564 564 565 - let resolvedCmd: string; 566 565 try { 567 - resolvedCmd = resolveCommand(cmd); 568 - } catch (e: any) { 569 - releaseLock(name); 570 - console.error(e.message); 571 - const updated = await listSessions(); 572 - sessions.set(updated); 573 - currentScreen.set("list"); 574 - myApp?.resume(); 575 - return; 576 - } 577 - 578 - try { 579 - await spawnDaemon(name, resolvedCmd, args, cmd, dir); 566 + await spawnDaemon(name, shellCmd, shellArgs, command, dir); 580 567 } catch (e: any) { 581 568 releaseLock(name); 582 569 console.error(e.message);