From 89a172333366e20af28cfd3e2bd09a1edbc8ef56 Mon Sep 17 00:00:00 2001 From: arkon Date: Tue, 27 Jan 2026 17:55:45 +0100 Subject: [PATCH] chore: bump version to 0.1398 --- CLAUDE.md | 10 +++++++--- package.json | 2 +- src/web/public/app.js | 10 ++++++++-- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index fd883952..e9f7f226 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -38,7 +38,7 @@ Claudeman is a Claude Code session manager with a web interface and autonomous R When adding new features, always ask: "Will this maintain responsiveness with 20 sessions and 50 agent windows?" -**Version**: 0.1397 (must match `package.json`) +**Version**: 0.1398 (must match `package.json`) **Tech Stack**: TypeScript (ES2022/NodeNext, strict mode), Node.js, Fastify, Server-Sent Events, node-pty @@ -578,12 +578,14 @@ Two methods: 2. **`session.writeViaScreen(data)`** - Via GNU screen (RECOMMENDED for programmatic input). Used by RespawnController, auto-compact, auto-clear. **How `writeViaScreen` works internally** (in `screen-manager.ts:sendInput`): -1. Splits input into text and `\r` (carriage return) +1. Strips all `\n` newlines and `\r` carriage returns from text 2. Sends text first: `screen -S name -p 0 -X stuff "text"` 3. Sends Enter separately: `screen -S name -p 0 -X stuff "$(printf '\015')"` **Why separate commands?** Claude CLI uses Ink (React for terminals) which requires text and Enter as separate `screen -X stuff` commands. Combining them doesn't work. This is a critical implementation detail when debugging input issues. +**IMPORTANT**: All prompts sent via `writeViaScreen` (respawn updatePrompt, kickstartPrompt, auto-compact, etc.) must be **single-line**. Newlines are stripped, so multi-line prompts become one long line. + ### Idle Detection **Session**: emits `idle`/`working` events on prompt detection + 2s activity timeout. @@ -844,4 +846,6 @@ The `RalphTracker` class (`src/ralph-tracker.ts`) detects Ralph patterns in Clau 3. Optionally generates a task plan (`@fix_plan.md`) 4. Sends the initial prompt with iteration protocol -**IMPORTANT**: The respawn controller is **disabled by default** for Ralph Loops. The "Enable Respawn" checkbox in Advanced Options is unchecked by default. Ralph Loops handle their own iteration protocol via `` tags and do not need the respawn controller's autonomous session cycling. +**Respawn for Ralph Loops**: Disabled by default (checkbox unchecked). When enabled, the respawn controller uses Ralph-specific prompts: +- **Update Prompt**: Instructs Claude to document progress to CLAUDE.md, update planning files (`@fix_plan.md`), mark completed tasks, and write a summary before `/clear` +- **Kickstart Prompt**: After `/init`, tells Claude it's in a Ralph Wiggum loop and to continue work by reading `@fix_plan.md` and CLAUDE.md notes, then resume on uncompleted tasks diff --git a/package.json b/package.json index 8c122f8c..8c83f872 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "claudeman", - "version": "0.1397", + "version": "0.1398", "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/web/public/app.js b/src/web/public/app.js index 947f9a1e..6802b86f 100644 --- a/src/web/public/app.js +++ b/src/web/public/app.js @@ -3255,15 +3255,21 @@ class ClaudemanApp { } } - // Step 3: Enable respawn if requested + // Step 3: Enable respawn if requested (with Ralph-specific prompts) + // NOTE: Prompts must be single-line because screen-manager.ts strips newlines if (config.enableRespawn) { + const ralphUpdatePrompt = 'Before /clear: Update CLAUDE.md with discoveries and notes, mark completed tasks in @fix_plan.md, write a brief progress summary to a file so the next iteration can continue seamlessly.'; + + const ralphKickstartPrompt = `You are in a Ralph Wiggum loop. Read @fix_plan.md for task status, check CLAUDE.md for notes from previous iterations, continue on the next uncompleted task, output ${config.completionPhrase} when ALL tasks are complete.`; + await fetch(`/api/sessions/${sessionId}/respawn/enable`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ - prompt: 'continue', + updatePrompt: ralphUpdatePrompt, sendClear: true, sendInit: true, + kickstartPrompt: ralphKickstartPrompt, }) }); }