rtk-ai/rtk

rtk rewrite mishandles newline-separated (multi-line) commands → only first line rewritten / empty output

Open

#2 191 ouverte le 1 juin 2026

Voir sur GitHub
 (1 commentaire) (0 réactions) (0 assignés)Rust (2 914 forks)batch import
area:clibughelp wantedneeds-reproductionpriority:high

Métriques du dépôt

Stars
 (48 085 stars)
Métriques de merge PR
 (Merge moyen 8j 17h) (49 PRs mergées en 30 j)

Description

Summary

rtk rewrite does not treat newline (\n) as a statement separator. It appears to only parse/rewrite the first newline-delimited segment of a command, which breaks multi-statement commands written across multiple lines:

  • If the first line is not a rewritable command (cd, echo, …), rtk rewrite returns empty output with exit 1 ("no rewrite"), even when later lines contain rewritable commands (grep, tsc, …).
  • If the first line is rewritable, only that first line is rewritten; rewritable commands on subsequent lines are left untouched.

Semicolon-separated equivalents are handled correctly. So the bug is specifically newline handling.

Combined with the official Claude Code hook (rtk-rewrite.sh, which applies the rewrite when rtk rewrite exits 0/3), this surfaces as silently truncated output when an agent runs a multi-line shell snippet containing several greps — only the first section is produced. This was hit repeatedly in a real Claude Code session.

Environment

  • rtk 0.42.0 (Homebrew, macOS arm64) — also reproduced on 0.35.0
  • Claude Code PreToolUse hook rtk-rewrite.sh (# rtk-hook-version: 3)

Reproduction (deterministic, no Claude Code needed)

printf 'x\n' > /tmp/f.txt

# (1) Semicolon-separated — CORRECT (both greps rewritten):
rtk rewrite 'cd /tmp; grep -c x /tmp/f.txt; grep -c x /tmp/f.txt'
#   -> cd /tmp; rtk grep -c x /tmp/f.txt; rtk grep -c x /tmp/f.txt      (exit 0)

# (2) Same commands, newline-separated, first line is `cd` — BUG: empty + exit 1
rtk rewrite "$(printf 'cd /tmp\ngrep -c x /tmp/f.txt\ngrep -c x /tmp/f.txt')"
#   -> (empty)                                                          (exit 1)

# (3) Newline-separated, first line is grep — BUG: only FIRST line rewritten
rtk rewrite "$(printf 'grep -c x /tmp/f.txt\ngrep -c x /tmp/f.txt')"
#   -> rtk grep -c x /tmp/f.txt
#      grep -c x /tmp/f.txt          <- second grep left un-rewritten   (exit 0)

Bisection — every multiline form whose first line is non-rewritable returns exit=1, len=0:

command result
cd /tmp; grep…; grep… (semicolons) exit 0, full rewrite ✓
grep…\ngrep… (newlines, grep first) exit 0, only 1st line rewritten
cd /tmp\ngrep…\ngrep… exit 1, empty
cd /tmp\necho A\ngrep…\necho B\ngrep… exit 1, empty
echo 中文\ngrep…\ngrep… exit 1, empty
cd "/tmp/a b"\ngrep…\ngrep… exit 1, empty

Expected

rtk rewrite should treat \n as a statement separator (like ;), rewriting each rewritable statement independently and preserving every statement in the output — so a newline-separated command yields the same result as its semicolon-separated equivalent.

Impact

Multi-line shell snippets are extremely common in agent/hook usage (heredocs, multi-step verification blocks). Today they are either:

  • left with only the first statement token-optimized (mild), or
  • effectively pass-through/empty depending on the first line (and, via hooks that act on the rewrite, produce truncated/partial command execution with no error) — silent and easy to mistake for a tool/agent bug.

Suggested fix

In the rewrite parser (registry), split the command on top-level statement separators including newline (\n) in addition to ; / && / ||, rewrite each segment, and re-join preserving the original separators.

Guide contributeur