Kickstart Prompt: - Remove Idle Timeout setting from respawn UI (uses 10s default) - Add Kickstart Prompt field sent when /init doesn't trigger work - New states: monitoring_init, sending_kickstart, waiting_kickstart - Monitors 3s after /init to detect if Claude started working Auto-Compact Context: - Add auto-compact at 110k tokens (sends /compact with optional prompt) - Preserves summarized context unlike /clear which wipes everything - Optional focus prompt to guide what compact should preserve - Blocks auto-clear while compacting to prevent conflicts Also updates auto-clear default threshold from 100k to 140k tokens. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
11 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Project Overview
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, strict mode), Node.js, Fastify, Server-Sent Events, node-pty
Requirements: Node.js 18+, Claude CLI (claude) installed and available in PATH
Commands
npm run build # Compile TypeScript + copy static files to dist/web/
npm run clean # Remove dist/
# Start web server (pick one):
npx tsx src/index.ts web # Dev mode - no build needed (RECOMMENDED)
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
# ⚠️ GOTCHA: `npm run dev` runs CLI help, NOT the web server!
# Always use `npx tsx src/index.ts web` for development
# Testing (vitest - tests run against WebServer, 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
# Tests use different ports (3101-3108) to avoid conflicts
# 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 (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 persistence + process stats
├── respawn-controller.ts # Auto-respawn state machine
├── ralph-loop.ts # Autonomous task assignment
├── task-queue.ts # Priority queue with dependencies
├── task-tracker.ts # Background task detection from terminal output
├── state-store.ts # Persistence to ~/.claudeman/state.json
├── types.ts # All TypeScript interfaces
├── web/
│ ├── server.ts # Fastify REST API + SSE + session restoration
│ └── 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
Data Flow
- Session spawns
claude -p --dangerously-skip-permissionsvianode-pty - PTY output is buffered, ANSI stripped, and parsed for JSON messages
- WebServer broadcasts events to SSE clients at
/api/events - State persists to
~/.claudeman/state.jsonvia StateStore
Key Components
-
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,autoCompact,clearTerminal. -
RespawnController (
src/respawn-controller.ts): State machine that keeps sessions productive. Detects idle → sends update prompt → optionally/clear→ optionally/init→ optionally kickstart prompt → repeats. State flow:WATCHING → SENDING_UPDATE → WAITING_UPDATE → SENDING_CLEAR → WAITING_CLEAR → SENDING_INIT → WAITING_INIT → MONITORING_INIT → (optional) SENDING_KICKSTART → WAITING_KICKSTART → WATCHING -
ScreenManager (
src/screen-manager.ts): Wraps sessions in GNU screen for persistence across server restarts. On startup, reconciles withscreen -lsto restore sessions and discovers unknown "ghost" screens. Uses 4-strategy kill process to prevent orphaned claude processes. -
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)): Single prompt execution, emits completion, exits - Interactive (
startInteractive()): Persistent PTY terminal with resize support - Shell (
startShell()): Plain bash/zsh terminal without Claude
Code Patterns
Pre-compiled Regex Patterns
For performance, regex patterns that are used frequently should be compiled once at module level:
// Good - compile once
const ANSI_ESCAPE_PATTERN = /\x1b\[[0-9;]*m/g;
const TOKEN_PATTERN = /(\d+(?:\.\d+)?)\s*([kKmM])?\s*tokens/;
// Bad - recompiles on each call
function parse(line: string) {
return line.replace(/\x1b\[[0-9;]*m/g, '');
}
Claude Message Parsing
Claude CLI outputs newline-delimited JSON. Strip ANSI codes before parsing:
const cleanLine = line.replace(ANSI_ESCAPE_PATTERN, '');
const msg = JSON.parse(cleanLine) as ClaudeMessage;
// msg.type: 'system' | 'assistant' | 'user' | 'result'
// msg.message?.content: Array<{ type: 'text', text: string }>
// msg.total_cost_usd: number (on result messages)
PTY Spawn Modes
// One-shot mode (JSON output for token tracking)
pty.spawn('claude', ['-p', '--dangerously-skip-permissions', '--output-format', 'stream-json', prompt], { ... })
// Interactive mode (tokens parsed from status line)
pty.spawn('claude', ['--dangerously-skip-permissions'], { ... })
Idle Detection
RespawnController uses a hybrid approach:
- Primary: Looks for
↵ sendindicator (Claude's suggestion prompt) - Fallback: Prompt characters (
❯,\u276f,⏵) + timeout (10s default) - Working detection: Patterns like
Thinking,Writing,Running, etc.
Session emits idle and working events based on prompt detection and activity timeout (2s after prompt character).
Token Tracking
- One-shot mode: Uses
--output-format stream-jsonfor 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
Auto-Compact & Auto-Clear
Sessions support automatic context management when token thresholds are reached:
- Auto-Compact (default: 110k tokens): Sends
/compactcommand with optional prompt to summarize context - Auto-Clear (default: 140k tokens): Sends
/clearto reset context entirely
Both wait for Claude to be idle before executing. Auto-compact runs first if both are enabled. Configured via session.setAutoCompact(enabled, threshold?, prompt?) and session.setAutoClear(enabled, threshold?).
Terminal Display Fix (Tab Switch & New Session)
When switching tabs or creating new sessions, terminal may be rendered at wrong size. Fix sequence:
- Clear and reset xterm
- Write terminal buffer
- Send resize to update PTY dimensions
- 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:, screen:, init. Key events: session:idle, session:working, session:terminal, session:clearTerminal, session:completion, session:autoClear, session:autoCompact.
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
State Store Debouncing
State writes to ~/.claudeman/state.json are debounced (100ms) to prevent excessive disk I/O during rapid updates. The StateStore class batches rapid state changes and writes once after activity settles.
Adding New Features
New API Endpoint
- Add types to
src/types.ts - Add route in
src/web/server.tswithinbuildServer() - Use
createErrorResponse()for errors
New SSE Event
- Emit from component via
broadcast()in server.ts - Handle in
src/web/public/app.jshandleSSEEvent()switch
New Session Event
- Add to
SessionEventsinterface insrc/session.ts - Emit via
this.emit() - Subscribe in
src/web/server.tswhen wiring session to SSE - Handle in frontend SSE listener
Session Lifecycle & Cleanup
Session Limits
MAX_CONCURRENT_SESSIONS = 50prevents unbounded session creation- Limit enforced on
/api/sessions,/api/run,/api/quick-start, and scheduled runs
Kill Process (4 Strategies)
When killing a screen session, killScreen() uses multiple strategies to ensure no orphaned processes:
- Child PIDs: Recursively find and kill all child processes (SIGTERM then SIGKILL)
- Process Group: Kill entire process group (
kill -TERM -$PID) to catch orphans - Screen Name:
screen -S <name> -X quitto cleanly terminate screen - Direct Kill: SIGKILL the screen PID as final fallback
Ghost Screen Discovery
On server startup, reconcileScreens() discovers unknown claudeman screens from screen -ls output. This prevents "ghost" screens that persist if screens.json is lost or corrupted.
Cleanup Function
cleanupSession() in server.ts provides comprehensive cleanup:
- Stops and removes respawn controller + listeners
- Clears respawn timers
- Clears terminal/output/task batches
- Removes session event listeners
- Stops session and kills screen
Buffer Limits
Long-running sessions are supported with automatic trimming:
| Buffer | Max Size | Trim To |
|---|---|---|
| Terminal | 5MB | 4MB |
| Text output | 2MB | 1.5MB |
| Messages | 1000 | 800 |
| Line buffer | 64KB | (flushed every 100ms) |
| Respawn buffer | 1MB | 512KB |
E2E Testing (agent-browser)
Browser automation for testing the web UI. See README.md for full setup.
# Quick test sequence
npx agent-browser open http://localhost:3000
npx agent-browser wait --load networkidle
npx agent-browser snapshot
npx agent-browser find text "Run Claude" click
npx agent-browser wait 2000
npx agent-browser snapshot
npx agent-browser close
Full test plan available at .claude/skills/e2e-test.md.
Notes
- State persists to
~/.claudeman/state.jsonand~/.claudeman/screens.json - Cases created in
~/claudeman-cases/by default