From f5de77d4ca67e238de186f50e9652a6cd155966c Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 13 Apr 2026 18:19:40 +0000 Subject: [PATCH] Fix echo >> append redirect being parsed as > redirect The >> operator in echo commands was being incorrectly parsed because the > regex matched first, capturing "> file" as the redirect target instead of appending to "file". Fix by checking >> before > and adding a negative lookahead to the > pattern so it doesn't match >>. Fixes pixari/gitvana#43 https://claude.ai/code/session_01HoJy5MNq1ePZXgubLzWm8x --- src/lib/engine/shell/builtins.ts | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/lib/engine/shell/builtins.ts b/src/lib/engine/shell/builtins.ts index c0f9992..d49b611 100644 --- a/src/lib/engine/shell/builtins.ts +++ b/src/lib/engine/shell/builtins.ts @@ -160,21 +160,7 @@ async function echoCommand(args: string[], fs: FsLike, cwd: string): Promise file - const redirectMatch = content_raw.match(/^(.*?)\s*>\s*(.+)$/); - if (redirectMatch) { - let content = redirectMatch[1].replace(/^["']|["']$/g, ''); - if (escapeFlag) content = interpretEscapes(content); - const filepath = resolvePath(redirectMatch[2].trim(), cwd); - try { - await fs.promises.writeFile(filepath, content + '\n'); - return { output: '', success: true }; - } catch { - return { output: `echo: cannot write to '${redirectMatch[2]}'`, success: false }; - } - } - - // Handle append: echo [-e] "content" >> file + // Handle append: echo [-e] "content" >> file (must check before > to avoid >> being parsed as >) const appendMatch = content_raw.match(/^(.*?)\s*>>\s*(.+)$/); if (appendMatch) { let content = appendMatch[1].replace(/^["']|["']$/g, ''); @@ -189,6 +175,20 @@ async function echoCommand(args: string[], fs: FsLike, cwd: string): Promise file + const redirectMatch = content_raw.match(/^(.*?)\s*>(?!>)\s*(.+)$/); + if (redirectMatch) { + let content = redirectMatch[1].replace(/^["']|["']$/g, ''); + if (escapeFlag) content = interpretEscapes(content); + const filepath = resolvePath(redirectMatch[2].trim(), cwd); + try { + await fs.promises.writeFile(filepath, content + '\n'); + return { output: '', success: true }; + } catch { + return { output: `echo: cannot write to '${redirectMatch[2]}'`, success: false }; + } + } + let output = content_raw.replace(/^["']|["']$/g, ''); if (escapeFlag) output = interpretEscapes(output); return { output, success: true };