Skip to content

fix(approval): always render the full command in approval prompts#3

Merged
jkyberneees merged 1 commit into
mainfrom
fix/approval-command-rendering
Jun 3, 2026
Merged

fix(approval): always render the full command in approval prompts#3
jkyberneees merged 1 commit into
mainfrom
fix/approval-command-rendering

Conversation

@jkyberneees
Copy link
Copy Markdown
Contributor

Problem

On both Telegram and the terminal, the command shown at approval time could be rendered incompletely. Approving a command you can't fully see defeats the purpose of the approval gate.

Root causes

Telegram — internal/telegram/approver.go

  1. Command hidden when a description exists. The prompt showed either the model-supplied description or the command — never both. Whenever the model included a description, the actual command was never rendered, so the user approved a command they could not see.
  2. Silent 200-char byte truncation. With no description, the command was desc[:200] + "..." — a byte slice (UTF-8 unsafe) that silently dropped the rest.
  3. No code-fence escaping. The command was dropped into a ``` block unescaped, so a backtick or backslash could close the fence early and corrupt the whole message.

Terminal — internal/narrate/narrate.go

  1. extractShell broke on embedded quotes. It scanned for the first " pair, so git commit -m "msg" or python -c "code" was cut at the first inner quote.
  2. Byte-based 50-char truncation of the narrated command.

Note: the terminal TTYApprover prompt itself already prints the full command — the visible terminal trimming comes from the activity narration, which this fixes.

Changes

  • Telegram: always render the full command in a fenced code block; show the description as a separate Why: line; escape ` and \ for MarkdownV2 code blocks (escapeCodeBlock); only truncate when the message would exceed Telegram's hard 4096-char limit, on a rune boundary, with an explicit [truncated] marker.
  • Terminal: extractShell now decodes the JSON args properly (old scan kept as a malformed-input fallback); truncate is rune-safe; shell narration budget raised 50 → 120.

Tests

Added coverage for: full-command-always-shown (with description), code-block escaping, hard-limit truncation, embedded-quote extraction, and rune-safe truncation. All internal/telegram and internal/narrate tests pass; go vet and gofmt clean.

🤖 Generated with Claude Code

Both the Telegram and terminal approval surfaces could show the
command-to-approve incompletely, undermining the user's ability to make
an informed decision.

Telegram (internal/telegram/approver.go):
- When the model supplied a description, the command was hidden entirely
  and only the description was shown — the user approved a command they
  could not see. The full command is now always rendered in a fenced
  code block, with the description shown as a separate "Why:" line.
- The command was byte-sliced to 200 chars (UTF-8 unsafe, silent). It is
  now only truncated when the whole message would exceed Telegram's hard
  4096-char limit, on a rune boundary, with an explicit "[truncated]"
  marker.
- Command content inside the code fence was unescaped, so a backtick or
  backslash could close the fence early and corrupt rendering. Added
  escapeCodeBlock() to escape ` and \ for MarkdownV2 code blocks.

Terminal (internal/narrate/narrate.go):
- extractShell scanned for the first quote pair, so any command with an
  embedded quote (git commit -m "msg", python -c "code") was cut at the
  first inner quote. It now decodes the JSON args properly, with the old
  scan kept only as a malformed-input fallback.
- The shell narration was byte-truncated at 50 chars; truncate() is now
  rune-safe and the shell budget is raised to 120.

Adds tests covering full-command rendering, code-block escaping,
hard-limit truncation, embedded-quote extraction, and rune-safe cuts.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@jkyberneees jkyberneees merged commit 7c198c9 into main Jun 3, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant