Files
Codeman/CLAUDE.md
T
arkonandClaude Opus 4.5 2c38f7924a feat(tracker): disable Ralph Wiggum tracker by default, auto-enable on detection
The InnerLoopTracker is now disabled by default and auto-enables when
Ralph-related patterns are detected:
- /ralph-loop command
- <promise>PHRASE</promise> completion phrases
- TodoWrite tool usage
- Iteration patterns (Iteration 5/50, [5/50])
- Todo checkboxes (- [ ]/- [x]) or indicator icons

This reduces noise for sessions that don't use Ralph loops while
maintaining full functionality when loops are detected.

Changes:
- Add `enabled` property to InnerLoopState type
- Add enable()/disable() methods to InnerLoopTracker
- Implement shouldAutoEnable() for pattern detection
- Update frontend to show "Tracking" status when enabled
- Add CSS for tracking state indicator
- Update CLAUDE.md documentation
- Add comprehensive tests for auto-enable behavior

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 18:34:43 +01:00

13 KiB
Raw Blame History

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

GOTCHA: npm run dev runs CLI help, NOT the web server. Always use npx tsx src/index.ts web for development.

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

# 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 ports 3101-3108 to avoid conflicts with dev server (3000)
# Test timeout: 30s (configured in vitest.config.ts for integration tests)

# TypeScript checking (no linter configured)
npx tsc --noEmit                          # Type check without building

# 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.ts               # Task class implementation
├── task-queue.ts         # Priority queue with dependencies
├── task-tracker.ts       # Background task detection from terminal output
├── inner-loop-tracker.ts # Detect Ralph loops and todos inside Claude sessions
├── 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

test/                             # All tests use vitest, ports 3101-3108
├── session.test.ts               # Core session creation, lifecycle, PTY behavior
├── pty-interactive.test.ts       # Interactive mode, terminal input/output
├── respawn-controller.test.ts    # Respawn state machine, idle detection
├── inner-loop-tracker.test.ts    # Ralph loop and todo detection parsing
├── quick-start.test.ts           # Quick-start API endpoint
├── scheduled-runs.test.ts        # Timed/scheduled session runs
├── sse-events.test.ts            # Server-Sent Events broadcasting
├── integration-flows.test.ts     # Multi-step workflow tests
├── session-cleanup.test.ts       # Resource cleanup, buffer trimming
└── edge-cases.test.ts            # Error handling, boundary conditions

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
  4. State persists to ~/.claudeman/state.json via StateStore

Key Components

Component File Purpose
Session session.ts PTY wrapper for Claude CLI. Modes: runPrompt(), startInteractive(), startShell()
RespawnController respawn-controller.ts State machine for autonomous session cycling (see diagram below)
ScreenManager screen-manager.ts GNU screen persistence, ghost discovery, 4-strategy kill
WebServer web/server.ts Fastify REST + SSE at /api/events
InnerLoopTracker inner-loop-tracker.ts Detects <promise>PHRASE</promise>, todos, loop status in output

Respawn State Machine

WATCHING → SENDING_UPDATE → WAITING_UPDATE → SENDING_CLEAR → WAITING_CLEAR → SENDING_INIT → WAITING_INIT → WATCHING
    ↑                                                                                                          |
    └──────────────────────────────────────────────────────────────────────────────────────────────────────────┘

Steps can be skipped via config (sendClear: false, sendInit: false). Idle detection triggers state transitions.

Session Modes

Sessions have a mode property (SessionMode type):

  • 'claude': Runs Claude CLI for AI interactions (default)
  • 'shell': Runs a plain bash shell for debugging/testing

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'], { ... })

// Shell mode (debugging/testing - no Claude CLI)
pty.spawn('bash', [], { ... })

Screen Input for Ink/Claude CLI

Claude CLI uses Ink (React for terminals) which has specific input handling. When sending programmatic input via screen -X stuff:

IMPORTANT: Text and Enter key MUST be sent as separate commands:

# Correct - two separate commands
screen -S claudeman-xxx -p 0 -X stuff "hello world"
screen -S claudeman-xxx -p 0 -X stuff "$(printf '\015')"

# Wrong - doesn't work with Ink
screen -S claudeman-xxx -p 0 -X stuff "$(printf 'hello world\015')"

