Skip to main content
← Back to list
01Issue
BugShippedSwamp CLIPublic
Assigneesskunk-ape

Relationships

#2260 Interactive prompts write to stdout, corrupting commands whose stdout carries a value

Opened by skunk-ape · 9/17/2026· Shipped 9/18/2026

Problem

src/cli/prompt_helpers.ts writes every prompt to stdout:

  • promptLineDeno.stdout.write(encoder.encode(message)) (line 53)
  • promptChoice — the message and each numbered choice (lines 84-88)
  • promptConfirmation delegates to promptLine (line 67)

For commands whose stdout is prose this is harmless. For a command whose stdout carries a value, the prompt lands in the consumer's output.

swamp vault read-secret is the live case. It asks for confirmation before revealing a secret (src/cli/commands/vault_read_secret.ts:122), and its renderer writes byte-exact secret bytes to stdout — deliberately, swamp-club#1768 removed even the trailing newline so external consumers get exactly the stored value.

The prompt is gated on assertInteractiveStdin(), but redirecting stdout does not change stdin. So run from a terminal:

swamp vault read-secret myvault mykey > key.pem

key.pem gets Reveal secret? [y/N] ahead of the secret. The byte-exactness that #1768 established is lost to a prompt.

Suggested fix

Prompts are interaction, not output — they belong on stderr, the same argument the invite-link renderer makes for its payout note. Writing promptLine and promptChoice to Deno.stderr would fix this for every command at once. That is a small change with a wide footprint: any test asserting prompt text on stdout, in this repo and in swamp-uat, would need to move the assertion to the other stream.

A narrower alternative is to route prompts to stderr only for the commands listed in the value-on-stdout registry (src/cli/stdout_contract.ts, added by #2254).

Context

Found in adversarial review while planning swamp-club#2254. That issue fixes LogTape records leaking to stdout for the three value-on-stdout commands (invite link, first-rule, vault read-secret). Prompts do not go through LogTape, so the stderrOnly mechanism has no reach over them — this is a separate polluter on the same contract, and vault read-secret's stdout is not fully clean until both are fixed.

Read from the code, not reproduced: confirming it needs a TTY on stdin.

Related: #2254, #2259 (stderr-by-default), #2257 (-q ignored by banners).

02Bog Flow
OPENTRIAGEDIN PROGRESSSHIPPED+ 1 MOREASSIGNED+ 5 MOREREVIEW+ 9 MOREPR_MERGED+ 2 MORESESSION_SUMMARIZED

Shipped

9/18/2026, 3:45:06 PM

Click a lifecycle step above to view its details.

03Sludge Pulse
skunk-ape assigned skunk-ape9/17/2026, 10:59:30 PM

Sign in to post a ripple.