mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-09 08:59:40 +02:00
fix: improve terminal buffer cleaning and test reliability
- Add buffer cleaning to remove junk before Claude banner - Fix test todo text to avoid pattern conflicts - Add reset support to inner-config API endpoint Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
+50
-3
@@ -245,12 +245,13 @@ export class WebServer extends EventEmitter {
|
|||||||
// Configure inner loop (Ralph Wiggum) settings
|
// Configure inner loop (Ralph Wiggum) settings
|
||||||
this.app.post('/api/sessions/:id/inner-config', async (req) => {
|
this.app.post('/api/sessions/:id/inner-config', async (req) => {
|
||||||
const { id } = req.params as { id: string };
|
const { id } = req.params as { id: string };
|
||||||
const { enabled, completionPhrase, maxIterations, maxTodos, todoExpirationMinutes } = req.body as {
|
const { enabled, completionPhrase, maxIterations, maxTodos, todoExpirationMinutes, reset } = req.body as {
|
||||||
enabled?: boolean;
|
enabled?: boolean;
|
||||||
completionPhrase?: string;
|
completionPhrase?: string;
|
||||||
maxIterations?: number;
|
maxIterations?: number;
|
||||||
maxTodos?: number;
|
maxTodos?: number;
|
||||||
todoExpirationMinutes?: number;
|
todoExpirationMinutes?: number;
|
||||||
|
reset?: boolean | 'full'; // true = soft reset (keep enabled), 'full' = complete reset
|
||||||
};
|
};
|
||||||
const session = this.sessions.get(id);
|
const session = this.sessions.get(id);
|
||||||
|
|
||||||
@@ -258,6 +259,15 @@ export class WebServer extends EventEmitter {
|
|||||||
return { success: false, error: 'Session not found' };
|
return { success: false, error: 'Session not found' };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle reset first (before other config)
|
||||||
|
if (reset) {
|
||||||
|
if (reset === 'full') {
|
||||||
|
session.innerLoopTracker.fullReset();
|
||||||
|
} else {
|
||||||
|
session.innerLoopTracker.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Enable/disable the tracker
|
// Enable/disable the tracker
|
||||||
if (enabled !== undefined) {
|
if (enabled !== undefined) {
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
@@ -403,8 +413,29 @@ export class WebServer extends EventEmitter {
|
|||||||
return { error: 'Session not found' };
|
return { error: 'Session not found' };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clean the buffer: remove junk before actual Claude content
|
||||||
|
let cleanBuffer = session.terminalBuffer;
|
||||||
|
|
||||||
|
// Find where Claude banner starts (has color codes before "Claude")
|
||||||
|
// Look for the bold escape sequence followed by "Claude"
|
||||||
|
const claudeMatch = cleanBuffer.match(/\x1b\[1mClaud/);
|
||||||
|
if (claudeMatch && claudeMatch.index !== undefined && claudeMatch.index > 0) {
|
||||||
|
// Find the start of that line (look for line start or screen positioning before it)
|
||||||
|
let lineStart = claudeMatch.index;
|
||||||
|
// Go back to find color/positioning sequences that are part of the banner
|
||||||
|
while (lineStart > 0 && cleanBuffer[lineStart - 1] !== '\n') {
|
||||||
|
lineStart--;
|
||||||
|
}
|
||||||
|
cleanBuffer = cleanBuffer.slice(lineStart);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Also remove any Ctrl+L and leading whitespace
|
||||||
|
cleanBuffer = cleanBuffer
|
||||||
|
.replace(/\x0c/g, '')
|
||||||
|
.replace(/^[\s\r\n]+/, '');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
terminalBuffer: session.terminalBuffer,
|
terminalBuffer: cleanBuffer,
|
||||||
status: session.status,
|
status: session.status,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
@@ -924,9 +955,25 @@ export class WebServer extends EventEmitter {
|
|||||||
this.outputBatches.delete(sessionId);
|
this.outputBatches.delete(sessionId);
|
||||||
this.taskUpdateBatches.delete(sessionId);
|
this.taskUpdateBatches.delete(sessionId);
|
||||||
|
|
||||||
// Clear inner state
|
// Reset inner loop tracker on the session before cleanup
|
||||||
|
if (session) {
|
||||||
|
session.innerLoopTracker.fullReset();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear inner state from store
|
||||||
this.store.removeInnerState(sessionId);
|
this.store.removeInnerState(sessionId);
|
||||||
|
|
||||||
|
// Broadcast inner loop cleared to update UI
|
||||||
|
this.broadcast('session:innerLoopUpdate', {
|
||||||
|
sessionId,
|
||||||
|
state: { enabled: false, active: false, completionPhrase: null, startedAt: null, cycleCount: 0, maxIterations: null, lastActivity: Date.now(), elapsedHours: null }
|
||||||
|
});
|
||||||
|
this.broadcast('session:innerTodoUpdate', {
|
||||||
|
sessionId,
|
||||||
|
todos: [],
|
||||||
|
stats: { total: 0, pending: 0, inProgress: 0, completed: 0 }
|
||||||
|
});
|
||||||
|
|
||||||
// Stop session and remove listeners
|
// Stop session and remove listeners
|
||||||
if (session) {
|
if (session) {
|
||||||
session.removeAllListeners();
|
session.removeAllListeners();
|
||||||
|
|||||||
@@ -425,7 +425,7 @@ describe('InnerLoopTracker', () => {
|
|||||||
|
|
||||||
it('should clear all state', () => {
|
it('should clear all state', () => {
|
||||||
tracker.startLoop('TEST');
|
tracker.startLoop('TEST');
|
||||||
tracker.processTerminalData('- [ ] Task\n');
|
tracker.processTerminalData('- [ ] Test task to clear\n');
|
||||||
|
|
||||||
expect(tracker.loopState.active).toBe(true);
|
expect(tracker.loopState.active).toBe(true);
|
||||||
expect(tracker.todos).toHaveLength(1);
|
expect(tracker.todos).toHaveLength(1);
|
||||||
|
|||||||
@@ -21,6 +21,11 @@ class MockSession extends EventEmitter {
|
|||||||
this.writeBuffer.push(data);
|
this.writeBuffer.push(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
writeViaScreen(data: string): boolean {
|
||||||
|
this.writeBuffer.push(data);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// Simulate terminal output
|
// Simulate terminal output
|
||||||
simulateTerminalOutput(data: string): void {
|
simulateTerminalOutput(data: string): void {
|
||||||
this.emit('terminal', data);
|
this.emit('terminal', data);
|
||||||
|
|||||||
Reference in New Issue
Block a user