The agent's last tool call is git commit, and it hasn't returned. Somewhere under the agent, git has opened an editor for the commit message, and the editor is waiting for a person to type into it.

In Claude Code, a command that waits like this is killed after two minutes by default, when the Bash tool's timeout (BASH_DEFAULT_TIMEOUT_MS) runs out, and the model can ask for up to ten. The agent gets back a timeout, with no sign of which prompt the command was stuck on, and has to guess what went wrong.

Each of the nine commands below stops on a prompt meant for a human. For each one: what it waits on, and the flag or environment variable that makes it either finish or fail fast. The aim is a fast failure, an error message the agent can read and act on, instead of a silent wait.

Whether a command stalls depends on whether it thinks it has a terminal. Git only starts its pager when standard output is a terminal, and npx assumes --yes when standard input is not a TTY or CI is detected. Agent tools differ on what they hand a command, and the fixes are harmless either way, so set them all.

Git: the editor and the pager

1. git commit without -m

Git opens an editor for the message. It picks $GIT_EDITOR, then core.editor, then $VISUAL, then $EDITOR, and falls back to a compile-time default that is usually vi. git merge and git commit --amend open the same editor.

Tell the agent to always pass -m or -F, and to add --no-edit on merges and amends (git-commit docs). Then set GIT_EDITOR=true in the agent's environment as the backstop. true exits at once, so a bare git commit ends with an empty message, and git won't make a commit with no message unless you pass --allow-empty-message. A bare git merge keeps git's default merge message.

2. git log, git diff, git show

These pipe long output through a pager, usually less, and less waits for you to press q. Set GIT_PAGER=cat: per the git docs, an empty value or cat means git launches no pager at all. For a single call, git --no-pager log does the same.

Set GIT_PAGER even if you also set PAGER. Git checks core.pager in your config before it looks at $PAGER (git-var), so a pager in your own gitconfig wins over a plain PAGER=cat.

One more git prompt: a git push or git fetch over HTTPS with no stored credentials asks for a username. GIT_TERMINAL_PROMPT=0 tells git not to prompt on the terminal, so the push fails with an auth error instead.

A small robot waiting on a stool in front of a giant blinking terminal cursor, a wall clock behind it
A prompt with nobody at the keyboard: the agent waits until a timeout kills the command.

Installers: npx, npm init, apt

3. npx on a package that isn't installed

If the package is not in the project's dependencies, npm exec prints a prompt before it installs anything. npx --yes skips it.

Read why that prompt exists before you add --yes everywhere. The npx docs say it is there "to prevent security and user-experience problems from mistyping package names." With --yes, a typo installs whatever lives under the typo. A safer rule: add the tool to devDependencies once, and npx runs the local copy without asking.

4. npm init

Plain npm init walks through a questionnaire. npm init -y skips it and writes a default package.json.

npm init <initializer> is different: it runs a create-<initializer> package through npm exec, and that package asks its own questions. Options after -- are passed to it (npm init foo -- --hello maps to npm exec -- create-foo --hello). Check that package's --help for its own non-interactive flags.

5. apt install (and the sudo in front of it)

Three prompts stack here. sudo asks for a password. apt asks you to confirm the install. Some packages then ask configuration questions through debconf.

sudo -n env DEBIAN_FRONTEND=noninteractive \ apt-get install -y jq 

sudo -n never prompts: if a password is required, it prints an error and exits. apt-get -y answers yes to the confirmation and still aborts on things like removing an essential package. DEBIAN_FRONTEND=noninteractive makes debconf use the default answer for every question. The variable is set through env after sudo because sudo normally resets the environment.

Remote machines, databases, infra and GitHub

6. ssh to a host it has never seen

The default StrictHostKeyChecking is ask, so a new host key waits for you to confirm it. Two options stop the wait:

ssh -o BatchMode=yes build.example.com uptime ssh -o StrictHostKeyChecking=accept-new \ build.example.com uptime 

BatchMode=yes disables password prompts and host key questions, so ssh fails rather than asks. accept-new adds new host keys to known_hosts and still refuses a host whose key has changed.

The caveat: accept-new trusts whichever key answers first. If that first connection is intercepted, you have saved the attacker's key. For hosts you know, put the real keys in ~/.ssh/known_hosts ahead of time. GitHub publishes known_hosts entries for this. For git over ssh, GIT_SSH_COMMAND="ssh -o BatchMode=yes" applies the same rule.

7. psql

psql pages a result when the output goes to a terminal and doesn't fit on the screen, and it may ask for a password.

psql -w -P pager=off -c 'select count(*) from orders' 

-P pager=off turns the pager off, and PSQL_PAGER or PAGER set to cat does the same for every call. -w never prompts for a password: without a .pgpass file or another source, the connection fails.

