# Headless mode

> Non-interactive runs with -p/--single, --prompt-file, --prompt-json; output formats plain/json/streaming-json; tool allow/deny lists, max-turns, resume/continue, and CI patterns.

- Repository: xai-org/grok-build
- GitHub: https://github.com/xai-org/grok-build
- Human docs: https://grok-wiki.com/public/docs/xai-org-grok-build-90205de50458
- Complete Markdown: https://grok-wiki.com/public/docs/xai-org-grok-build-90205de50458/llms-full.txt

## Source Files

- `crates/codegen/xai-grok-pager/docs/user-guide/14-headless-mode.md`
- `crates/codegen/xai-grok-pager/src/headless.rs`
- `crates/codegen/xai-grok-pager/src/app/cli.rs`
- `crates/codegen/xai-grok-shell/src/leader/mod.rs`
- `crates/codegen/xai-grok-tools/src/registry/mod.rs`
- `crates/codegen/xai-grok-shell/README.md`

---

---
title: "Headless mode"
description: "Non-interactive runs with -p/--single, --prompt-file, --prompt-json; output formats plain/json/streaming-json; tool allow/deny lists, max-turns, resume/continue, and CI patterns."
---

Headless mode is the non-interactive surface of the `grok` binary: any of `-p`/`--single`, `--prompt-json`, or `--prompt-file` starts an in-process agent via the ACP lifecycle (initialize → authenticate → session → prompt), streams or buffers the reply to **stdout**, and exits when the turn finishes. The pager marks the process as headless, sets `AgentMode::Headless`, and applies CLI tool/permission overrides before the first model call—so the same shell, tools, config, and sessions stack as the TUI, without a terminal UI.

## When to use headless

| Use case | Pattern |
| -------- | ------- |
| One-shot automation | `grok -p "…"` with `--output-format json` or `plain` |
| CI / pre-commit | `XAI_API_KEY` + `--yolo` (or scoped `--allow`/`--deny`) |
| Multi-step scripts | Capture `sessionId` from JSON, then `-r` / `-c` |
| Streaming consumers | `--output-format streaming-json` (NDJSON events) |
| IDE / custom protocol clients | Prefer `grok agent stdio` (ACP server), not `-p` |

<Note>
Headless (`-p`) is a **single-process prompt runner**. Long-lived multi-session control over JSON-RPC is **agent mode** (`grok agent stdio`). Both share auth, tools, and much of the flag surface.
</Note>

## Entry points and prompt inputs

Three mutually exclusive flags enter headless mode:

| Flag | Input |
| ---- | ----- |
| `-p`, `--single` (alias `--print`) | Prompt string |
| `--prompt-json <JSON>` | ACP content blocks as JSON |
| `--prompt-file <PATH>` | File contents as prompt; `.json` files parse as content blocks, other extensions as text |

Empty prompts are rejected. `--prompt-json` accepts either a bare array of ACP content blocks or `{"type":"acp","content":[...]}`.

```bash
grok -p "Explain this codebase"
grok --prompt-file ./prompt.txt
grok --prompt-json '[{"type":"text","text":"Summarize Cargo.toml"}]'
```

<Warning>
Headless mode does **not** read piped stdin as the prompt. Pass content via command substitution or `--prompt-file`:

```bash
grok -p "Write a commit message for:

$(git diff --staged)"
```
</Warning>

Optional prompt shaping:

| Flag | Effect |
| ---- | ------ |
| `--verbatim` | Send the prompt exactly as given |
| `--rules <TEXT>` | Append custom rules to the system prompt |
| `--system-prompt-override` | Replace the agent system prompt (alias `--system-prompt`) |
| `--json-schema <SCHEMA>` | Constrain structured output; implies `--output-format json` |

## Core flags

| Flag | Scope | Description |
| ---- | ----- | ----------- |
| `-m`, `--model` | Both | Model ID |
| `--cwd <PATH>` | Both | Working directory (project root discovery walks up for `.git`) |
| `--output-format` | Headless | `plain` (default), `json`, `streaming-json` |
| `--yolo` / `--always-approve` | Both | Always-approve tool execution (aliases include `dangerously-skip-permissions`) |
| `--permission-mode <MODE>` | Both | Permission mode; `bypassPermissions` is always-approve via this path |
| `--allow` / `--deny` | Both | Permission rules (`ToolPrefix(glob)`); repeatable; deny wins |
| `--tools` | **Headless only** | Comma-separated built-in tool allowlist |
| `--disallowed-tools` | **Headless only** | Comma-separated denylist; supports `Agent` / `Agent(type)` |
| `--max-turns <N>` | **Headless only** | Cap agentic model rounds (`N ≥ 1`) |
| `--reasoning-effort` / `--effort` | Both | Canonical levels: `none`, `minimal`, `low`, `medium`, `high`, `xhigh`, `max` (alias of `xhigh`); also per-model menu ids |
| `-s`, `--session-id` | Headless | Create a **new** session with a **UUID** (not resume) |
| `-r`, `--resume` | Both | Resume existing session by ID |
| `-c`, `--continue` | Both | Continue most recent session for the cwd |
| `--fork-session` | Both | With `-r`/`-c`, fork into a new session ID |
| `--sandbox <PROFILE>` | Both | Sandbox profile (`GROK_SANDBOX` env also accepted) |
| `--no-auto-update` | Both | Disable update checks for this run |

<Info>
If you pass `--tools`, `--disallowed-tools`, `--max-turns`, or `--agents` in the interactive TUI, Grok prints a warning and ignores them. Permission and effort flags apply in both modes.
</Info>

### Additional headless-oriented flags

| Flag | Description |
| ---- | ----------- |
| `--agent <NAME>` | Agent name or definition file path |
| `--agents <JSON>` | Inline subagent definitions |
| `--check` / `--self-verify` | Append a verification loop (headless only) |
| `--best-of-n <N>` | Run the task N ways and pick the best (headless only) |
| `--no-plan` | Disable plan mode |
| `--no-subagents` | Disable subagent spawning |
| `--no-memory` | Disable cross-session memory |
| `--disable-web-search` | Disable web search and fetch tools |
| `--worktree [NAME]` | Start in a new git worktree |
| `--worktree-ref` / `--ref` | Base ref for the worktree |

## Output formats

Selected with `--output-format`. Diagnostic and update messages go to **stderr** so stdout stays machine-clean for `json` / `streaming-json`.

### plain (default)

Human-readable assistant text only—suitable for display or simple pipes.

```bash
grok -p "Generate a README" > README.md
```

### json

One JSON object after the turn completes. Always useful fields: `text`, `stopReason`, `sessionId`, `requestId` (and `thought` when reasoning is present). When the prompt reached the model, spend fields may also appear: `usage`, `num_turns`, `modelUsage`, cost fields.

```bash
grok -p "List files" --output-format json | jq -r '.text'
SID=$(grok -p "Hello" --output-format json | jq -r '.sessionId')
```

Example shape (fields omitted when unavailable):

```json
{
  "text": "Here's a summary of the codebase...",
  "stopReason": "EndTurn",
  "sessionId": "abc123",
  "requestId": "xyz789",
  "num_turns": 7,
  "usage": {
    "input_tokens": 7210,
    "cache_read_input_tokens": 41000,
    "output_tokens": 1893,
    "reasoning_tokens": 412,
    "total_tokens": 50103
  },
  "modelUsage": {
    "grok-build": {
      "inputTokens": 7210,
      "outputTokens": 1893,
      "cacheReadInputTokens": 41000,
      "modelCalls": 7,
      "costUSD": 0.01268905
    }
  },
  "total_cost_usd": 0.01268905,
  "total_cost_usd_ticks": 126890500
}
```

Spend / usage notes (automation-critical):

- Headless `usage.input_tokens` and `modelUsage.*.inputTokens` are **uncached only**; cache hits use `cache_read_input_tokens` / `cacheReadInputTokens`.
- `total_tokens = input_tokens + cache_read_input_tokens + output_tokens`.
- `num_turns` counts main-agent model rounds with usage (same family as `--max-turns`, not a hard equality guarantee under gates/missing usage).
- `total_cost_usd` / ticks appear only for **complete** cost; absence means unreported or incomplete—not free. Partial cost sets `cost_is_partial` and omits all cost floats.
- Incomplete subagent drain sets `usage_is_incomplete` and omits cost floats.
- Failure path: non-zero exit; may emit `{"type":"error","message":"…"}` with frozen spend when usage was recorded.

### streaming-json

Newline-delimited JSON events in real time. `end` is always last.

| `type` | Meaning |
| ------ | ------- |
| `text` | Response text chunk (`data`) |
| `thought` | Reasoning / thinking chunk |
| `end` | Final metadata + spend fields when available |
| `error` | Error (`message`, optional spend) |
| `max_turns_reached` | Turn cap hit (also fails the process) |

Treat the event list as non-exhaustive (for example `auto_compact_*` may appear). Switch on `type`.

```bash
grok -p "Explain the architecture" --output-format streaming-json
```

