mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-05 15:09:42 +02:00
feat: track Ralph Wiggum loops and todo lists in Claude sessions
Add InnerLoopTracker to detect and expose internal Claude Code state running inside claudeman sessions by parsing terminal output patterns. Detection patterns: - Completion phrases: <promise>PHRASE</promise> - Todo items: checkbox format, indicator icons, status parentheses - Loop status: cycle counts, elapsed time, start/completion New features: - Inner state panel UI (collapsible, below session tabs) - SSE events: innerLoopUpdate, innerTodoUpdate, innerCompletionDetected - API endpoint: GET /api/sessions/:id/inner-state - State persistence to ~/.claudeman/state-inner.json Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
+41
-1
@@ -2,11 +2,13 @@ import { EventEmitter } from 'node:events';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import * as pty from 'node-pty';
|
||||
import { execSync } from 'node:child_process';
|
||||
import { SessionState, SessionStatus, SessionConfig, ScreenSession } from './types.js';
|
||||
import { SessionState, SessionStatus, SessionConfig, ScreenSession, InnerLoopState, InnerTodoItem } from './types.js';
|
||||
import { TaskTracker, type BackgroundTask } from './task-tracker.js';
|
||||
import { InnerLoopTracker } from './inner-loop-tracker.js';
|
||||
import { ScreenManager } from './screen-manager.js';
|
||||
|
||||
export type { BackgroundTask } from './task-tracker.js';
|
||||
export type { InnerLoopState, InnerTodoItem } from './types.js';
|
||||
|
||||
// Maximum terminal buffer size in characters (default 5MB of text)
|
||||
const MAX_TERMINAL_BUFFER_SIZE = 5 * 1024 * 1024;
|
||||
@@ -64,6 +66,10 @@ export interface SessionEvents {
|
||||
autoClear: (data: { tokens: number; threshold: number }) => void;
|
||||
// Auto-compact event
|
||||
autoCompact: (data: { tokens: number; threshold: number; prompt?: string }) => void;
|
||||
// Inner loop tracking events (Claude Code running inside this session)
|
||||
innerLoopUpdate: (state: InnerLoopState) => void;
|
||||
innerTodoUpdate: (todos: InnerTodoItem[]) => void;
|
||||
innerCompletionDetected: (phrase: string) => void;
|
||||
}
|
||||
|
||||
export type SessionMode = 'claude' | 'shell';
|
||||
@@ -114,6 +120,9 @@ export class Session extends EventEmitter {
|
||||
private _screenSession: ScreenSession | null = null;
|
||||
private _useScreen: boolean = false;
|
||||
|
||||
// Inner loop tracking (Ralph Wiggum loops and todo lists inside Claude Code)
|
||||
private _innerLoopTracker: InnerLoopTracker;
|
||||
|
||||
constructor(config: Partial<SessionConfig> & {
|
||||
workingDir: string;
|
||||
mode?: SessionMode;
|
||||
@@ -137,6 +146,12 @@ export class Session extends EventEmitter {
|
||||
this._taskTracker.on('taskUpdated', (task) => this.emit('taskUpdated', task));
|
||||
this._taskTracker.on('taskCompleted', (task) => this.emit('taskCompleted', task));
|
||||
this._taskTracker.on('taskFailed', (task, error) => this.emit('taskFailed', task, error));
|
||||
|
||||
// Initialize inner loop tracker and forward events
|
||||
this._innerLoopTracker = new InnerLoopTracker();
|
||||
this._innerLoopTracker.on('loopUpdate', (state) => this.emit('innerLoopUpdate', state));
|
||||
this._innerLoopTracker.on('todoUpdate', (todos) => this.emit('innerTodoUpdate', todos));
|
||||
this._innerLoopTracker.on('completionDetected', (phrase) => this.emit('innerCompletionDetected', phrase));
|
||||
}
|
||||
|
||||
get status(): SessionStatus {
|
||||
@@ -207,6 +222,23 @@ export class Session extends EventEmitter {
|
||||
return this._taskTracker.getStats();
|
||||
}
|
||||
|
||||
// Inner loop tracking getters
|
||||
get innerLoopTracker(): InnerLoopTracker {
|
||||
return this._innerLoopTracker;
|
||||
}
|
||||
|
||||
get innerLoopState(): InnerLoopState {
|
||||
return this._innerLoopTracker.loopState;
|
||||
}
|
||||
|
||||
get innerTodos(): InnerTodoItem[] {
|
||||
return this._innerLoopTracker.todos;
|
||||
}
|
||||
|
||||
get innerTodoStats(): { total: number; pending: number; inProgress: number; completed: number } {
|
||||
return this._innerLoopTracker.getTodoStats();
|
||||
}
|
||||
|
||||
// Token tracking getters and setters
|
||||
get totalTokens(): number {
|
||||
return this._totalInputTokens + this._totalOutputTokens;
|
||||
@@ -323,6 +355,10 @@ export class Session extends EventEmitter {
|
||||
enabled: this._autoClearEnabled,
|
||||
threshold: this._autoClearThreshold,
|
||||
},
|
||||
// Inner loop tracking state
|
||||
innerLoop: this._innerLoopTracker.loopState,
|
||||
innerTodos: this._innerLoopTracker.todos,
|
||||
innerTodoStats: this._innerLoopTracker.getTodoStats(),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -408,6 +444,9 @@ export class Session extends EventEmitter {
|
||||
this.emit('terminal', data);
|
||||
this.emit('output', data);
|
||||
|
||||
// Forward to inner loop tracker to detect Ralph loops and todos
|
||||
this._innerLoopTracker.processTerminalData(data);
|
||||
|
||||
// Parse token count from status line (e.g., "123.4k tokens" or "5234 tokens")
|
||||
this.parseTokensFromStatusLine(data);
|
||||
|
||||
@@ -996,5 +1035,6 @@ export class Session extends EventEmitter {
|
||||
this._errorBuffer = '';
|
||||
this._messages = [];
|
||||
this._taskTracker.clear();
|
||||
this._innerLoopTracker.clear();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user