mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-06 23:49:41 +02:00
refactor: rename inner-loop-tracker to ralph-tracker with API improvements
- Rename inner-loop-tracker.ts → ralph-tracker.ts throughout codebase - Add ralph-config.ts for parsing .claude/ralph-loop.local.md config - Standardize API error responses using createErrorResponse() - Add input validation for auto-compact/auto-clear thresholds - Update UI labels to "Ralph / Todo Tracker" consistently - Add 46 integration tests for Ralph tracking functionality - Update test badge to 438 total tests Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
+46
-46
@@ -8,7 +8,7 @@
|
||||
* - `state.json`: Main app state (sessions, tasks, config)
|
||||
* - `state-inner.json`: Inner loop state (todos, Ralph loop state per session)
|
||||
*
|
||||
* The separation reduces write frequency since inner loop state changes rapidly
|
||||
* The separation reduces write frequency since Ralph state changes rapidly
|
||||
* during Ralph Wiggum loops.
|
||||
*
|
||||
* @module state-store
|
||||
@@ -17,7 +17,7 @@
|
||||
import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'node:fs';
|
||||
import { homedir } from 'node:os';
|
||||
import { dirname, join } from 'node:path';
|
||||
import { AppState, createInitialState, InnerSessionState, createInitialInnerSessionState } from './types.js';
|
||||
import { AppState, createInitialState, RalphSessionState, createInitialRalphSessionState } from './types.js';
|
||||
|
||||
/** Debounce delay for batching state writes (ms) */
|
||||
const SAVE_DEBOUNCE_MS = 500;
|
||||
@@ -50,17 +50,17 @@ export class StateStore {
|
||||
private dirty: boolean = false;
|
||||
|
||||
// Inner state storage (separate from main state to reduce write frequency)
|
||||
private innerStates: Map<string, InnerSessionState> = new Map();
|
||||
private innerStatePath: string;
|
||||
private innerStateSaveTimeout: NodeJS.Timeout | null = null;
|
||||
private innerStateDirty: boolean = false;
|
||||
private ralphStates: Map<string, RalphSessionState> = new Map();
|
||||
private ralphStatePath: string;
|
||||
private ralphStateSaveTimeout: NodeJS.Timeout | null = null;
|
||||
private ralphStateDirty: boolean = false;
|
||||
|
||||
constructor(filePath?: string) {
|
||||
this.filePath = filePath || join(homedir(), '.claudeman', 'state.json');
|
||||
this.innerStatePath = this.filePath.replace('.json', '-inner.json');
|
||||
this.ralphStatePath = this.filePath.replace('.json', '-inner.json');
|
||||
this.state = this.load();
|
||||
this.state.config.stateFilePath = this.filePath;
|
||||
this.loadInnerStates();
|
||||
this.loadRalphStates();
|
||||
}
|
||||
|
||||
private ensureDir(): void {
|
||||
@@ -203,20 +203,20 @@ export class StateStore {
|
||||
reset(): void {
|
||||
this.state = createInitialState();
|
||||
this.state.config.stateFilePath = this.filePath;
|
||||
this.innerStates.clear();
|
||||
this.ralphStates.clear();
|
||||
this.saveNow(); // Immediate save for reset operations
|
||||
this.saveInnerStatesNow();
|
||||
this.saveRalphStatesNow();
|
||||
}
|
||||
|
||||
// ========== Inner State Methods (Ralph Loop tracking) ==========
|
||||
|
||||
private loadInnerStates(): void {
|
||||
private loadRalphStates(): void {
|
||||
try {
|
||||
if (existsSync(this.innerStatePath)) {
|
||||
const data = readFileSync(this.innerStatePath, 'utf-8');
|
||||
const parsed = JSON.parse(data) as Record<string, InnerSessionState>;
|
||||
if (existsSync(this.ralphStatePath)) {
|
||||
const data = readFileSync(this.ralphStatePath, 'utf-8');
|
||||
const parsed = JSON.parse(data) as Record<string, RalphSessionState>;
|
||||
for (const [sessionId, state] of Object.entries(parsed)) {
|
||||
this.innerStates.set(sessionId, state);
|
||||
this.ralphStates.set(sessionId, state);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
@@ -225,43 +225,43 @@ export class StateStore {
|
||||
}
|
||||
|
||||
// Debounced save for inner states
|
||||
private saveInnerStates(): void {
|
||||
this.innerStateDirty = true;
|
||||
if (this.innerStateSaveTimeout) {
|
||||
private saveRalphStates(): void {
|
||||
this.ralphStateDirty = true;
|
||||
if (this.ralphStateSaveTimeout) {
|
||||
return; // Already scheduled
|
||||
}
|
||||
this.innerStateSaveTimeout = setTimeout(() => {
|
||||
this.saveInnerStatesNow();
|
||||
this.ralphStateSaveTimeout = setTimeout(() => {
|
||||
this.saveRalphStatesNow();
|
||||
}, SAVE_DEBOUNCE_MS);
|
||||
}
|
||||
|
||||
// Immediate save for inner states
|
||||
private saveInnerStatesNow(): void {
|
||||
if (this.innerStateSaveTimeout) {
|
||||
clearTimeout(this.innerStateSaveTimeout);
|
||||
this.innerStateSaveTimeout = null;
|
||||
private saveRalphStatesNow(): void {
|
||||
if (this.ralphStateSaveTimeout) {
|
||||
clearTimeout(this.ralphStateSaveTimeout);
|
||||
this.ralphStateSaveTimeout = null;
|
||||
}
|
||||
if (!this.innerStateDirty) {
|
||||
if (!this.ralphStateDirty) {
|
||||
return;
|
||||
}
|
||||
this.innerStateDirty = false;
|
||||
this.ralphStateDirty = false;
|
||||
this.ensureDir();
|
||||
const data: Record<string, InnerSessionState> = {};
|
||||
for (const [sessionId, state] of this.innerStates) {
|
||||
const data: Record<string, RalphSessionState> = {};
|
||||
for (const [sessionId, state] of this.ralphStates) {
|
||||
data[sessionId] = state;
|
||||
}
|
||||
writeFileSync(this.innerStatePath, JSON.stringify(data, null, 2), 'utf-8');
|
||||
writeFileSync(this.ralphStatePath, JSON.stringify(data, null, 2), 'utf-8');
|
||||
}
|
||||
|
||||
/** Returns inner state for a session, or null if not found. */
|
||||
getInnerState(sessionId: string): InnerSessionState | null {
|
||||
return this.innerStates.get(sessionId) || null;
|
||||
getRalphState(sessionId: string): RalphSessionState | null {
|
||||
return this.ralphStates.get(sessionId) || null;
|
||||
}
|
||||
|
||||
/** Sets inner state for a session and triggers a debounced save. */
|
||||
setInnerState(sessionId: string, state: InnerSessionState): void {
|
||||
this.innerStates.set(sessionId, state);
|
||||
this.saveInnerStates();
|
||||
setRalphState(sessionId: string, state: RalphSessionState): void {
|
||||
this.ralphStates.set(sessionId, state);
|
||||
this.saveRalphStates();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -269,34 +269,34 @@ export class StateStore {
|
||||
* Creates initial state if none exists.
|
||||
* @returns The updated inner state.
|
||||
*/
|
||||
updateInnerState(sessionId: string, updates: Partial<InnerSessionState>): InnerSessionState {
|
||||
let state = this.innerStates.get(sessionId);
|
||||
updateRalphState(sessionId: string, updates: Partial<RalphSessionState>): RalphSessionState {
|
||||
let state = this.ralphStates.get(sessionId);
|
||||
if (!state) {
|
||||
state = createInitialInnerSessionState(sessionId);
|
||||
state = createInitialRalphSessionState(sessionId);
|
||||
}
|
||||
state = { ...state, ...updates, lastUpdated: Date.now() };
|
||||
this.innerStates.set(sessionId, state);
|
||||
this.saveInnerStates();
|
||||
this.ralphStates.set(sessionId, state);
|
||||
this.saveRalphStates();
|
||||
return state;
|
||||
}
|
||||
|
||||
/** Removes inner state for a session and triggers a debounced save. */
|
||||
removeInnerState(sessionId: string): void {
|
||||
if (this.innerStates.has(sessionId)) {
|
||||
this.innerStates.delete(sessionId);
|
||||
this.saveInnerStates();
|
||||
removeRalphState(sessionId: string): void {
|
||||
if (this.ralphStates.has(sessionId)) {
|
||||
this.ralphStates.delete(sessionId);
|
||||
this.saveRalphStates();
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns a copy of all inner states as a Map. */
|
||||
getAllInnerStates(): Map<string, InnerSessionState> {
|
||||
return new Map(this.innerStates);
|
||||
getAllRalphStates(): Map<string, RalphSessionState> {
|
||||
return new Map(this.ralphStates);
|
||||
}
|
||||
|
||||
/** Flushes all pending saves (main and inner state). Call before shutdown. */
|
||||
flushAll(): void {
|
||||
this.saveNow();
|
||||
this.saveInnerStatesNow();
|
||||
this.saveRalphStatesNow();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user