## Tool allow and deny lists

Use internal tool IDs (not display names). Shell is `run_terminal_cmd`, not `bash`.

| Display / concept | Tool ID |
| ----------------- | ------- |
| Shell | `run_terminal_cmd` |
| Grep | `grep` |
| Read file | `read_file` |
| Edit | `search_replace` |
| List dir | `list_dir` |
| Web search | `web_search` |
| Web fetch | `web_fetch` |
| Todos | `todo_write` |
| Task | `task` |

```bash
# Read-only toolset
grok -p "Explain this codebase" --tools "read_file,grep,list_dir"

# Strip shell and web
grok -p "Review this code" --disallowed-tools "run_terminal_cmd,web_search,web_fetch"
```

When both flags are set, **`--disallowed-tools` wins** (applied after the allowlist). MCP meta-tools stay available unless denied. Stock agent profiles may inject enabled optional tools before the allowlist; curated profiles stay strict.

### Restricting subagents

`--disallowed-tools` accepts special `Agent` entries:

| Entry | Effect |
| ----- | ------ |
| `Agent` | Block all subagent spawning |
| `Agent(explore)` | Block only the `explore` type |
| `Agent(explore, plan)` | Block multiple types |

```bash
grok -p "Fix this bug" --disallowed-tools "Agent"
grok -p "Refactor this module" --disallowed-tools "Agent(explore)"
```

`--max-turns N` stops the agent after N agentic turns. On the cap, headless emits `max_turns_reached` (streaming) or includes it in stop metadata (json), prints a stderr note in plain mode, and exits with failure (`max turns reached`).

## Permissions vs tool lists

| Mechanism | Removes tools? | Gates invocations? |
| --------- | -------------- | ------------------ |
| `--tools` / `--disallowed-tools` | Yes | N/A (tool absent) |
| `--allow` / `--deny` | No | Yes |
| `--yolo` / `--permission-mode bypassPermissions` | No | Auto-approve (deny rules and PreToolUse hooks still apply) |

Permission rule prefixes include `Bash(...)`, `Edit(...)`, `Write(...)`, `Read(...)`, `Grep(...)`, `WebFetch(...)`, `MCPTool(...)`. Bare prefix matches all of that kind. Deny overrides allow.

```bash
grok -p "Set up the project" --allow "Bash(npm*)" --deny "Bash(sudo*)"
grok -p "Clean up" --deny "Bash(rm*)"
```

<Warning>
`--yolo` / always-approve grants full autonomy for file and command tools. Prefer scoped `--allow`/`--deny`, sandbox profiles, and narrow prompts in untrusted trees. Administrators can disable bypass via managed `requirements.toml`.
</Warning>

## Sessions: resume, continue, fork

Default: each `grok -p` creates a **fresh** session.

| Flag | Behavior |
| ---- | -------- |
| `-r` / `--resume <ID>` | Resume that session; errors if missing |
| `-c` / `--continue` | Resume most recent session for the current directory |
| `-s` / `--session-id <UUID>` | Create a **new** session with that UUID; errors if invalid or already in use |
| `--fork-session` | With `-r`/`-c`, fork to a new ID (optionally named with `-s`) |

```bash
SID=$(grok -p "Review the PR" --output-format json | jq -r '.sessionId')
grok -p "Now check for security issues" --resume "$SID"
grok -p "Continue where we left off" -c

# Client-chosen new UUID (must not exist)
grok -p "hello" --session-id "$(uuidgen | tr '[:upper:]' '[:lower:]')" --output-format json
```

<Note>
Older docs that describe `-s` as “create or resume by arbitrary string” are outdated. Current behavior is UUID create-only; use `-r` / `-c` to continue.
</Note>

On SIGINT/SIGTERM: session state is saved through the last completed tool call; filesystem edits are **not** rolled back. Exit **130** (SIGINT) or **143** (SIGTERM). Resume with `-r` or `-c`.

## Authentication and environment

| Variable / method | Role |
| ----------------- | ---- |
| `XAI_API_KEY` | Preferred for CI; no browser |
| `grok login --device-auth` | Device-code flow for remote/headless hosts |
| `grok login` | Browser OAuth when a GUI is available |
| `GROK_HOME` | Override config root (default `~/.grok`) |
| `GROK_LOG_FILE` | Log path (honors `RUST_LOG`) |
| `RUST_LOG` | Log filter; headless logs to stderr |
| `GROK_DISABLE_AUTOUPDATER=1` | Process-level update suppression |

```bash
export XAI_API_KEY="xai-..."
grok -p "Run the test suite" --yolo --output-format json
```

