mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-09-30 12:39:42 +02:00
fix: prevent orphaned claude processes and session memory leaks
- killScreen now finds and kills all child processes before quitting screen to prevent claude processes from becoming orphaned (the main bug) - Add cleanupSession() method for comprehensive resource cleanup: respawn controllers, timers, batches, event listeners, and sessions - Fix /api/run endpoint to cleanup sessions after completion - Fix /api/quick-start error path to cleanup on failure - Add MAX_CONCURRENT_SESSIONS (50) limit to prevent unbounded growth - Update CLAUDE.md with improved documentation Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -6,7 +6,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
||||
|
||||
Claudeman is a Claude Code session manager with a web interface and autonomous Ralph Loop. It spawns Claude CLI processes via PTY, streams output in real-time via SSE, and supports scheduled/timed runs.
|
||||
|
||||
**Tech Stack**: TypeScript (ES2022/NodeNext), Node.js, Fastify, Server-Sent Events, node-pty
|
||||
**Tech Stack**: TypeScript (ES2022/NodeNext, strict mode), Node.js, Fastify, Server-Sent Events, node-pty
|
||||
|
||||
**Requirements**: Node.js 18+, Claude CLI (`claude`) installed and available in PATH
|
||||
|
||||
@@ -22,35 +22,42 @@ npx tsx src/index.ts web -p 8080 # Dev mode with custom port
|
||||
node dist/index.js web # After npm run build
|
||||
claudeman web # After npm link
|
||||
|
||||
# IMPORTANT: `npm run dev` runs the CLI help, NOT the web server
|
||||
# GOTCHA: `npm run dev` runs CLI help, NOT the web server
|
||||
# Always use `npx tsx src/index.ts web` for development
|
||||
|
||||
# Testing (vitest)
|
||||
# Testing (vitest with vi.mock() - no real Claude CLI spawned)
|
||||
npm run test # Run all tests once
|
||||
npm run test:watch # Watch mode
|
||||
npm run test:coverage # With coverage report
|
||||
npx vitest run test/session.test.ts # Single file
|
||||
npx vitest run -t "should create session" # By pattern
|
||||
|
||||
# Debugging
|
||||
screen -ls # List GNU screen sessions
|
||||
screen -r <name> # Attach to screen session
|
||||
curl localhost:3000/api/sessions # Check active sessions
|
||||
```
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
src/
|
||||
├── index.ts # CLI entry point (commander)
|
||||
├── cli.ts # CLI command implementations
|
||||
├── index.ts # CLI entry (commander)
|
||||
├── cli.ts # CLI commands
|
||||
├── session.ts # Core: PTY wrapper for Claude CLI + token tracking
|
||||
├── session-manager.ts # Manages multiple sessions
|
||||
├── screen-manager.ts # GNU screen session persistence + process stats
|
||||
├── screen-manager.ts # GNU screen persistence + process stats
|
||||
├── respawn-controller.ts # Auto-respawn state machine
|
||||
├── task-tracker.ts # Background task detection and tree display
|
||||
├── ralph-loop.ts # Autonomous task assignment
|
||||
├── task.ts / task-queue.ts # Priority queue with dependencies
|
||||
├── task-queue.ts # Priority queue with dependencies
|
||||
├── state-store.ts # Persistence to ~/.claudeman/state.json
|
||||
├── types.ts # All TypeScript interfaces
|
||||
├── web/
|
||||
│ ├── server.ts # Fastify REST API + SSE + session restoration
|
||||
│ └── public/ # Static frontend files (vanilla JS, xterm.js)
|
||||
│ └── public/ # Vanilla JS frontend (xterm.js, no bundler)
|
||||
│ ├── app.js # Main app logic, SSE handling, tab management
|
||||
│ ├── styles.css # All styles including responsive/mobile
|
||||
│ └── index.html # Single page with modal templates
|
||||
└── templates/
|
||||
└── claude-md.ts # CLAUDE.md generator for new cases
|
||||
```
|
||||
@@ -64,18 +71,18 @@ src/
|
||||
|
||||
### Key Components
|
||||
|
||||
- **Session** (`src/session.ts`): Wraps Claude CLI as PTY subprocess. Two modes: `runPrompt(prompt)` for one-shot, `startInteractive()` for persistent terminal. Emits `output`, `terminal`, `message`, `completion`, `exit`, `idle`, `working`, `autoClear`, `clearTerminal` events.
|
||||
- **Session** (`src/session.ts`): Wraps Claude CLI as PTY subprocess. Two modes: `runPrompt(prompt)` for one-shot, `startInteractive()` for persistent terminal. Emits events: `output`, `terminal`, `message`, `completion`, `exit`, `idle`, `working`, `autoClear`, `clearTerminal`.
|
||||
|
||||
- **RespawnController** (`src/respawn-controller.ts`): State machine that keeps interactive sessions productive. Detects idle → sends update prompt → optionally `/clear` → optionally `/init` → repeats.
|
||||
- **RespawnController** (`src/respawn-controller.ts`): State machine that keeps sessions productive. Detects idle → sends update prompt → optionally `/clear` → optionally `/init` → repeats. State flow: `WATCHING → SENDING_UPDATE → WAITING_UPDATE → SENDING_CLEAR → WAITING_CLEAR → SENDING_INIT → WAITING_INIT → WATCHING`
|
||||
|
||||
- **ScreenManager** (`src/screen-manager.ts`): Manages GNU screen sessions for persistent terminals. Screens survive server restarts.
|
||||
- **ScreenManager** (`src/screen-manager.ts`): Wraps sessions in GNU screen for persistence across server restarts. On startup, reconciles with `screen -ls` to restore sessions.
|
||||
|
||||
- **WebServer** (`src/web/server.ts`): Fastify server with REST API + SSE. All endpoints under `/api/`. See file for full route list.
|
||||
- **WebServer** (`src/web/server.ts`): Fastify server with REST API (`/api/*`) + SSE (`/api/events`). Wires session events to SSE broadcast.
|
||||
|
||||
### Session Modes
|
||||
|
||||
- **One-Shot** (`runPrompt(prompt)`): Execute single prompt, receive completion event, session exits
|
||||
- **Interactive** (`startInteractive()`): Persistent PTY terminal with resize support, buffer persistence
|
||||
- **One-Shot** (`runPrompt(prompt)`): Single prompt execution, emits completion, exits
|
||||
- **Interactive** (`startInteractive()`): Persistent PTY terminal with resize support
|
||||
- **Shell** (`startShell()`): Plain bash/zsh terminal without Claude
|
||||
|
||||
## Code Patterns
|
||||
@@ -104,38 +111,35 @@ pty.spawn('claude', ['--dangerously-skip-permissions'], { ... })
|
||||
|
||||
### Idle Detection
|
||||
|
||||
Session detects idle by watching for prompt character (`❯` or `\u276f`) and waiting 2 seconds without activity. RespawnController uses the same patterns plus spinner characters to detect working state.
|
||||
Session detects idle by watching for prompt character (`❯` or `\u276f`) and waiting 2 seconds without activity.
|
||||
|
||||
### Token Tracking
|
||||
|
||||
- **One-shot mode**: Uses `--output-format stream-json` for detailed token usage from JSON
|
||||
- **Interactive mode**: Parses tokens from Claude's status line (e.g., "123.4k tokens"), estimates 60/40 input/output split
|
||||
|
||||
### Respawn Controller State Machine
|
||||
### Terminal Display Fix (Tab Switch & New Session)
|
||||
|
||||
```
|
||||
WATCHING → SENDING_UPDATE → WAITING_UPDATE → SENDING_CLEAR → WAITING_CLEAR → SENDING_INIT → WAITING_INIT → WATCHING
|
||||
```
|
||||
|
||||
### Screen Session Initialization
|
||||
|
||||
GNU screen creates blank space at top when initializing. After attaching:
|
||||
- **Claude sessions**: Clear buffer, emit `clearTerminal` event, client clears xterm
|
||||
- **Shell sessions**: Clear buffer, send `clear\n` command
|
||||
|
||||
### Tab Switching Fix
|
||||
|
||||
When switching Claude session tabs, terminal may be rendered at wrong size. Fix sequence:
|
||||
When switching tabs or creating new sessions, terminal may be rendered at wrong size. Fix sequence:
|
||||
1. Clear and reset xterm
|
||||
2. Write terminal buffer
|
||||
3. Send resize to update PTY dimensions
|
||||
4. Send Ctrl+L (`\x0c`) to trigger Claude CLI redraw
|
||||
|
||||
Uses `pendingCtrlL` Set to track sessions needing the fix. Waits for `session:idle` or `session:working` SSE event before sending resize + Ctrl+L.
|
||||
|
||||
### SSE Events
|
||||
|
||||
All events broadcast to `/api/events` with format: `{ type: string, sessionId?: string, data: any }`.
|
||||
|
||||
Event prefixes: `session:`, `task:`, `respawn:`, `scheduled:`, `case:`, `init`. Key events: `session:idle`, `session:working`, `session:terminal`, `session:clearTerminal`, `session:completion`, `respawn:stateChanged`.
|
||||
Event prefixes: `session:`, `task:`, `respawn:`, `scheduled:`, `case:`, `init`. Key events: `session:idle`, `session:working`, `session:terminal`, `session:clearTerminal`, `session:completion`.
|
||||
|
||||
### Frontend (app.js)
|
||||
|
||||
The frontend uses vanilla JS with xterm.js. Key patterns:
|
||||
- **SSE handling**: `handleSSEEvent()` switch statement dispatches all event types
|
||||
- **Tab management**: `switchToSession()` handles terminal buffer restore + resize
|
||||
- **60fps rendering**: Server batches at 16ms intervals, client uses `requestAnimationFrame`
|
||||
|
||||
## Adding New Features
|
||||
|
||||
@@ -158,7 +162,6 @@ Event prefixes: `session:`, `task:`, `respawn:`, `scheduled:`, `case:`, `init`.
|
||||
|
||||
- State persists to `~/.claudeman/state.json` and `~/.claudeman/screens.json`
|
||||
- Cases created in `~/claudeman-cases/` by default
|
||||
- Sessions wrapped in GNU screen for persistence across server restarts
|
||||
- Tests use vitest with `vi.mock()` - no real Claude CLI spawned
|
||||
- Long-running sessions (12-24+ hours) supported with automatic buffer trimming
|
||||
- Kill All works on restored sessions: `Session.stop()` checks screenManager directly by session ID
|
||||
- Long-running sessions (12-24+ hours) supported with automatic buffer trimming (5MB terminal, 2MB text, 1000 messages max)
|
||||
- E2E testing available via agent-browser (see `.claude/skills/e2e-test.md`)
|
||||
|
||||
Reference in New Issue
Block a user