Ever watch an agent freeze on a yes-no prompt? The model keeps talking like nothing blocked it. The process on the box waits for stdin.
I keep seeing the same five claims in chat logs. People treat a headless agent box like a laptop. Then they blame the model when apt sits there.
This FAQ is about TTY myths. It is not about model quality. It is not about git identity either.
Why these myths spread
A chat UI looks interactive, right? You type. It types back. That feeling lies.
The free model writes a shell command. A free server may run that command. Those are two different processes with no shared keyboard.
I draft probes with MonkeyCode when I want a free model and a free server in the same loop. Disclosure: This article was prepared as part of MonkeyCode's product outreach. The checklist still works if you paste it elsewhere.
The mental model that actually holds
Treat the pair like CI. CI has no human at stdin. CI has no pager waiting for q.
If a command needs a human, rewrite it. Do not narrate the prompt in chat. Kill the process and replace the flags.
Ask yourself one question before every install. Can this command finish with stdin closed?
Myth 1: The agent can answer "Are you sure?"
The claim
People say the agent will type Y. Why would it? The installer is not reading the chat transcript.
What you actually see
apt-get install foo stops on a confirmation line. The shell is blocked on read. The model may still invent a success sentence.
Evidence you can run
# Label: run this yourself. Do not trust chat narration.
sudo apt-get install foo
# Now in another pane, if you even have one:
ps -o pid,stat,wchan,cmd -C apt-get
Look at wchan. A waiting read is not a thinking model. It is a stuck prompt.
Corrected model
Confirmation is a process, not a conversation. Reach for a noninteractive flag first.
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y foo
If you cannot find that flag, do not run it. Ask for a noninteractive equivalent before the box starts waiting.
Myth 2: yes | unlocks every prompt
The claim
Someone always pastes yes | in front of the command. It looks clever in a laptop demo. It fails on a headless box more often than it helps.
What you actually see
yes writes y into stdin. Many tools ignore stdin completely. They open /dev/tty instead.
Password prompts do this. SSH host-key checks do this. Some npm lifecycle scripts do this too.
Evidence you can run
# Looks clever. Often feeds a pipe nobody reads.
yes | ssh-keygen -t ed25519 -f /tmp/demo-key
# Same tool, flags that skip the prompt entirely.
ssh-keygen -t ed25519 -f /tmp/demo-key -N "" -q
Did the first form hang on overwrite? That hang is /dev/tty, not stdin.
Corrected model
stdin is not a keyboard. /dev/tty is a different door. Prefer flags that disable the question.
Never pipe yes into a secret prompt. You will spray a pattern into logs. You may also lock an account.
Myth 3: A hung command means the model is thinking
The claim
The chat still streams tokens, so you wait. Bad instinct. Those two clocks are not coupled at all.
What you actually see
The box is blocked on read(). The model is guessing the next paragraph. You now have two liars in one window.
Evidence you can run
PID=$(pgrep -n -f 'apt-get|npm|pip|less' || true)
echo "pid=${PID:-none}"
[ -n "${PID:-}" ] && ps -o pid,stat,wchan,cmd -p "$PID"
# Optional. Stop after a few lines.
[ -n "${PID:-}" ] && timeout 5 strace -p "$PID" 2>&1 | head
See read on fd 0? That is a prompt. See wait in wchan? That is a child, not a deep thought.
Corrected model
Token stream is not process state. Check the process. Ignore the narration until ps agrees.
Wrap unknown commands in timeout. A killed process is cheaper than a ten-minute shrug.
Myth 4: CI=true makes every installer quiet
The claim
One environment variable will silence apt, npm, pip, and git. Which universe is that from?
What you actually see
Some tools honor CI. Many do not. npm often does. apt does not care. pip depends on the package. cargo is its own world.
Evidence you can run
# npm: sometimes enough, never a contract
CI=true npm install
# more honest for npm in a clean tree
npm ci --yes --no-fund --no-audit
# pip: fail if it must ask
pip install --no-input some-pkg
# git: never open less
GIT_PAGER=cat git --no-pager log -1 --oneline
Did npm install still stop on a funding prompt? Then CI=true was not the mute switch you needed.
Corrected model
Each toolchain has its own mute switch. Collect them in a wrapper. Do not trust one env var across ecosystems.
Myth 5: If a pager appeared, someone scrolled it
The claim
The chat showed the first screen of git log. So somebody hit space, right? Nobody did.
What you actually see
git log, systemctl status, man, and less assume a human. On a TTY they wait for q. Without a TTY they may still block.
Your chat captured the first page. The process is still inside the pager.
Evidence you can run
git --no-pager log -5 --oneline
export PAGER=cat
export GIT_PAGER=cat
export SYSTEMD_PAGER=cat
export MANPAGER=cat
systemctl --no-pager --full status || true
If less is in ps, you did not finish the command. You screenshotted a waiting TUI.
Corrected model
Pagers are interactive programs. Ban them in agent shells. Set PAGER=cat before the first git command.
Artifact: a TTY probe and a fail-fast wrapper
Run this on the box before you install anything. Do not skip the printout. Label: unexecuted template. You run it.
#!/usr/bin/env bash
# probe-tty.sh
set -euo pipefail
python3 - <<'PY'
import os, sys
for fd, name in ((0, "stdin"), (1, "stdout"), (2, "stderr")):
print(f"isatty({name})={os.isatty(fd)}")
PY
echo "TERM=${TERM-<unset>}"
echo "CI=${CI-<unset>}"
echo "DEBIAN_FRONTEND=${DEBIAN_FRONTEND-<unset>}"
echo "PAGER=${PAGER-<unset>}"
echo "GIT_PAGER=${GIT_PAGER-<unset>}"
if [ -t 0 ]; then
echo "stdin is a TTY. Interactive tools may wait for you."
else
echo "stdin is not a TTY. Prompts will hang or fail."
fi
Then wrap risky commands so a hidden prompt dies quickly.
#!/usr/bin/env bash
# run-noninteractive.sh
# 45s is a local guess, not a product SLA.
set -euo pipefail
cmd=("$@")
timeout --signal=TERM --kill-after=5s 45s env \
DEBIAN_FRONTEND=noninteractive \
CI=true \
PAGER=cat \
GIT_PAGER=cat \
SYSTEMD_PAGER=cat \
"${cmd[@]}"
Use it like this.
chmod +x probe-tty.sh run-noninteractive.sh
./probe-tty.sh
./run-noninteractive.sh npm ci --yes --no-fund --no-audit
If timeout fires, you found a hidden prompt. Do not retry the same command. Change flags and probe again.
A ten-minute drill
- Print
isattyfor stdin, stdout, and stderr. - Export pager variables before any git or systemctl call.
- Rewrite one install command with explicit yes-flags.
- Run that rewrite under
timeout. - If it dies, read
psandwchan, not the chat.
That drill is the whole workflow. The model drafts. The box executes. You believe ps.
Decision table
| You wanted | Interactive default | Noninteractive stand-in | If no stand-in exists |
|---|---|---|---|
| apt install | confirm + pager | DEBIAN_FRONTEND=noninteractive apt-get -y |
skip the package |
| npm init | many questions | npm init -y |
write package.json yourself |
| npm install | funding / audit prompts | npm ci --yes --no-fund --no-audit |
vendor the tree |
| pip install | maybe confirm | pip install --no-input |
vendor a wheel |
| git log | less |
git --no-pager plus GIT_PAGER=cat
|
print git log -1 only |
| ssh-keygen | overwrite prompt | -N "" -f path -q |
do not generate keys here |
| docker pull | rare auth prompt | check credentials first | do not pull private images |
Print the table next to the probe output. Pick the stand-in before the model invents yes |.
Limitations
This does not make interactive tools safe. It only fails faster.
timeout can kill a slow but honest compile. Forty-five seconds is a guess. Raise it for real builds. Keep it short for package managers that should not compile the world.
The probe cannot see /dev/tty opens after fork. A child can still block. strace helps. It is not complete coverage.
Do not put credentials in flags the model can see. Do not use this wrapper as a production installer. This is a debugging workflow for hung chats.
Who should skip this
Skip this if you have a real terminal attached. Skip this if you debug inside a multiplexer. Skip this if the job is a TUI.
Skip this for anything that needs a password. A password prompt is not a yes prompt. Do not flatten those two cases.
Skip this if you need a long-running dev server with a human attached. That is a laptop job. A free server is the wrong shape for that work.
What I want back
Run the probe. Paste the three isatty lines. Which myth bit you first?
Top comments (0)