Update suppression also follows `--no-auto-update`, non-TTY stderr auto-detect, and `[cli] auto_update = false`.

### Read-only `~/.grok` (containers)

Mount credentials read-only if needed:

- Pre-seed `auth.json` or set `XAI_API_KEY`
- Session persistence may fail silently (ephemeral)
- Prefer `GROK_DISABLE_AUTOUPDATER=1` and `--no-auto-update`

## Exit codes

| Code | Meaning |
| ---- | ------- |
| `0` | Prompt completed successfully |
| `1` | Auth, network, runtime, or max-turns failure |
| `130` | Interrupted by SIGINT |
| `143` | Terminated by SIGTERM |

## CI and scripting patterns

### Automated review

```bash
grok -p "Review changes for bugs and security issues." \
  --output-format json --yolo | jq -r '.text' > review.md
```

### Pre-commit gate

```bash
grok -p "Review staged changes for obvious bugs. Reply OK if fine, or list issues." \
  --yolo --output-format json | jq -r '.text' | grep -q "^OK" || exit 1
```

### Batch over files

```bash
for file in src/*.js; do
  grok -p "Migrate $file from CommonJS to ES modules." --yolo
done
```

### Guardrails with rules

```bash
grok -p "Refactor the module" --yolo --rules "Never delete files; never run rm -rf"
```

### Scoped cwd in monorepos

Point `--cwd` at the subproject you care about so AGENTS.md, skills, and git history discovery stay local—starting deep under a huge monorepo root can slow startup.

```bash
grok -p "Run the package tests and fix failures" \
  --cwd ~/projects/monorepo/packages/api --yolo
```

### Debug a headless failure

```bash
RUST_LOG=debug grok -p "Reproduce the bug" --yolo 2> debug.log
```

## Runtime path (implementers)

1. CLI parses `PagerArgs`; presence of single / prompt-json / prompt-file builds `HeadlessPrompt`.
2. `run_single_turn` stamps headless HTTP client mode, loads effective config, builds `AgentConfig` with `mode = Headless`, yolo/permission mode, and `CliAgentOverrides` (tools, denylist, allow/deny, max_turns).
3. Spawns the in-process shell (`spawn_grok_shell`), runs ACP initialize + auth + session materialization, then sends the prompt.
4. `HeadlessEmitter` projects ACP stream events into plain / json / streaming-json on stdout; cancel tokens clean up background work on exit.

Leader IPC can still host headless clients alongside TUI/IDE followers when leader mode is enabled, but a default `grok -p` run is self-contained in-process unless configured otherwise.

## Failure modes

| Symptom | Likely cause | Mitigation |
| ------- | ------------ | ---------- |
| Immediate auth error | No `XAI_API_KEY` / stale `auth.json` | Export key or `grok login --device-auth` |
| Tools hang waiting for approval | No `--yolo` / allow rules in non-TTY | Use `--yolo`, `--permission-mode`, or explicit `--allow` |
| `max turns reached` | Tight `--max-turns` | Raise N or narrow the task |
| Empty / wrong prompt | Expecting stdin pipe | Use `--prompt-file` or `$(…)` |
| JSON polluted on stdout | Logs mixed in | Keep logs on stderr; set `RUST_LOG` carefully; suppress auto-update |
| Slow monorepo start | `--cwd` too high | Point at package root |
| Session not found on `-r` | Wrong ID or cwd session dir | Capture `sessionId` from prior `json` output |

## Related pages

<CardGroup>
  <Card title="Authentication" href="/authentication">
    API keys, device-code, and OAuth for non-interactive hosts.
  </Card>
  <Card title="Sessions" href="/sessions">
    Resume, continue, fork, and persistence layout.
  </Card>
  <Card title="Permissions and safety" href="/permissions-and-safety">
    Allow/deny rules, permission modes, hooks, and YOLO limits.
  </Card>
  <Card title="Agent mode (ACP)" href="/agent-mode">
    Long-lived JSON-RPC agent for IDEs and custom clients.
  </Card>
  <Card title="Sandbox profiles" href="/sandbox">
    OS-level isolation with `--sandbox` / `GROK_SANDBOX`.
  </Card>
  <Card title="Subagents and personas" href="/subagents">
    Child agents and `Agent(...)` denylist entries.
  </Card>
  <Card title="CLI reference" href="/cli-reference">
    Full flag and subcommand surface.
  </Card>
  <Card title="Troubleshooting" href="/troubleshooting">
    Auth recovery, logging, and diagnostics.
  </Card>
</CardGroup>