The \015 (octal) is carriage return (ASCII 13), which Ink interprets as key.return for submission. Use writeViaScreen() method for programmatic input that needs Enter key.

Idle Detection

RespawnController: Primary ↵ send indicator, fallback prompt chars (❯, ⏵) + 10s timeout. Working patterns: Thinking, Writing, Running. Session: emits idle/working events on prompt detection + 2s activity timeout.

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

Auto-Compact & Auto-Clear

Feature Default Threshold Action
Auto-Compact 110k tokens /compact with optional prompt
Auto-Clear 140k tokens /clear to reset context

Both wait for idle. Configure via session.setAutoCompact() / session.setAutoClear().

Inner Loop Tracking

Detects Ralph loops and todos inside Claude sessions. Disabled by default - auto-enables when Ralph-related patterns are detected:

  • /ralph-loop command
  • <promise>PHRASE</promise> completion phrases
  • TodoWrite tool usage
  • Iteration patterns (Iteration 5/50, [5/50])
  • Todo checkboxes (- [ ]/- [x]) or indicator icons (☐/◐/✓)

API: GET /api/sessions/:id/inner-state. UI: collapsible panel below tabs, shows "Tracking" status when enabled. Use tracker.enable() / tracker.disable() for manual control.

Terminal Display Fix

Tab switch/new session fix: clear xterm → write buffer → resize PTY → Ctrl+L redraw. Uses pendingCtrlL Set, triggered on session:idle/session:working events.

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, session:innerLoopUpdate, session:innerTodoUpdate, session:innerCompletionDetected.

Frontend (app.js)

Vanilla JS + xterm.js. handleSSEEvent() dispatches events, switchToSession() manages tabs. 60fps: server batches 16ms, client uses requestAnimationFrame.

State Store

Writes debounced (100ms) to ~/.claudeman/state.json. Batches rapid changes.

Adding New Features

New API Endpoint

  1. Add types to src/types.ts
  2. Add route in src/web/server.ts within buildServer()
  3. Use createErrorResponse() for errors

New SSE Event

  1. Emit from component via broadcast() in server.ts
  2. Handle in src/web/public/app.js handleSSEEvent() switch

New Session Event

  1. Add to SessionEvents interface in src/session.ts
  2. Emit via this.emit()
  3. Subscribe in src/web/server.ts when wiring session to SSE
  4. Handle in frontend SSE listener

Session Lifecycle & Cleanup

  • Limit: MAX_CONCURRENT_SESSIONS = 50
  • Kill (killScreen()): child PIDs → process group → screen quit → SIGKILL
  • Ghost discovery: reconcileScreens() finds orphaned screens on startup
  • Cleanup (cleanupSession()): stops respawn, clears buffers/timers, 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

Uses agent-browser for web UI automation. Full test plan: .claude/skills/e2e-test.md

npx agent-browser open http://localhost:3000 && npx agent-browser snapshot

API Routes Quick Reference

Method Endpoint Description
GET /api/events SSE stream for real-time updates
GET /api/status Full application state
GET/POST/DELETE /api/sessions List/create/kill-all sessions
GET/DELETE /api/sessions/:id Get/delete specific session
POST /api/sessions/:id/input Send input to session PTY
POST /api/sessions/:id/resize Resize terminal (cols, rows)
POST /api/sessions/:id/interactive Start interactive mode
POST /api/sessions/:id/respawn/start Start respawn controller
POST /api/sessions/:id/respawn/stop Stop respawn controller
POST /api/sessions/:id/respawn/enable Enable respawn with config + optional timer
POST /api/sessions/:id/inner-config Configure Ralph Wiggum loop settings
GET /api/sessions/:id/inner-state Get Ralph loop state + todos
POST /api/sessions/:id/auto-compact Configure auto-compact threshold
POST /api/sessions/:id/auto-clear Configure auto-clear threshold
POST /api/quick-start Create case + start interactive session
GET /api/cases List available cases
POST /api/cases Create new case
GET /api/screens List screen sessions with stats

Notes

  • State persists to ~/.claudeman/state.json, ~/.claudeman/state-inner.json, and ~/.claudeman/screens.json
  • Inner loop/todo state persists separately in state-inner.json to reduce write frequency
  • Cases created in ~/claudeman-cases/ by default