From cbab9e847c3087b75a23dc22130b1f66f2525231 Mon Sep 17 00:00:00 2001 From: arkon Date: Mon, 19 Jan 2026 15:43:15 +0100 Subject: [PATCH] fix(screen): use printf octal for carriage return in screen -X stuff The bash $'...' escaping wasn't reliably sending carriage return to Ink/Claude CLI. Using printf with octal '\015' works correctly. Tested: screen -X stuff "$(printf 'text\015')" successfully triggers Enter key submission in Claude CLI. Co-Authored-By: Claude Opus 4.5 --- src/screen-manager.ts | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/src/screen-manager.ts b/src/screen-manager.ts index ab11e6c3..1e87141a 100644 --- a/src/screen-manager.ts +++ b/src/screen-manager.ts @@ -467,9 +467,26 @@ export class ScreenManager extends EventEmitter { } try { - // Use screen -X stuff with $'...' syntax to send literal characters - // \r (carriage return) is what Claude CLI expects for Enter - execSync(`screen -S ${screen.screenName} -X stuff $'${input.replace(/'/g, "'\\''")}'`, { + // 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 + 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; + 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}"`; + } + + execSync(cmd, { encoding: 'utf-8', timeout: 5000 });