···1111- Export `PtyServer` and `ServerOptions` for embedding
1212- Export events system: `EventType`, `EventRecord`, `EventFollower`, `readRecentEvents`, `formatEvent`, and all event subtypes
1313- Export key resolution: `resolveKey`, `parseSeqValue`
1414-- Export session helpers: `validateName`, `cleanupAll`, `cleanupSocket`, `getSocketPath`
1414+- Export session helpers: `gc`, `validateName`, `cleanupAll`, `cleanupSocket`, `getSocketPath`
1515- Export protocol types: `PacketReader`, `MessageType`, `Packet`
1616- `spawnDaemon` now takes an options object with optional `rows`/`cols` (breaking change from positional args)
17171818### CLI improvements
1919+- Add `pty gc` command to remove all exited sessions at once
1920- Prevent accidental session nesting: `pty run` inside an existing session execs the command directly instead of creating a nested session (`-d` bypasses the check)
2021- Set `PTY_SESSION` env var in child processes so they can detect they're inside a pty session
2122- Add CPU and memory usage to `pty stats` (child process and daemon, via `ps`)
+1
README.md
···8080pty restart myserver # restart an exited session
8181pty kill myserver # terminate a running session
8282pty rm myserver # remove an exited session's metadata
8383+pty gc # remove all exited sessions
83848485pty wrap claude # auto-wrap claude in pty sessions
8586pty unwrap claude # remove the wrapper
+9
docs/client.md
···28282929Returns the Unix socket path for a session.
30303131+### `gc(): Promise<string[]>`
3232+3333+Remove all exited sessions. Returns the names of removed sessions.
3434+3535+```typescript
3636+const removed = await gc();
3737+console.log(`Cleaned up ${removed.length} sessions`);
3838+```
3939+3140### `cleanupSocket(name: string): void`
32413342Remove a session's `.sock` and `.pid` files.
+21
src/cli.ts
···88import {
99 listSessions,
1010 getSession,
1111+ gc,
1112 cleanupAll,
1213 cleanupSocket,
1314 validateName,
···4344 pty list List active sessions
4445 pty list --json List sessions as JSON
4546 pty kill <name> Kill or remove a session
4747+ pty gc Remove all exited sessions
4648 pty wrap <command> Auto-wrap a command in pty sessions
4749 pty unwrap <command> Remove a wrap
4850 pty wrap --list List wrapped commands
···368370 process.exit(1);
369371 }
370372 await cmdKill(args[1]);
373373+ break;
374374+ }
375375+376376+ case "gc": {
377377+ await cmdGc();
371378 break;
372379 }
373380···820827821828 cleanupAll(name);
822829 console.log(`Session "${name}" removed.`);
830830+}
831831+832832+async function cmdGc(): Promise<void> {
833833+ const removed = await gc();
834834+835835+ if (removed.length === 0) {
836836+ console.log("No exited sessions to clean up.");
837837+ return;
838838+ }
839839+840840+ for (const name of removed) {
841841+ console.log(`Removed: ${name}`);
842842+ }
843843+ console.log(`Cleaned up ${removed.length} exited session${removed.length === 1 ? "" : "s"}.`);
823844}
824845825846async function cmdRestart(name: string, force = false): Promise<void> {