This repository has no description
0

Configure Feed

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

Add pty gc command and gc() API to remove all exited sessions

Nathan Herald (Apr 6, 2026, 7:36 PM +0200) a5f09b5d c9f31de5

+44 -2
+2 -1
CHANGELOG.md
··· 11 11 - Export `PtyServer` and `ServerOptions` for embedding 12 12 - Export events system: `EventType`, `EventRecord`, `EventFollower`, `readRecentEvents`, `formatEvent`, and all event subtypes 13 13 - Export key resolution: `resolveKey`, `parseSeqValue` 14 - - Export session helpers: `validateName`, `cleanupAll`, `cleanupSocket`, `getSocketPath` 14 + - Export session helpers: `gc`, `validateName`, `cleanupAll`, `cleanupSocket`, `getSocketPath` 15 15 - Export protocol types: `PacketReader`, `MessageType`, `Packet` 16 16 - `spawnDaemon` now takes an options object with optional `rows`/`cols` (breaking change from positional args) 17 17 18 18 ### CLI improvements 19 + - Add `pty gc` command to remove all exited sessions at once 19 20 - Prevent accidental session nesting: `pty run` inside an existing session execs the command directly instead of creating a nested session (`-d` bypasses the check) 20 21 - Set `PTY_SESSION` env var in child processes so they can detect they're inside a pty session 21 22 - Add CPU and memory usage to `pty stats` (child process and daemon, via `ps`)
+1
README.md
··· 80 80 pty restart myserver # restart an exited session 81 81 pty kill myserver # terminate a running session 82 82 pty rm myserver # remove an exited session's metadata 83 + pty gc # remove all exited sessions 83 84 84 85 pty wrap claude # auto-wrap claude in pty sessions 85 86 pty unwrap claude # remove the wrapper
+9
docs/client.md
··· 28 28 29 29 Returns the Unix socket path for a session. 30 30 31 + ### `gc(): Promise<string[]>` 32 + 33 + Remove all exited sessions. Returns the names of removed sessions. 34 + 35 + ```typescript 36 + const removed = await gc(); 37 + console.log(`Cleaned up ${removed.length} sessions`); 38 + ``` 39 + 31 40 ### `cleanupSocket(name: string): void` 32 41 33 42 Remove a session's `.sock` and `.pid` files.
+21
src/cli.ts
··· 8 8 import { 9 9 listSessions, 10 10 getSession, 11 + gc, 11 12 cleanupAll, 12 13 cleanupSocket, 13 14 validateName, ··· 43 44 pty list List active sessions 44 45 pty list --json List sessions as JSON 45 46 pty kill <name> Kill or remove a session 47 + pty gc Remove all exited sessions 46 48 pty wrap <command> Auto-wrap a command in pty sessions 47 49 pty unwrap <command> Remove a wrap 48 50 pty wrap --list List wrapped commands ··· 368 370 process.exit(1); 369 371 } 370 372 await cmdKill(args[1]); 373 + break; 374 + } 375 + 376 + case "gc": { 377 + await cmdGc(); 371 378 break; 372 379 } 373 380 ··· 820 827 821 828 cleanupAll(name); 822 829 console.log(`Session "${name}" removed.`); 830 + } 831 + 832 + async function cmdGc(): Promise<void> { 833 + const removed = await gc(); 834 + 835 + if (removed.length === 0) { 836 + console.log("No exited sessions to clean up."); 837 + return; 838 + } 839 + 840 + for (const name of removed) { 841 + console.log(`Removed: ${name}`); 842 + } 843 + console.log(`Cleaned up ${removed.length} exited session${removed.length === 1 ? "" : "s"}.`); 823 844 } 824 845 825 846 async function cmdRestart(name: string, force = false): Promise<void> {
+1 -1
src/client-api.ts
··· 3 3 4 4 // Session management 5 5 export { 6 - listSessions, getSession, validateName, 6 + listSessions, getSession, gc, validateName, 7 7 getSessionDir, getSocketPath, 8 8 cleanupSocket, cleanupAll, 9 9 type SessionInfo, type SessionMetadata,
+10
src/sessions.ts
··· 155 155 return sessions.find((s) => s.name === name) ?? null; 156 156 } 157 157 158 + /** Remove all exited sessions. Returns the names of removed sessions. */ 159 + export async function gc(): Promise<string[]> { 160 + const sessions = await listSessions(); 161 + const exited = sessions.filter((s) => s.status === "exited"); 162 + for (const s of exited) { 163 + cleanupAll(s.name); 164 + } 165 + return exited.map((s) => s.name); 166 + } 167 + 158 168 function readPid(name: string): number | null { 159 169 try { 160 170 const content = fs.readFileSync(getPidPath(name), "utf-8").trim();