feat: replace SpawnDetector with MCP server for spawn1337 protocol

Instead of parsing terminal output for <spawn1337> tags, spawn capabilities
are now exposed as native MCP tools that Claude Code can call directly.
The MCP server (stdio transport) proxies requests to the existing REST API.

- Add src/mcp-server.ts with 6 tools: spawn_agent, list_agents,
  get_agent_status, get_agent_result, send_agent_message, cancel_agent
- Remove src/spawn-detector.ts and all references in session.ts/server.ts
- Add CLAUDEMAN_API_URL env var propagation to sessions and screens
- Write .mcp.json to case directories during creation
- Remove spawn1337 tag documentation from case-template.md
- Add claudeman-mcp bin entry to package.json

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
arkon
2026-01-23 23:16:21 +01:00
co-authored by Claude Opus 4.5
parent 7c72802886
commit 99919d6dfb
11 changed files with 1549 additions and 710 deletions
+3 -47
View File
@@ -21,7 +21,6 @@ import * as pty from 'node-pty';
import { SessionState, SessionStatus, SessionConfig, ScreenSession, RalphTrackerState, RalphTodoItem } from './types.js';
import { TaskTracker, type BackgroundTask } from './task-tracker.js';
import { RalphTracker } from './ralph-tracker.js';
import { SpawnDetector } from './spawn-detector.js';
import { ScreenManager } from './screen-manager.js';
export type { BackgroundTask } from './task-tracker.js';
@@ -226,14 +225,6 @@ export interface SessionEvents {
ralphTodoUpdate: (todos: RalphTodoItem[]) => void;
/** Ralph completion phrase detected */
ralphCompletionDetected: (phrase: string) => void;
/** Spawn1337 agent spawn requested */
spawnRequested: (filePath: string, rawLine: string) => void;
/** Spawn1337 agent status query */
spawnStatusRequested: (agentId: string) => void;
/** Spawn1337 agent cancel request */
spawnCancelRequested: (agentId: string) => void;
/** Spawn1337 message to child agent */
spawnMessageToChild: (agentId: string, content: string) => void;
}
/**
@@ -334,9 +325,6 @@ export class Session extends EventEmitter {
// Ralph tracking (Ralph Wiggum loops and todo lists inside Claude Code)
private _ralphTracker: RalphTracker;
// Spawn1337 detection (agent spawning protocol)
private _spawnDetector: SpawnDetector;
// Agent tree tracking
private _parentAgentId: string | null = null;
private _childAgentIds: string[] = [];
@@ -355,13 +343,6 @@ export class Session extends EventEmitter {
completionDetected: (phrase: string) => void;
} | null = null;
private _spawnHandlers: {
spawnRequested: (filePath: string, rawLine: string) => void;
statusRequested: (agentId: string) => void;
cancelRequested: (agentId: string) => void;
messageToChild: (agentId: string, content: string) => void;
} | null = null;
constructor(config: Partial<SessionConfig> & {
workingDir: string;
mode?: SessionMode;
@@ -405,18 +386,6 @@ export class Session extends EventEmitter {
this._ralphTracker.on('todoUpdate', this._ralphHandlers.todoUpdate);
this._ralphTracker.on('completionDetected', this._ralphHandlers.completionDetected);
// Initialize Spawn detector and forward events (store handlers for cleanup)
this._spawnDetector = new SpawnDetector();
this._spawnHandlers = {
spawnRequested: (filePath, rawLine) => this.emit('spawnRequested', filePath, rawLine),
statusRequested: (agentId) => this.emit('spawnStatusRequested', agentId),
cancelRequested: (agentId) => this.emit('spawnCancelRequested', agentId),
messageToChild: (agentId, content) => this.emit('spawnMessageToChild', agentId, content),
};
this._spawnDetector.on('spawnRequested', this._spawnHandlers.spawnRequested);
this._spawnDetector.on('statusRequested', this._spawnHandlers.statusRequested);
this._spawnDetector.on('cancelRequested', this._spawnHandlers.cancelRequested);
this._spawnDetector.on('messageToChild', this._spawnHandlers.messageToChild);
}
get status(): SessionStatus {
@@ -504,11 +473,6 @@ export class Session extends EventEmitter {
return this._ralphTracker.getTodoStats();
}
// Spawn1337 tracking getters
get spawnDetector(): SpawnDetector {
return this._spawnDetector;
}
get parentAgentId(): string | null {
return this._parentAgentId;
}
@@ -785,6 +749,7 @@ export class Session extends EventEmitter {
// Inform Claude it's running within Claudeman (helps prevent self-termination)
CLAUDEMAN_SCREEN: '1',
CLAUDEMAN_SESSION_ID: this.id,
CLAUDEMAN_API_URL: process.env.CLAUDEMAN_API_URL || 'http://localhost:3000',
},
});
}
@@ -809,9 +774,6 @@ export class Session extends EventEmitter {
// Forward to Ralph tracker to detect Ralph loops and todos
this._ralphTracker.processTerminalData(data);
// Forward to Spawn detector to detect spawn1337 protocol tags
this._spawnDetector.processTerminalData(data);
// Parse token count from status line (e.g., "123.4k tokens" or "5234 tokens")
this.parseTokensFromStatusLine(data);
@@ -958,6 +920,7 @@ export class Session extends EventEmitter {
TERM: 'xterm-256color',
CLAUDEMAN_SCREEN: '1',
CLAUDEMAN_SESSION_ID: this.id,
CLAUDEMAN_API_URL: process.env.CLAUDEMAN_API_URL || 'http://localhost:3000',
},
});
}
@@ -1067,6 +1030,7 @@ export class Session extends EventEmitter {
// Inform Claude it's running within Claudeman
CLAUDEMAN_SCREEN: '1',
CLAUDEMAN_SESSION_ID: this.id,
CLAUDEMAN_API_URL: process.env.CLAUDEMAN_API_URL || 'http://localhost:3000',
},
});
@@ -1460,14 +1424,6 @@ export class Session extends EventEmitter {
this._ralphHandlers = null;
}
// Remove SpawnDetector handlers
if (this._spawnHandlers) {
this._spawnDetector.off('spawnRequested', this._spawnHandlers.spawnRequested);
this._spawnDetector.off('statusRequested', this._spawnHandlers.statusRequested);
this._spawnDetector.off('cancelRequested', this._spawnHandlers.cancelRequested);
this._spawnDetector.off('messageToChild', this._spawnHandlers.messageToChild);
this._spawnHandlers = null;
}
}
/**