diff --git a/CLAUDE.md b/CLAUDE.md index dc5b7243..df63f292 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -17,7 +17,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co 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. -**Version**: 0.1341 +**Version**: 0.1342 **Tech Stack**: TypeScript (ES2022/NodeNext, strict mode), Node.js, Fastify, Server-Sent Events, node-pty diff --git a/package.json b/package.json index c3e269fd..a0cf60a3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "claudeman", - "version": "0.1341", + "version": "0.1342", "description": "The missing control plane for Claude Code - run 20 autonomous agents with real-time monitoring and session persistence", "type": "module", "main": "dist/index.js", diff --git a/src/ai-idle-checker.ts b/src/ai-idle-checker.ts index 3bcb1696..edb74c1c 100644 --- a/src/ai-idle-checker.ts +++ b/src/ai-idle-checker.ts @@ -143,6 +143,7 @@ export class AiIdleChecker extends EventEmitter { // Active check state private checkScreenName: string | null = null; private checkTempFile: string | null = null; + private checkPromptFile: string | null = null; private checkPollTimer: NodeJS.Timeout | null = null; private checkTimeoutTimer: NodeJS.Timeout | null = null; private checkStartTime: number = 0; @@ -312,21 +313,25 @@ export class AiIdleChecker extends EventEmitter { // Build the prompt const prompt = AI_CHECK_PROMPT.replace('{TERMINAL_BUFFER}', trimmed); - // Generate temp file and screen name + // Generate temp files and screen name const shortId = this.sessionId.slice(0, 8); const timestamp = Date.now(); this.checkTempFile = join(tmpdir(), `claudeman-aicheck-${shortId}-${timestamp}.txt`); + this.checkPromptFile = join(tmpdir(), `claudeman-aicheck-prompt-${shortId}-${timestamp}.txt`); this.checkScreenName = `claudeman-aicheck-${shortId}`; - // Ensure temp file exists (empty) so we can poll it + // Ensure output temp file exists (empty) so we can poll it writeFileSync(this.checkTempFile, ''); - // Build the command - escape the prompt for shell - const escapedPrompt = prompt.replace(/'/g, "'\\''"); + // Write prompt to file to avoid E2BIG error (argument list too long) + // The prompt can be 16KB+ which exceeds shell argument limits + writeFileSync(this.checkPromptFile, prompt); + + // Build the command - read prompt from file via stdin to avoid argument size limits const modelArg = `--model ${this.config.model}`; const augmentedPath = getAugmentedPath(); - const claudeCmd = `claude -p ${modelArg} --output-format text '${escapedPrompt}'`; - const fullCmd = `export PATH="${augmentedPath}"; ${claudeCmd} > "${this.checkTempFile}" 2>&1; echo "${DONE_MARKER}" >> "${this.checkTempFile}"`; + const claudeCmd = `cat "${this.checkPromptFile}" | claude -p ${modelArg} --output-format text`; + const fullCmd = `export PATH="${augmentedPath}"; ${claudeCmd} > "${this.checkTempFile}" 2>&1; echo "${DONE_MARKER}" >> "${this.checkTempFile}"; rm -f "${this.checkPromptFile}"`; // Spawn screen try { @@ -430,7 +435,7 @@ export class AiIdleChecker extends EventEmitter { this.checkScreenName = null; } - // Delete temp file + // Delete temp files if (this.checkTempFile) { try { if (existsSync(this.checkTempFile)) { @@ -441,6 +446,17 @@ export class AiIdleChecker extends EventEmitter { } this.checkTempFile = null; } + + if (this.checkPromptFile) { + try { + if (existsSync(this.checkPromptFile)) { + unlinkSync(this.checkPromptFile); + } + } catch { + // Best effort cleanup + } + this.checkPromptFile = null; + } } private handleError(errorMsg: string): void {