The smallest version of running an unattended Claude Code prompt is this:

claude -p "What is today's date?"

-p (or --print) runs Claude Code non-interactively: it prints the final result to stdout and exits. A read-only question like that one usually needs no approvals.

I use the same idea for ticket triage. I fetch the open tickets that still need it (no triage label, or whatever the project uses), then fire off an unattended Claude Code session per ticket. It triages the ticket and closes it if the report is refuted. No need to issue a /clear between tickets, as each ticket gets triaged in its own session.

A session like that stalls in two ways. It stops on a permission prompt, or it finishes by asking a clarifying question instead of doing the work. This is a simplified version the command I run so neither happens:

claude -p "Triage this ticket. Close it if the report is refuted." \
  --permission-mode acceptEdits \
  --allowedTools "Bash(gh *)" \
  --permission-prompts none \
  --append-system-prompt "You are running unattended. Never ask questions. If something is ambiguous, pick the most reasonable interpretation, state the assumption in your final answer, and finish the task." \
  --max-budget-usd 1 \
  --output-format json \
  | jq -r '.result'

acceptEdits lets Claude write files without prompting. It also auto-approves common filesystem commands (mkdir, touch, mv, cp) and a built-in set of read-only commands, which includes grep. Other shell commands and network requests still need an --allowedTools entry, which is why gh is listed. The space before * matters. Bash(gh *) matches commands that start with gh . Without the space, Bash(gh*) would also match something like ghostscript.

--permission-prompts none is the flag for “nobody is home.” Anything that would have prompted is denied, Claude is told nobody can approve it and not to retry, and tools that need a human answer, such as AskUserQuestion, are removed. The flag needs Claude Code v2.1.259 or later.

Removing the question tool is not the whole fix. Claude can still end with “Should I also…?” in plain text. The --append-system-prompt line tells it to assume, record the assumption, and continue.

--output-format json puts the answer in .result. The same payload includes total_cost_usd and a per-model cost breakdown, and --max-budget-usd puts a ceiling on a runaway run. The process exits 0 on success and non-zero on failure, so a script can branch on the status.

In cron or CI you can consider adding --bare. It skips auto-discovery of hooks, skills, custom commands, subagents, plugins, MCP servers, auto memory, and CLAUDE.md, so every machine gets the same result. Two caveats. Bare mode never reads OAuth credentials or the keychain, so you need ANTHROPIC_API_KEY set, not a subscription login. And you lose CLAUDE.md. If the task depends on project conventions, pass that context another way, for example with --append-system-prompt-file.

Full reference: Run Claude Code programmatically

Categories
Tags