2026-01-18 04:50:46 +01:00
# CLAUDE.md
2026-01-18 07:31:19 +01:00
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
2026-01-18 04:50:46 +01:00
## Project Overview
2026-01-18 05:17:06 +01:00
2026-01-18 07:31:19 +01:00
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.
2026-01-18 04:50:46 +01:00
2026-01-18 07:31:19 +01:00
**Tech Stack** : TypeScript, Node.js, Fastify, Server-Sent Events, node-pty
2026-01-18 05:17:06 +01:00
2026-01-18 07:31:19 +01:00
## Commands
2026-01-18 05:17:06 +01:00
```bash
2026-01-18 07:31:19 +01:00
npm run build # Compile TypeScript + copy static files to dist/web/
2026-01-18 05:17:06 +01:00
npm run dev # Run with tsx (no build needed)
2026-01-18 07:38:19 +01:00
npm run clean # Remove dist/
2026-01-18 05:17:06 +01:00
2026-01-18 07:31:19 +01:00
npm link # Make 'claudeman' globally available
2026-01-18 05:17:06 +01:00
claudeman web # Start web interface on port 3000
claudeman web -p 8080 # Custom port
```
2026-01-18 07:31:19 +01:00
## Architecture
2026-01-18 05:17:06 +01:00
2026-01-18 07:38:19 +01:00
```
src/
├── index.ts # CLI entry point (commander)
├── cli.ts # CLI command implementations
├── session.ts # Core: PTY wrapper for Claude CLI
├── session-manager.ts # Manages multiple sessions
├── respawn-controller.ts # Auto-respawn state machine
├── ralph-loop.ts # Autonomous task assignment
├── task.ts / 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
│ └── public/ # Static frontend files
└── templates/
└── claude-md.ts # CLAUDE.md generator for new cases
```
2026-01-18 07:31:19 +01:00
2026-01-18 07:38:19 +01:00
### Data Flow
1. **Session** spawns `claude -p --dangerously-skip-permissions` via `node-pty`
2. PTY output is buffered, ANSI stripped, and parsed for JSON messages
3. **WebServer** broadcasts events to SSE clients at `/api/events`
2026-01-18 07:31:19 +01:00
4. State persists to `~/.claudeman/state.json` via **StateStore**
### Key Components
2026-01-18 21:37:03 +01:00
- **Session** (`src/session.ts` ): Wraps Claude CLI as PTY subprocess. Two modes: `runPrompt(prompt)` for one-shot execution, `startInteractive()` for persistent terminal. Emits `output` , `terminal` , `message` , `completion` , `exit` , `idle` , `working` events. Maintains terminal buffer for reconnections. Includes buffer management for long-running sessions (12-24+ hours) with automatic trimming.
2026-01-18 07:31:19 +01:00
2026-01-18 07:38:19 +01:00
- **RespawnController** (`src/respawn-controller.ts` ): State machine that keeps interactive sessions productive. Detects idle → sends update prompt → `/clear` → `/init` → repeats. Configurable timeouts and prompts.
2026-01-18 07:31:19 +01:00
2026-01-18 07:38:19 +01:00
- **RalphLoop** (`src/ralph-loop.ts` ): Autonomous task assignment controller. Monitors sessions for idle state, assigns tasks from queue, detects completion via `<promise>PHRASE</promise>` markers. Supports time-aware loops with minimum duration.
2026-01-18 07:31:19 +01:00
2026-01-18 07:38:19 +01:00
- **WebServer** (`src/web/server.ts` ): Fastify server with REST API + SSE. Manages sessions, scheduled runs, respawn controllers, and case directories. Broadcasts all events to connected clients.
2026-01-18 07:31:19 +01:00
2026-01-18 07:38:19 +01:00
### Session Modes
2026-01-18 07:31:19 +01:00
2026-01-18 07:38:19 +01:00
**One-Shot Mode** (`runPrompt(prompt)` ):
- Execute a single prompt and receive completion event
- Used for scheduled runs and quick API calls
- Session exits after prompt completes
2026-01-18 07:31:19 +01:00
2026-01-18 07:38:19 +01:00
**Interactive Mode** (`startInteractive()` ):
- Persistent PTY terminal with full Claude CLI access
- Supports terminal resize for proper formatting
- Terminal buffer persisted for client reconnections
- Works with RespawnController for autonomous cycling
2026-01-18 05:17:06 +01:00
## Code Patterns
2026-01-18 07:31:19 +01:00
### Claude Message Parsing
Claude CLI outputs newline-delimited JSON. Strip ANSI codes before parsing:
2026-01-18 05:17:06 +01:00
```typescript
2026-01-18 07:31:19 +01:00
const cleanLine = line . replace ( /\x1b\[[0-9;]*m/g , '' );
const msg = JSON . parse ( cleanLine ) as ClaudeMessage ;
2026-01-18 21:37:03 +01:00
// msg.type: 'system' | 'assistant' | 'user' | 'result'
2026-01-18 07:31:19 +01:00
// msg.message?.content: Array<{ type: 'text', text: string }>
// msg.total_cost_usd: number (on result messages)
2026-01-18 05:17:06 +01:00
```
2026-01-18 21:37:03 +01:00
### PTY Spawn Modes
**One-shot mode** (prompt execution):
```typescript
pty . spawn ( 'claude' , [ '-p' , '--dangerously-skip-permissions' , prompt ], { ... })
```
**Interactive mode** (persistent terminal):
```typescript
pty . spawn ( 'claude' , [ '--dangerously-skip-permissions' ], { ... })
```
2026-01-18 07:31:19 +01:00
### Idle Detection
2026-01-18 07:38:19 +01:00
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.
2026-01-18 07:31:19 +01:00
2026-01-18 21:37:03 +01:00
### Long-Running Session Support
Sessions are optimized for 12-24+ hour runs with automatic buffer management:
**Buffer Limits:**
- Terminal buffer: 5MB max, trims to 4MB when exceeded
- Text output: 2MB max, trims to 1.5MB when exceeded
- Messages: 1000 max, keeps most recent 800 when exceeded
**Performance Optimizations:**
- Server-side terminal batching at 60fps (16ms intervals)
- Client-side requestAnimationFrame batching for smooth rendering
- Buffer statistics available via session details for monitoring
**Buffer Stats Response:**
```typescript
{
bufferStats : {
terminalBufferSize : number ; // Current terminal buffer size in bytes
textOutputSize : number ; // Current text output size in bytes
messageCount : number ; // Number of parsed messages
maxTerminalBuffer : number ; // Max allowed terminal buffer
maxTextOutput : number ; // Max allowed text output
maxMessages : number ; // Max allowed messages
}
}
```
2026-01-18 07:31:19 +01:00
### Respawn Controller State Machine
```
WATCHING → SENDING_UPDATE → WAITING_UPDATE → SENDING_CLEAR → WAITING_CLEAR → SENDING_INIT → WAITING_INIT → WATCHING
2026-01-18 05:17:06 +01:00
```
2026-01-18 04:50:46 +01:00
2026-01-18 07:38:19 +01:00
Default config (`RespawnConfig` in `src/types.ts` ):
- `idleTimeoutMs` : 5000 (5s after prompt)
- `updatePrompt` : "update all the docs and CLAUDE.md"
- `interStepDelayMs` : 1000 (1s between steps)
2026-01-18 04:50:46 +01:00
2026-01-18 07:38:19 +01:00
### SSE Event Catalog
2026-01-18 07:31:19 +01:00
2026-01-18 07:38:19 +01:00
All events are broadcast to clients connected to `/api/events` . Event format: `{ type: string, sessionId?: string, data: any }` .
2026-01-18 07:31:19 +01:00
2026-01-18 07:38:19 +01:00
**Session Events:**
| Event | Data | Description |
|-------|------|-------------|
2026-01-18 21:37:03 +01:00
| `session:created` | `{ session }` | New session created |
| `session:deleted` | `{ id }` | Session removed |
| `session:output` | `{ id, data }` | Parsed output line (ANSI stripped) |
| `session:terminal` | `{ id, data }` | Raw terminal data with ANSI codes |
| `session:message` | `{ id, message }` | Parsed Claude JSON message |
| `session:running` | `{ id, prompt }` | Prompt execution started |
| `session:interactive` | `{ id }` | Interactive mode started |
| `session:completion` | `{ id, result, cost }` | Prompt completed, includes cost |
| `session:exit` | `{ id, code }` | Session process exited |
| `session:idle` | `{ id }` | Session is idle (prompt detected) |
| `session:working` | `{ id }` | Session is working (activity detected) |
| `session:updated` | `{ session }` | Session state updated |
| `session:error` | `{ id, error }` | Session error occurred |
2026-01-18 07:38:19 +01:00
**Respawn Controller Events:**
| Event | Data | Description |
|-------|------|-------------|
2026-01-18 21:37:03 +01:00
| `respawn:started` | `{ sessionId, status }` | Respawn controller started |
| `respawn:stopped` | `{ sessionId }` | Respawn controller stopped |
| `respawn:stateChanged` | `{ sessionId, state, prevState }` | State machine transition |
| `respawn:cycleStarted` | `{ sessionId, cycleNumber }` | New update cycle starting |
| `respawn:cycleCompleted` | `{ sessionId, cycleNumber }` | Update cycle finished |
| `respawn:stepSent` | `{ sessionId, step, input }` | Command sent (update/clear/init) |
| `respawn:stepCompleted` | `{ sessionId, step }` | Command completed |
| `respawn:configUpdated` | `{ sessionId, config }` | Configuration changed |
| `respawn:log` | `{ sessionId, message }` | Debug/info log message |
| `respawn:error` | `{ sessionId, error }` | Error occurred |
2026-01-18 07:38:19 +01:00
**Scheduled Run Events:**
| Event | Data | Description |
|-------|------|-------------|
2026-01-18 21:37:03 +01:00
| `scheduled:created` | `{ run }` | New scheduled run created |
| `scheduled:updated` | `{ run }` | Status/timer update |
| `scheduled:log` | `{ id, log }` | Scheduled run log entry |
| `scheduled:completed` | `{ run }` | Scheduled run finished |
| `scheduled:stopped` | `{ run }` | Scheduled run stopped by user |
**Case Events:**
| Event | Data | Description |
|-------|------|-------------|
| `case:created` | `{ name, path }` | New case directory created |
**Init Event:**
| Event | Data | Description |
|-------|------|-------------|
| `init` | `{ sessions, scheduledRuns, respawnStatus, timestamp }` | Full state sent on SSE connection |
2026-01-18 07:38:19 +01:00
## API Endpoints
### Session Management
```
2026-01-18 21:37:03 +01:00
GET /api/sessions # List all sessions (includes buffer stats)
2026-01-18 07:38:19 +01:00
POST /api/sessions # Create session { workingDir }
GET /api/sessions/:id # Get single session details
2026-01-18 21:37:03 +01:00
DELETE /api/sessions/:id # Stop and remove a session (kills process + children)
DELETE /api/sessions # Kill all sessions at once
2026-01-18 07:38:19 +01:00
GET /api/sessions/:id/output # Get session output buffer
GET /api/sessions/:id/terminal # Get terminal buffer (raw ANSI)
```
### Session Operations
```
POST /api/sessions/:id/run # Run prompt { prompt } (one-shot mode)
POST /api/sessions/:id/interactive # Start interactive terminal mode
POST /api/sessions/:id/input # Send input to interactive session { input }
POST /api/sessions/:id/resize # Resize terminal { cols, rows }
POST /api/sessions/:id/interactive-respawn # Start interactive + respawn controller
```
### Respawn Controller
```
GET /api/sessions/:id/respawn # Get respawn controller state
POST /api/sessions/:id/respawn/start # Start respawn controller { config? }
POST /api/sessions/:id/respawn/stop # Stop respawn controller
PUT /api/sessions/:id/respawn/config # Update config { idleTimeoutMs, updatePrompt, interStepDelayMs }
```
### Scheduled Runs
```
GET /api/scheduled # List all scheduled runs
POST /api/scheduled # Create { prompt, workingDir, durationMinutes }
GET /api/scheduled/:id # Get scheduled run details
DELETE /api/scheduled/:id # Cancel scheduled run
```
2026-01-18 21:37:03 +01:00
### Cases & Quick Start
2026-01-18 07:38:19 +01:00
```
GET /api/cases # List case directories
POST /api/cases # Create case { name, description }
GET /api/cases/:name # Get case details
2026-01-18 21:37:03 +01:00
POST /api/quick-start # Quick start { caseName? } - creates case + interactive session
2026-01-18 07:38:19 +01:00
POST /api/run # Quick run { prompt, workingDir } (no session management)
```
2026-01-18 21:37:03 +01:00
**Quick Start Response:**
```typescript
{
success : boolean ;
sessionId? : string ; // ID of the created session
casePath? : string ; // Full path to the case directory
caseName? : string ; // Name of the case
error? : string ; // Error message if success is false
}
```
2026-01-18 07:38:19 +01:00
### Events
```
GET /api/events # SSE stream (real-time events)
GET /api/status # Full state snapshot (sessions + scheduled + respawn)
```
2026-01-18 04:50:46 +01:00