8. terraform apply

Without a saved plan, terraform apply shows the plan and waits for approval. The tempting fix is -auto-approve, and HashiCorp's own docs note that it "includes destructive operations such as deleting resources." I would not hand that flag to an agent outside a throwaway workspace.

The split that works:

terraform plan -input=false -out=tfplan # a human reads the plan here terraform apply -input=false tfplan 

Applying a saved plan file runs it without a prompt, and -input=false turns off every interactive prompt. On a bare terraform apply, that makes Terraform assume you don't want to apply, so the agent gets a failure instead of a hang.

9. gh auth login

gh auth login is an interactive flow. Set GH_TOKEN instead: the gh docs say it "avoids being prompted to authenticate and takes precedence over previously stored credentials." Give the agent a token limited to the repos it needs.

Two more gh prompts: GH_PROMPT_DISABLED=1 turns off interactive prompting, and gh pages through GH_PAGER or PAGER. gh pr create prompts for a title and body unless you pass --title and --body, or --fill to take them from the commits.

Put it in the instruction file and the environment

The model can forget an instruction halfway through a task, but it can't forget an environment variable, so set up both halves.

The instruction half goes in CLAUDE.md, or in AGENTS.md for agents that read it:

## Shell commands: nobody is at the keyboard - Never run a command that waits for input. - git: always `git commit -m "..."`. Use `--no-edit` on merge and amend. Use `git --no-pager` for log/diff/show. - npx: only for packages listed in package.json. - npm init: `npm init -y`. - apt: `sudo -n env DEBIAN_FRONTEND=noninteractive apt-get install -y <pkg>`. If sudo needs a password, stop and ask me. - ssh: always `-o BatchMode=yes`. Never edit known_hosts. - psql: `psql -w -P pager=off`. - terraform: `plan -input=false -out=tfplan`, then stop and show me the plan. Never `-auto-approve`. - gh: never `gh auth login`. If GH_TOKEN is missing, ask me. - If a command hangs or times out, don't retry it with a longer timeout. Tell me which prompt it waited on. 

A control panel with nine levers, one pulled in burnt orange to route around a closed gate
Set the variables once in the agent's own config, so every command it runs starts non-interactive.

The environment half goes in the agent's config, not your shell profile, so your own git log keeps its pager. Claude Code reads an env block from settings.json and copies it into its own environment, which the commands it starts inherit. A committed .claude/settings.json shares it with the team (settings docs):

{ "env": { "GIT_EDITOR": "true", "GIT_PAGER": "cat", "PAGER": "cat", "GIT_TERMINAL_PROMPT": "0", "GH_PROMPT_DISABLED": "1", "DEBIAN_FRONTEND": "noninteractive" } } 

PAGER=cat covers psql and gh too, since both fall back to it. Codex has a shell_environment_policy.set key in its config for explicit values on the commands it runs:

[shell_environment_policy] set = { GIT_PAGER = "cat", PAGER = "cat", GIT_EDITOR = "true" } 

Which agent is waiting on you

With three agents in three terminals, the harder question is which one is stuck, and on what. I build Hermes IDE, so weigh this part with that in mind.

Hermes gives every session one status: working, needs approval, asked you, done, error or idle, and it says when that status is a guess. The attention inbox counts the agents blocked on you, and ⌘I (Ctrl+Shift+I on Windows and Linux) jumps to the one that has waited longest. You get a system notification when an agent is blocked or done, except for the session you are already looking at.

Codex and Claude Code sessions side by side in Hermes IDE, each in its own hermes/ worktree, with the sessions sidebar on the left
Two real agent sessions side by side: Codex on orders-types and Claude Code on orders-api, each in its own hermes/ worktree, with the sessions sidebar (status, context use, cost, branch) on the left.

Hermes reads what agents report on purpose and what git shows. A pager stuck inside an agent's shell command is not the agent asking you anything, so don't expect it in the inbox. A session that hasn't moved on a one-line commit is the tell. The agent's own permission prompt is different: an agent that stops to ask your approval for terraform apply is blocked on you, which is what the needs-approval status and the inbox are for, and you answer in the agent's own terminal. Hermes never answers a permission prompt for you and never types into a terminal on its own.

Hermes is free to download, source-available under BSL 1.1, and runs on macOS, Windows and Linux. It is not affiliated with Nous Research, Anthropic, OpenAI or any agent vendor.

If this was useful

If you're writing your own run-command tool, the hang in this post is a design decision you get to make, from the timeout to the error text the model gets back. AI That Acts is the book in my AI in TypeScript series on tool calling and building a first working agent, which is where that decision lives.

AI That Acts — AI in TypeScript

The whole series, from your first LLM call to agents in production: AI in TypeScript.