From faa6f5d2891753ff4cdc9076b4b220db6de7d641 Mon Sep 17 00:00:00 2001 From: arkon Date: Mon, 19 Jan 2026 16:34:36 +0100 Subject: [PATCH] fix(screen): send text and Enter key as separate commands Claude CLI (Ink) doesn't process carriage return correctly when sent together with text via screen -X stuff. Splitting into two separate commands works reliably: 1. screen -X stuff "text" 2. screen -X stuff "$(printf '\015')" Also adds writeViaScreen() method to Session for programmatic input that bypasses PTY and uses screen -X stuff directly. Co-Authored-By: Claude Opus 4.5 --- src/screen-manager.ts | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/src/screen-manager.ts b/src/screen-manager.ts index 1e87141a..4cc1c21a 100644 --- a/src/screen-manager.ts +++ b/src/screen-manager.ts @@ -468,28 +468,26 @@ export class ScreenManager extends EventEmitter { try { // Split input into text and control characters - // Use printf to generate control characters (octal \015 = carriage return) - // This is more reliable than bash $'...' escaping for Ink/Claude CLI + // IMPORTANT: Must send text and carriage return as SEPARATE commands + // Sending them together doesn't work with Ink/Claude CLI const hasCarriageReturn = input.includes('\r'); const textPart = input.replace(/\r/g, '').replace(/\n/g, ''); // Escape the text part for shell (double quotes) const escapedText = textPart.replace(/"/g, '\\"').replace(/\$/g, '\\$').replace(/`/g, '\\`'); - // Build command: send text, then carriage return via printf - let cmd: string; + // Send text first (if any) + if (escapedText) { + const textCmd = `screen -S ${screen.screenName} -p 0 -X stuff "${escapedText}"`; + execSync(textCmd, { encoding: 'utf-8', timeout: 5000 }); + } + + // Send carriage return separately (Enter key for Ink) if (hasCarriageReturn) { - // Send text + carriage return (Enter key for Ink) - cmd = `screen -S ${screen.screenName} -p 0 -X stuff "$(printf '${escapedText}\\015')"`; - } else { - // Just send text without Enter - cmd = `screen -S ${screen.screenName} -p 0 -X stuff "${escapedText}"`; + const crCmd = `screen -S ${screen.screenName} -p 0 -X stuff "$(printf '\\015')"`; + execSync(crCmd, { encoding: 'utf-8', timeout: 5000 }); } - execSync(cmd, { - encoding: 'utf-8', - timeout: 5000 - }); return true; } catch (err) { console.error('[ScreenManager] Failed to send input:', err);