# Grok Build Documentation

> Technical documentation for the grok CLI/TUI agent: install and auth, interactive and headless runtimes, ACP integration, configuration surfaces, extension points (MCP, skills, plugins, hooks), sandbox and permissions, and reference for flags, slash commands, and key bindings.

## Context Links

- [Agent index](https://grok-wiki.com/public/docs/xai-org-grok-build-90205de50458/llms.txt)
- [Human interactive docs](https://grok-wiki.com/public/docs/xai-org-grok-build-90205de50458)
- [GitHub repository](https://github.com/xai-org/grok-build)

## Repository Metadata

- Repository: xai-org/grok-build

- Generated: 2026-07-16T02:37:47.853Z
- Updated: 2026-07-16T02:39:06.119Z
- Runtime: Grok CLI · grok-4.5
- Format: Documentation
- Pages: 25

## Page Index

- 01. [Overview](https://grok-wiki.com/public/docs/xai-org-grok-build-90205de50458/pages/01-overview.md) - What Grok Build exposes (interactive TUI, headless -p, ACP agent), who should use each surface, and the shortest path from install to a successful first turn.
- 02. [Installation](https://grok-wiki.com/public/docs/xai-org-grok-build-90205de50458/pages/02-installation.md) - Install and update the released grok binary on macOS, Linux, and Windows; PATH layout under ~/.grok/bin; version pin flags; verify with grok --version.
- 03. [Quickstart](https://grok-wiki.com/public/docs/xai-org-grok-build-90205de50458/pages/03-quickstart.md) - First interactive session: launch grok, authenticate, send a prompt, use @ file attachments, cancel turns, and confirm tool output in the TUI scrollback.
- 04. [Authentication](https://grok-wiki.com/public/docs/xai-org-grok-build-90205de50458/pages/04-authentication.md) - Browser OAuth (auth.x.ai), device-code flow, XAI_API_KEY for CI, grok login/logout, credential storage in ~/.grok/auth.json, and token refresh behavior.
- 05. [Sessions](https://grok-wiki.com/public/docs/xai-org-grok-build-90205de50458/pages/05-sessions.md) - Session lifecycle: create, resume, continue, fork, compact, export; persistence layout; /new /resume /compact; grok sessions subcommands and ID constraints.
- 06. [Permissions and safety](https://grok-wiki.com/public/docs/xai-org-grok-build-90205de50458/pages/06-permissions-and-safety.md) - Tool authorization pipeline: PreToolUse hooks, allow/deny/ask rules, remembered grants, permission modes (including bypassPermissions/dontAsk), safe-bash defaults, and interaction with sandbox.
- 07. [Project rules and memory](https://grok-wiki.com/public/docs/xai-org-grok-build-90205de50458/pages/07-project-rules-and-memory.md) - AGENTS.md discovery and precedence; cross-session memory (/flush, /dream, hybrid search); when rules inject into the system prompt versus when memory retrieval runs.
- 08. [Configure Grok](https://grok-wiki.com/public/docs/xai-org-grok-build-90205de50458/pages/08-configure-grok.md) - How to set config.toml and pager.toml, precedence (CLI > env > user config > managed/requirements > defaults), feature flags, and grok inspect to dump resolved settings.
- 09. [Custom models](https://grok-wiki.com/public/docs/xai-org-grok-build-90205de50458/pages/09-custom-models.md) - Select models via -m and /model; configure BYOK and OpenAI-compatible or Ollama endpoints; per-model overrides; grok models listing and provider-neutral endpoint settings.
- 10. [MCP servers](https://grok-wiki.com/public/docs/xai-org-grok-build-90205de50458/pages/10-mcp-servers.md) - Register MCP servers in config.toml (stdio and HTTP), timeouts and env, enable/disable, OAuth-related MCP credentials, and grok mcp management commands.
- 11. [Skills](https://grok-wiki.com/public/docs/xai-org-grok-build-90205de50458/pages/11-skills.md) - SKILL.md package layout, discovery paths (.grok, .agents, .claude, .cursor), priority and ignore rules, slash-command surface, and config toggles for vendor skill trees.
- 12. [Plugins](https://grok-wiki.com/public/docs/xai-org-grok-build-90205de50458/pages/12-plugins.md) - Install and trust plugins that bundle skills, commands, agents, hooks, MCP, and LSP; marketplace sources; plugin.json conventions; GROK_PLUGIN_ROOT and GROK_PLUGIN_DATA.
- 13. [Hooks](https://grok-wiki.com/public/docs/xai-org-grok-build-90205de50458/pages/13-hooks.md) - Lifecycle hooks (PreToolUse, PostToolUse, SessionStart, and peers): JSON discovery, shell vs HTTP runners, deny/allow results, environment variables, and safety patterns.
- 14. [Headless mode](https://grok-wiki.com/public/docs/xai-org-grok-build-90205de50458/pages/14-headless-mode.md) - 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.
- 15. [Agent mode (ACP)](https://grok-wiki.com/public/docs/xai-org-grok-build-90205de50458/pages/15-agent-mode-acp.md) - Run grok agent stdio as an Agent Client Protocol server for IDEs and custom clients; JSON-RPC session lifecycle, streaming, permissions, and options shared with agent subcommands.
- 16. [Sandbox profiles](https://grok-wiki.com/public/docs/xai-org-grok-build-90205de50458/pages/16-sandbox-profiles.md) - OS-level isolation profiles (workspace, read-only, strict, devbox, off), --sandbox flag, custom deny globs, Linux vs macOS enforcement differences for child network.
- 17. [Subagents and personas](https://grok-wiki.com/public/docs/xai-org-grok-build-90205de50458/pages/17-subagents-and-personas.md) - Spawn and constrain child agents: agent types, personas, capability modes, isolation/worktree behavior, and headless Agent(...) denylist entries.
- 18. [Plan mode](https://grok-wiki.com/public/docs/xai-org-grok-build-90205de50458/pages/18-plan-mode.md) - Structured planning before edits: plan files, approval gates, mode entry/exit from the TUI, and how plan mode interacts with tools and permissions.
- 19. [Theming](https://grok-wiki.com/public/docs/xai-org-grok-build-90205de50458/pages/19-theming.md) - TUI appearance: /theme, pager.toml theme keys, color-support detection, screen modes (fullscreen/minimal), and terminal capability constraints.
- 20. [CLI reference](https://grok-wiki.com/public/docs/xai-org-grok-build-90205de50458/pages/20-cli-reference.md) - Top-level grok commands and flags: default TUI, agent, login/logout, mcp, plugin, memory, models, sessions, inspect, setup, update, wrap, export, trace; shared runtime flags.
- 21. [Configuration reference](https://grok-wiki.com/public/docs/xai-org-grok-build-90205de50458/pages/21-configuration-reference.md) - Schema-oriented keys for ~/.grok/config.toml and pager.toml: [cli], [models], [ui], [features], [session], [tools], [mcp_servers], [skills], [compat], permissions, sandbox, and env var counterparts.
- 22. [Slash commands](https://grok-wiki.com/public/docs/xai-org-grok-build-90205de50458/pages/22-slash-commands.md) - Interactive / commands for sessions, models, memory, hooks, plugins, themes, and more; pager vs shell builtins; skill-provided commands in the autocomplete menu.
- 23. [Keyboard shortcuts](https://grok-wiki.com/public/docs/xai-org-grok-build-90205de50458/pages/23-keyboard-shortcuts.md) - TUI key bindings for prompt and scrollback focus, cancel, rewind, vim mode, mouse actions, and multi-key Escape behavior with timing constraints.
- 24. [Troubleshooting](https://grok-wiki.com/public/docs/xai-org-grok-build-90205de50458/pages/24-troubleshooting.md) - Terminal support issues (tmux, SSH, truecolor, OSC 52 clipboard), color warnings, auth recovery, MCP startup failures, and diagnostics probes backed by the pager diagnostics module.
- 25. [Build from source](https://grok-wiki.com/public/docs/xai-org-grok-build-90205de50458/pages/25-build-from-source.md) - Workspace build of xai-grok-pager-bin: Rust toolchain pin, protoc/dotslash, cargo run/check/test per-crate, binary name xai-grok-pager vs shipped grok, and contribution policy constraints.

## Source File Index

- `bin/protoc`
- `Cargo.toml`
- `clippy.toml`
- `CONTRIBUTING.md`
- `crates/codegen/xai-acp-lib/src/gateway.rs`
- `crates/codegen/xai-acp-lib/src/lib.rs`
- `crates/codegen/xai-agent-lifecycle/src/lib.rs`
- `crates/codegen/xai-chat-state/src/lib.rs`
- `crates/codegen/xai-grok-agent/src`
- `crates/codegen/xai-grok-auth/src/lib.rs`
- `crates/codegen/xai-grok-config-types/src/lib.rs`
- `crates/codegen/xai-grok-config-types/src/mcp.rs`
- `crates/codegen/xai-grok-config-types/src/permission.rs`
- `crates/codegen/xai-grok-config/src/loader.rs`
- `crates/codegen/xai-grok-config/src/paths.rs`
- `crates/codegen/xai-grok-config/src/validation.rs`
- `crates/codegen/xai-grok-env/src`
- `crates/codegen/xai-grok-hooks/src/discovery.rs`
- `crates/codegen/xai-grok-hooks/src/dispatcher.rs`
- `crates/codegen/xai-grok-hooks/src/event.rs`
- `crates/codegen/xai-grok-hooks/src/runner`
- `crates/codegen/xai-grok-mcp/src/lib.rs`
- `crates/codegen/xai-grok-mcp/src/servers.rs`
- `crates/codegen/xai-grok-mcp/src/wire.rs`
- `crates/codegen/xai-grok-memory/src/dream.rs`
- `crates/codegen/xai-grok-memory/src/lib.rs`
- `crates/codegen/xai-grok-memory/src/search.rs`
- `crates/codegen/xai-grok-models/src/catalog.rs`
- `crates/codegen/xai-grok-models/src/lib.rs`
- `crates/codegen/xai-grok-pager-bin/Cargo.toml`
- `crates/codegen/xai-grok-pager-bin/src/main.rs`
- `crates/codegen/xai-grok-pager-render/src`
- `crates/codegen/xai-grok-pager/docs/custom-hooks.md`
- `crates/codegen/xai-grok-pager/docs/user-guide/01-getting-started.md`
- `crates/codegen/xai-grok-pager/docs/user-guide/02-authentication.md`
- `crates/codegen/xai-grok-pager/docs/user-guide/03-keyboard-shortcuts.md`
- `crates/codegen/xai-grok-pager/docs/user-guide/04-slash-commands.md`
- `crates/codegen/xai-grok-pager/docs/user-guide/05-configuration.md`
- `crates/codegen/xai-grok-pager/docs/user-guide/06-theming.md`
- `crates/codegen/xai-grok-pager/docs/user-guide/07-mcp-servers.md`
- `crates/codegen/xai-grok-pager/docs/user-guide/08-skills.md`
- `crates/codegen/xai-grok-pager/docs/user-guide/09-plugins.md`
- `crates/codegen/xai-grok-pager/docs/user-guide/10-hooks.md`
- `crates/codegen/xai-grok-pager/docs/user-guide/11-custom-models.md`
- `crates/codegen/xai-grok-pager/docs/user-guide/12-project-rules.md`
- `crates/codegen/xai-grok-pager/docs/user-guide/13-memory.md`
- `crates/codegen/xai-grok-pager/docs/user-guide/14-headless-mode.md`
- `crates/codegen/xai-grok-pager/docs/user-guide/15-agent-mode.md`
- `crates/codegen/xai-grok-pager/docs/user-guide/16-subagents.md`
- `crates/codegen/xai-grok-pager/docs/user-guide/17-sessions.md`
- `crates/codegen/xai-grok-pager/docs/user-guide/18-sandbox.md`
- `crates/codegen/xai-grok-pager/docs/user-guide/19-plan-mode.md`
- `crates/codegen/xai-grok-pager/docs/user-guide/21-terminal-support.md`
- `crates/codegen/xai-grok-pager/docs/user-guide/22-permissions-and-safety.md`
- `crates/codegen/xai-grok-pager/docs/user-guide/README.md`
- `crates/codegen/xai-grok-pager/scripts/install.ps1`
- `crates/codegen/xai-grok-pager/scripts/install.sh`
- `crates/codegen/xai-grok-pager/src/app/cli.rs`
- `crates/codegen/xai-grok-pager/src/app/dispatch/router.rs`
- `crates/codegen/xai-grok-pager/src/app/mod.rs`
- `crates/codegen/xai-grok-pager/src/diagnostics.rs`
- `crates/codegen/xai-grok-pager/src/headless.rs`
- `crates/codegen/xai-grok-pager/src/input`
- `crates/codegen/xai-grok-pager/src/mcp_cmd.rs`
- `crates/codegen/xai-grok-pager/src/memory_cmd.rs`
- `crates/codegen/xai-grok-pager/src/models.rs`
- `crates/codegen/xai-grok-pager/src/plugin_cmd.rs`
- `crates/codegen/xai-grok-pager/src/sessions_cmd.rs`
- `crates/codegen/xai-grok-pager/src/settings`
- `crates/codegen/xai-grok-pager/src/slash/commands/mod.rs`
- `crates/codegen/xai-grok-pager/src/startup.rs`
- `crates/codegen/xai-grok-paths/src/lib.rs`
- `crates/codegen/xai-grok-plugin-marketplace/src/index.rs`
- `crates/codegen/xai-grok-plugin-marketplace/src/installer.rs`
- `crates/codegen/xai-grok-plugin-marketplace/src/lib.rs`
- `crates/codegen/xai-grok-sandbox/src/deny`
- `crates/codegen/xai-grok-sandbox/src/lib.rs`
- `crates/codegen/xai-grok-sandbox/src/profiles.rs`
- `crates/codegen/xai-grok-sandbox/src/types.rs`
- `crates/codegen/xai-grok-shell/README.md`
- `crates/codegen/xai-grok-shell/skills`
- `crates/codegen/xai-grok-shell/src/auth/device_code.rs`
- `crates/codegen/xai-grok-shell/src/auth/flow.rs`
- `crates/codegen/xai-grok-shell/src/auth/manager.rs`
- `crates/codegen/xai-grok-shell/src/auth/recovery.rs`
- `crates/codegen/xai-grok-shell/src/auth/storage.rs`
- `crates/codegen/xai-grok-shell/src/cli_models.rs`
- `crates/codegen/xai-grok-shell/src/extensions/mod.rs`
- `crates/codegen/xai-grok-shell/src/inspect/mod.rs`
- `crates/codegen/xai-grok-shell/src/leader/mod.rs`
- `crates/codegen/xai-grok-shell/src/mcp_doctor.rs`
- `crates/codegen/xai-grok-shell/src/plugin.rs`
- `crates/codegen/xai-grok-shell/src/sampling/mod.rs`
- `crates/codegen/xai-grok-shell/src/session/acp_session.rs`
- `crates/codegen/xai-grok-shell/src/session/acp_types.rs`
- `crates/codegen/xai-grok-shell/src/session/chat_persistence.rs`
- `crates/codegen/xai-grok-shell/src/session/commands.rs`
- `crates/codegen/xai-grok-shell/src/session/compaction.rs`
- `crates/codegen/xai-grok-shell/src/session/goal_orchestrator.rs`
- `crates/codegen/xai-grok-shell/src/session/goal_planner.rs`
- `crates/codegen/xai-grok-shell/src/session/goal_strategist.rs`
- `crates/codegen/xai-grok-shell/src/session/mod.rs`
- `crates/codegen/xai-grok-shell/src/util/config/load.rs`
- `crates/codegen/xai-grok-shell/src/util/config/mcp.rs`
- `crates/codegen/xai-grok-shell/src/util/config/permissions.rs`
- `crates/codegen/xai-grok-shell/src/util/config/resolve/features.rs`
- `crates/codegen/xai-grok-shell/src/util/config/resolve/mod.rs`
- `crates/codegen/xai-grok-shell/src/util/config/resolve/system_prompt.rs`
- `crates/codegen/xai-grok-shell/src/util/config/resolve/tool_approvals.rs`
- `crates/codegen/xai-grok-shell/src/util/hooks.rs`
- `crates/codegen/xai-grok-subagent-resolution/src/lib.rs`
- `crates/codegen/xai-grok-subagent-resolution/src/types.rs`
- `crates/codegen/xai-grok-tools/src/implementations/skills`
- `crates/codegen/xai-grok-tools/src/lib.rs`
- `crates/codegen/xai-grok-tools/src/registry/mod.rs`
- `crates/codegen/xai-grok-update/src/auto_update.rs`
- `crates/codegen/xai-grok-update/src/version.rs`
- `crates/codegen/xai-hooks-plugins-types/src`
- `crates/codegen/xai-ratatui-textarea/src`
- `crates/codegen/xai-tty-utils/src`
- `README.md`
- `rust-toolchain.toml`

---

## 01. Overview

> What Grok Build exposes (interactive TUI, headless -p, ACP agent), who should use each surface, and the shortest path from install to a successful first turn.

- Page Markdown: https://grok-wiki.com/public/docs/xai-org-grok-build-90205de50458/pages/01-overview.md
- Generated: 2026-07-16T01:18:38.083Z

### Source Files

- `README.md`
- `crates/codegen/xai-grok-pager/docs/user-guide/README.md`
- `crates/codegen/xai-grok-pager/docs/user-guide/01-getting-started.md`
- `crates/codegen/xai-grok-shell/README.md`
- `crates/codegen/xai-grok-pager-bin/src/main.rs`
- `crates/codegen/xai-grok-pager/src/app/cli.rs`
- `Cargo.toml`

---
title: "Overview"
description: "What Grok Build exposes (interactive TUI, headless -p, ACP agent), who should use each surface, and the shortest path from install to a successful first turn."
---

The shipped `grok` binary is SpaceXAI’s terminal AI coding agent. Default invocation opens the interactive TUI; `-p` / `--single` (or `--prompt-file` / `--prompt-json`) runs a non-interactive single-turn harness; `grok agent stdio` speaks Agent Client Protocol (ACP) JSON-RPC over stdin/stdout for IDEs and custom clients. The same agent runtime, tools, sessions, permissions, and config back all three surfaces.

## Product surfaces

| Surface | Entry | Process model | Primary audience |
| --- | --- | --- | --- |
| Interactive TUI | `grok` | Full-screen UI; optional positional first prompt | Day-to-day coding in a terminal |
| Headless single-turn | `grok -p "…"` / `--single` / `--prompt-file` / `--prompt-json` | One prompt, tools, stdout, exit | Scripts, CI, pipelines |
| ACP agent | `grok agent stdio` | Long-lived JSON-RPC server on stdio | IDE extensions, SDKs, host apps |

```mermaid
flowchart TB
  subgraph entry["Entry — xai-grok-pager-bin"]
    CLI["PagerArgs / Command"]
  end
  subgraph surfaces["Surfaces"]
    TUI["Interactive TUI\ngrok"]
    HL["Headless single-turn\ngrok -p"]
    ACP["ACP stdio\ngrok agent stdio"]
    AOTH["agent serve / leader /\nagent headless relay"]
  end
  subgraph runtime["Shared runtime — xai-grok-shell"]
    SESS["Sessions"]
    TOOLS["Tools + MCP"]
    AUTH["Auth + models"]
    CFG["config.toml / rules"]
  end
  CLI --> TUI
  CLI --> HL
  CLI --> ACP
  CLI --> AOTH
  TUI --> runtime
  HL --> runtime
  ACP --> runtime
  AOTH --> runtime
```

<Note>
`grok -p` (print / single-turn) is not the same path as `grok agent headless`. The latter attaches the agent to a Grok WebSocket relay for remote/headless *session* hosting. For one-shot automation, use `-p`.
</Note>

### Interactive TUI

Default when there is no subcommand and no headless prompt flags:

```bash
grok
grok "fix the failing auth test and run it"
grok --cwd ~/projects/my-app -m grok-build
```

The UI is split into **scrollback** (conversation, tool output, diffs) and **prompt** (input). Core interaction:

- `Enter` sends the prompt; tool runs stream into scrollback
- `@path` attaches files or line ranges via the fuzzy picker
- `/…` slash commands for session, model, compact, permissions, etc.
- Default permission prompts before shell and edits; `Ctrl+O`, `/always-approve`, or `--yolo` / `--always-approve` for always-approve

Use this surface when you want a human in the loop for multi-step work, review of edits, and interactive approvals.

### Headless single-turn (`-p`)

Headless mode is selected when any of `-p` / `--single`, `--prompt-json`, or `--prompt-file` is set. The binary runs `run_single_turn`, prints to stdout, and exits.

```bash
grok -p "Explain this codebase"
grok -p "Review changes for bugs" --output-format json --yolo
grok --prompt-file ./task.md --output-format streaming-json
```

| Flag / option | Role |
| --- | --- |
| `-p, --single <PROMPT>` | Prompt text (aliases include `--print`) |
| `--prompt-file <PATH>` | Prompt from file |
| `--prompt-json <JSON>` | Prompt as JSON content blocks |
| `--output-format` | `plain` (default), `json`, `streaming-json` |
| `--tools` / `--disallowed-tools` | Headless-only tool allow/deny lists |
| `--max-turns <N>` | Cap agentic turns (headless-only) |
| `--yolo` / `--always-approve` | Auto-approve tool execution |
| `-r` / `-c` / `--fork-session` | Resume, continue, or fork sessions |

Use this surface for CI reviews, scripted refactors, and anything that must not open a TUI. Prefer `XAI_API_KEY` or pre-hydrated credentials in non-browser environments.

### Agent mode (ACP)

```bash
grok agent stdio
grok agent --model grok-build --always-approve stdio
```

`grok agent stdio` runs `run_stdio_agent`: JSON-RPC over stdin/stdout (ACP session lifecycle, streaming `session/update`, tool visibility, permission requests). Related agent modes on the same subcommand:

| Mode | Command | Role |
| --- | --- | --- |
| stdio | `grok agent stdio` | IDE / client embedding (primary) |
| serve | `grok agent serve --bind 127.0.0.1:2419` | Local WebSocket server |
| headless | `grok agent headless` | Outbound WebSocket relay client |
| leader | `grok agent leader` | Shared backend for multiple clients |

Use ACP when a host process owns the UI (editor, desktop shell, custom app) and needs structured streaming rather than a terminal UI or a one-shot exit.

## Shortest path to a first turn

<Steps>
  <Step title="Install the released binary">
    ```bash
    curl -fsSL https://x.ai/cli/install.sh | bash   # macOS / Linux / Git Bash
    # Windows PowerShell:
    # irm https://x.ai/cli/install.ps1 | iex
    grok --version
    ```
    Installers place the binary under `~/.grok/bin` (and User PATH on Windows PowerShell). Pin with `bash -s <version>` or `$env:GROK_VERSION=…` as documented on Installation.
  </Step>
  <Step title="Authenticate">
    Interactive default opens a browser and writes credentials to `~/.grok/auth.json`. For CI or no-browser hosts:

    ```bash
    export XAI_API_KEY="xai-..."
    # or
    grok login --device-auth
    ```
  </Step>
  <Step title="Run one successful turn">
    <Tabs>
      <Tab title="TUI">
        ```bash
        cd /path/to/your/repo
        grok
        ```
        Type a prompt, press `Enter`, confirm tool output appears in scrollback.
      </Tab>
      <Tab title="Headless">
        ```bash
        grok -p "Summarize the top-level layout of this repository"
        ```
        Expect plain text on stdout and process exit when the turn finishes.
      </Tab>
      <Tab title="ACP smoke">
        ```bash
        grok agent stdio
        ```
        Leave the process running; an ACP client drives `initialize` → `session/new` → `session/prompt`. Without a client, use the TUI or `-p` for a human-visible first turn.
      </Tab>
    </Tabs>
  </Step>
</Steps>

<Check>
First-turn success signals: `grok --version` prints a version; auth does not loop; a prompt produces model text and (when tools run) streamed tool results in the TUI or headless output.
</Check>

## Shared runtime and home layout

All surfaces share the agent stack under `xai-grok-shell` (sessions, tools, MCP, models, memory) and workspace tooling under `xai-grok-tools` / `xai-grok-workspace`. User state defaults to `~/.grok` (override with `GROK_HOME`).

| Path | Purpose |
| --- | --- |
| `~/.grok/bin/` | Released binary install location |
| `~/.grok/auth.json` | OAuth / session credentials |
| `~/.grok/config.toml` | Main config (`[cli]`, `[models]`, `[ui]`, MCP, features, …) |
| `~/.grok/sessions/` | Persisted conversations (by working directory) |
| `~/.grok/memory/`, `skills/`, `plugins/`, `agents/` | Cross-session memory and extensions |
| Project `.grok/`, `AGENTS.md` | Repo-local config, skills, and instruction files |

Config precedence (high → low): CLI flags → environment variables → user `config.toml` → managed/requirements config → built-in defaults. Models can be SpaceXAI defaults or BYOK / OpenAI-compatible / Ollama endpoints—provider choice is configuration, not a hard-wired host requirement for every deployment.

## Repository composition (this tree)

This repository is the Rust workspace for the CLI/TUI and agent runtime (periodically synced from the SpaceXAI monorepo).

| Path | Role |
| --- | --- |
| `crates/codegen/xai-grok-pager-bin` | Composition root; builds `xai-grok-pager` (released as `grok`) |
| `crates/codegen/xai-grok-pager` | TUI, CLI parse (`PagerArgs` / `Command`), headless single-turn |
| `crates/codegen/xai-grok-shell` | Agent runtime: stdio ACP, leader, serve, relay headless |
| `crates/codegen/xai-grok-tools` | Built-in tools (shell, edit, search, web, subagents, …) |
| `crates/codegen/xai-grok-workspace` | Filesystem, VCS, execution, checkpoints |
| `crates/codegen/xai-grok-pager/docs/user-guide/` | In-tree user guide (source for many product docs) |

From-source build (maintainer path):

```bash
cargo run -p xai-grok-pager-bin              # build + launch
cargo build -p xai-grok-pager-bin --release  # target/release/xai-grok-pager
```

Root `Cargo.toml` is generated; prefer per-crate manifests. External contributions are not accepted (see `CONTRIBUTING.md`).

## Common launch flags (all surfaces)

| Flag | Effect |
| --- | --- |
| `-m, --model <ID>` | Model selection |
| `--cwd <PATH>` | Working directory |
| `--rules <TEXT>` | Extra system-prompt rules |
| `--yolo` / `--always-approve` | Auto-approve tools |
| `--sandbox <PROFILE>` | OS isolation profile |
| `-r` / `-c` / `-s` | Resume / continue / new session UUID |
| `-w, --worktree` | Session in a new git worktree |

Top-level management commands include `login`, `logout`, `mcp`, `plugin`, `memory`, `models`, `sessions`, `inspect`, `setup`, `update`, `export`, and `trace`—see CLI reference.

## Next

<CardGroup>
  <Card title="Installation" href="/installation">
    Install and update the released binary; PATH under ~/.grok/bin; version pins.
  </Card>
  <Card title="Quickstart" href="/quickstart">
    First interactive session: auth, prompts, @ attachments, cancel, scrollback.
  </Card>
  <Card title="Authentication" href="/authentication">
    Browser OAuth, device-code, XAI_API_KEY, auth.json, token refresh.
  </Card>
  <Card title="Headless mode" href="/headless-mode">
    -p flags, output formats, tool filters, CI patterns.
  </Card>
  <Card title="Agent mode (ACP)" href="/agent-mode">
    grok agent stdio, JSON-RPC lifecycle, serve/relay options.
  </Card>
  <Card title="CLI reference" href="/cli-reference">
    Full command and shared runtime flag inventory.
  </Card>
</CardGroup>

---

## 02. Installation

> Install and update the released grok binary on macOS, Linux, and Windows; PATH layout under ~/.grok/bin; version pin flags; verify with grok --version.

- Page Markdown: https://grok-wiki.com/public/docs/xai-org-grok-build-90205de50458/pages/02-installation.md
- Generated: 2026-07-16T01:18:43.721Z

### Source Files

- `README.md`
- `crates/codegen/xai-grok-pager/docs/user-guide/01-getting-started.md`
- `crates/codegen/xai-grok-pager/scripts/install.sh`
- `crates/codegen/xai-grok-pager/scripts/install.ps1`
- `crates/codegen/xai-grok-update/src/auto_update.rs`
- `crates/codegen/xai-grok-update/src/version.rs`
- `crates/codegen/xai-grok-paths/src/lib.rs`

---
title: "Installation"
description: "Install and update the released grok binary on macOS, Linux, and Windows; PATH layout under ~/.grok/bin; version pin flags; verify with grok --version."
---

Released `grok` binaries install into `$GROK_HOME` (default `~/.grok`) via platform bootstrap scripts hosted at `https://x.ai/cli`, or via npm (`@xai-official/grok`). The scripts place a managed binary under `bin/`, record `installer = "internal"` in `config.toml`, and wire PATH so `grok` and `agent` resolve immediately or after a shell restart.

## Supported platforms

| OS | Architectures | Installer |
|----|---------------|-----------|
| macOS | `x86_64`, `aarch64` | `install.sh` |
| Linux | `x86_64`, `aarch64` | `install.sh` |
| Windows | `x86_64`, `aarch64` | `install.ps1` (native), or `install.sh` under Git Bash / MSYS2 / Cygwin |
| WSL | Linux arch of the guest | `install.sh` (Linux binary) |

Unsupported `uname` OS/arch pairs exit with an explicit error. Artifacts are named `grok-<version>-<os>-<arch>` (`.exe` on Windows).

## Install latest stable

<Tabs>
<Tab title="macOS / Linux / Git Bash">

```bash
curl -fsSL https://x.ai/cli/install.sh | bash
```

Requires `curl` or `wget`. The script probes `https://x.ai/cli/<channel>` first, then falls back to the public GCS mirror when the primary is unreachable.

</Tab>
<Tab title="Windows PowerShell">

```powershell
irm https://x.ai/cli/install.ps1 | iex
```

Windows-only. PowerShell 5.1 enables TLS 1.2 automatically for the download. Adds `%USERPROFILE%\.grok\bin` to the User PATH and the current session.

</Tab>
<Tab title="npm">

```bash
npm i -g @xai-official/grok
```

Platform packages ship optional native binaries. Updates later go through `npm` or `grok update` when the installer is detected as `npm`.

</Tab>
</Tabs>

## Pin a version at install time

Version strings must match semver: `X.Y.Z` or `X.Y.Z-suffix` (for example `0.1.42`, `0.1.151-alpha.2`).

<CodeGroup>

```bash title="install.sh"
curl -fsSL https://x.ai/cli/install.sh | bash -s 0.1.42
```

```powershell title="install.ps1 (env)"
$env:GROK_VERSION="0.1.42"; irm https://x.ai/cli/install.ps1 | iex
```

```powershell title="install.ps1 (param)"
& ([scriptblock]::Create((irm https://x.ai/cli/install.ps1))) -Version 0.1.42
```

</CodeGroup>

Invalid formats fail before any download.

## Verify

```bash
grok --version
# aliases: grok -v, grok -V, grok version
```

`--version` / `-v` / `-V` print the compiled version plus an optional channel label derived from the cached stable pointer (for example ` [stable]` or ` [alpha]`). After install, prefer the managed path if PATH is not refreshed yet:

```bash
~/.grok/bin/grok --version          # Unix
%USERPROFILE%\.grok\bin\grok.exe --version   # Windows
```

On Unix, the bootstrap script smoke-tests the downloaded binary with `--version` before swapping links; a failed smoke test aborts and leaves the previous install in place.

## PATH and on-disk layout

Default home is `~/.grok` (canonicalized). Override with `GROK_HOME`. Binary directory defaults to `$GROK_HOME/bin` and can be overridden with `GROK_BIN_DIR` at install time only.

:::files
~/.grok/
├── bin/
│   ├── grok          # Unix: symlink → ../downloads/grok-<ver>-<platform>
│   ├── agent         # same target (or .exe copies on Windows)
│   ├── grok.exe      # Windows copies (not symlinks)
│   └── agent.exe
├── downloads/
│   └── grok-<version>-<os>-<arch>[.exe]
├── completions/      # bash / zsh / powershell scripts (best-effort)
├── config.toml       # [cli] installer, channel, auto_update, …
├── version.json      # update TTL cache
├── managed_config.toml   # deployment-key installs only
└── requirements.toml     # deployment-key installs only
:::

### Unix install behavior

1. Download into `~/.grok/downloads/grok-<platform>` (versioned file after install path runs).
2. `chmod +x`, smoke-test `--version`.
3. Symlink `~/.grok/bin/grok` and `~/.grok/bin/agent` to a **relative** target (`../downloads/...`) when bin and downloads share a parent — survives Docker bind-mounts with a different `$HOME`.
4. Generate shell completions under `~/.grok/completions/{bash,zsh}` and Fish’s auto-load dir when writable.
5. Write/update `[cli]` in `config.toml` with `installer = "internal"` and optional non-stable `channel`.
6. If `~/.grok/bin` is not already on `PATH`, try a writable dir already on PATH (`~/.local/bin`, then `/usr/local/bin`) and symlink `grok`/`agent` there.
7. Append or refresh a marked block in `~/.bashrc`, `~/.zshrc`, or Fish config so `$HOME/.grok/bin` is on PATH for future sessions. zsh also adds completion `fpath`; bash sources the bash completion file when readable.

### Windows install behavior

1. Download `grok-<version>-windows-<arch>.exe` into `%USERPROFILE%\.grok\downloads\`.
2. **Copy** to `bin\grok.exe` and `bin\agent.exe` (symlinks need Developer Mode; locked executables are renamed to `*.old` then replaced).
3. Prepend `%USERPROFILE%\.grok\bin` to the User `Path` and the current process `Path`.
4. Same `[cli]` config persistence as Unix.

Canonical application path used by the runtime for restarts: `$GROK_HOME/bin/grok` or `grok.exe`.

## Update after install

```bash
grok update
```

`grok update` reuses the recorded installer (`internal`, `npm`, or `gh-release`). For the default CDN install, it downloads the channel pointer target and rewrites the managed bin links.

| Flag | Behavior |
|------|----------|
| `--check` | Report whether an update is available; no install |
| `--json` | Machine-readable status; **requires** `--check` |
| `--force-reinstall` | Re-download even when already current |
| `--version <semver>` | Install that exact version (pin) |
| `--alpha` | Switch channel to `alpha` and update |
| `--stable` | Switch channel to `stable` and update |
| `--enterprise` | Switch channel to `enterprise` (hidden flag) |

Channel flags are mutually exclusive. Switching persists `cli.channel` in `config.toml`.

### Pin after install

```bash
grok update --version 0.1.150
```

Pinned installs set `cli.auto_update = false` so automatic upgrades do not override the pin. Re-enable auto-update in config (or accept an interactive update prompt path) when you want channel tracking again.

### Status check

```bash
grok update --check
grok update --check --json
```

JSON fields include `currentVersion`, `latestVersion`, `updateAvailable`, `installer`, `channel`, `autoUpdate`, and optional `error`.

npm users can also run:

```bash
npm i -g @xai-official/grok@latest
```

## Release channels and artifact URLs

| Env / config | Values | Default |
|--------------|--------|---------|
| `GROK_CHANNEL` (install scripts) | `stable`, `alpha`, `enterprise` | `stable` |
| `cli.channel` (`config.toml`) | same | `stable` |

Channel pointer (plain-text semver):

- Primary: `https://x.ai/cli/<channel>`
- Fallback: `https://storage.googleapis.com/grok-build-public-artifacts/cli/<channel>`

Binary artifact:

- `https://x.ai/cli/grok-<version>-<os>-<arch>[.exe]`
- Same path under the GCS fallback base

Alpha channel resolution takes the semver-greater of the alpha and stable pointers (and the same rule for npm/`gh` backends) so alpha installs are not stranded behind a newer stable.

## Auto-update

When `cli.auto_update` is unset, the first update check treats it as enabled and persists `true`. Explicit `cli.auto_update = false` skips automatic checks.

Suppression gates (any one disables auto-update checks):

- Debug builds (`debug_assertions`)
- `--no-auto-update` (hidden session flag)
- `GROK_DISABLE_AUTOUPDATER` set in the environment

Version freshness is cached in `~/.grok/version.json` with a ~30 minute TTL. Background TUI/leader paths may spawn `grok update` as a detached child when the on-disk install lags the channel pointer; internal/gh-release installs allow intentional channel rollbacks, while npm never auto-downgrades (stale registry safety).

`cli.minimum_version` (any config layer) can refuse startup below a floor and, when auto-update is on, install `max(latest, minimum)`.

## Installer environment variables

| Variable | Used by | Role |
|----------|---------|------|
| `GROK_HOME` | runtime | Config/binary root; default `~/.grok` |
| `GROK_BIN_DIR` | install scripts | Override install bin directory |
| `GROK_CHANNEL` | install scripts | Channel pointer (`stable` / `alpha` / `enterprise`) |
| `GROK_VERSION` | `install.ps1` | Pin version when using `irm \| iex` |
| `GROK_DEPLOYMENT_KEY` | install scripts | Auth for deployment config pull; takes precedence over `auth.json` tokens |
| `GROK_PROXY_URL` | install scripts | Deployment config API base (default `https://cli-chat-proxy.grok.com/v1`) |
| `GROK_INSTALLER` | updater | Force `npm` / `internal` / `gh-release` |
| `GROK_MANAGED_BY_NPM` / `GROK_MANAGED_BY_INTERNAL` | updater | Installer detection hints |
| `GROK_DISABLE_AUTOUPDATER` | runtime | Disable automatic update checks |
| `NPM_TOKEN` | npm install path | Optional private registry auth for npm-backed updates |

Without a deployment key, install still works for public artifacts. If present, installers may also write `managed_config.toml` and `requirements.toml` from the deployment config endpoint.

## Completions

Best-effort generation at install time:

| Shell | Path |
|-------|------|
| bash | `~/.grok/completions/bash/grok.bash` |
| zsh | `~/.grok/completions/zsh/_grok` |
| fish | `~/.config/fish/completions/grok.fish` |
| PowerShell | `~/.grok/completions/powershell/grok.ps1` |

Regenerate anytime:

```bash
grok completions bash
grok completions zsh
grok completions fish
grok completions powershell
```

## Failure modes

| Symptom | Likely cause | Fix |
|---------|--------------|-----|
| `Either curl or wget is required` | No downloader on PATH | Install `curl` or `wget` |
| `Unsupported OS` / architecture | Platform not published | Use a supported host or [build from source](/build-from-source) |
| Primary URL unreachable note | Cloudflare path down | Script falls back to GCS automatically |
| Binary download 404 | Version/platform not published | Pin a known version; check channel |
| `downloaded grok failed to run` (Unix) | Corrupt/wrong-arch binary | Previous install kept; retry or pin another version |
| Windows `Failed to install grok.exe` | File lock / permissions | Close running `grok`, retry; check antivirus locks |
| `grok: command not found` | PATH not updated | Restart shell; or `export PATH="$HOME/.grok/bin:$PATH"`; Windows: open a new terminal after User PATH write |
| `Auto-update is not available for manual installations` | No known installer recorded | Re-run bootstrap install, or set `cli.installer` / `GROK_INSTALLER` |
| Pinned version never upgrades | `auto_update=false` after `--version` pin | `grok update` without pin, or set `cli.auto_update = true` |

## Build-from-source note

Workspace builds produce `xai-grok-pager` under `target/release/`; official installers ship the same product as `grok`. Source builds are not managed by the CDN installer layout. See [Build from source](/build-from-source).

## Next

<CardGroup>
<Card title="Quickstart" href="/quickstart">
Launch the TUI, authenticate, and run a first turn.
</Card>
<Card title="Authentication" href="/authentication">
Browser OAuth, device code, and `XAI_API_KEY` for CI.
</Card>
<Card title="Configure Grok" href="/configure-grok">
`config.toml` precedence, channels, and auto-update keys.
</Card>
<Card title="CLI reference" href="/cli-reference">
Full `grok` command and flag surface including `update`.
</Card>
<Card title="Build from source" href="/build-from-source">
Rust toolchain pin, `cargo run -p xai-grok-pager-bin`, binary naming.
</Card>
<Card title="Troubleshooting" href="/troubleshooting">
Terminal, auth, and runtime diagnostics after install.
</Card>
</CardGroup>

---

## 03. Quickstart

> First interactive session: launch grok, authenticate, send a prompt, use @ file attachments, cancel turns, and confirm tool output in the TUI scrollback.

- Page Markdown: https://grok-wiki.com/public/docs/xai-org-grok-build-90205de50458/pages/03-quickstart.md
- Generated: 2026-07-16T01:19:49.241Z

### Source Files

- `crates/codegen/xai-grok-pager/docs/user-guide/01-getting-started.md`
- `crates/codegen/xai-grok-pager/docs/user-guide/03-keyboard-shortcuts.md`
- `crates/codegen/xai-grok-pager/src/app/cli.rs`
- `crates/codegen/xai-grok-pager/src/startup.rs`
- `crates/codegen/xai-grok-shell/README.md`
- `crates/codegen/xai-grok-tools/src/lib.rs`

---
title: "Quickstart"
description: "First interactive session: launch grok, authenticate, send a prompt, use @ file attachments, cancel turns, and confirm tool output in the TUI scrollback."
---

`grok` with no subcommand starts the interactive **Grok Build TUI** (`PagerArgs` in the pager binary): a full-screen agent screen with a **scrollback** transcript and a bottom **prompt** composer. The first successful session is: install on `PATH`, authenticate into `~/.grok/auth.json` (or `XAI_API_KEY`), send a turn, optionally attach files with `@`, cancel with `Ctrl+C` when needed, and verify tool results streaming into scrollback.

<Info>
Requires a released `grok` binary on `PATH` (`grok --version`). Install and update steps live on the [Installation](/installation) page.
</Info>

## Prerequisites

| Check | Command / path | Expected signal |
|-------|----------------|-----------------|
| Binary installed | `grok --version` | Version string, exit 0 |
| Writable home layout | `~/.grok/` | Created on first run for auth, config, sessions |
| Project directory | `cd` into a repo (or use `--cwd`) | Tools operate relative to that working directory |

```bash
grok --version
```

## First interactive session

<Steps>
<Step title="Launch the TUI">
From a project directory:

```bash
grok
```

Or seed the first turn with a positional prompt (interactive only; not headless `-p`):

```bash
grok "summarize this repo and list the top risks"
```

Useful first-session flags:

| Flag | Effect |
|------|--------|
| `--cwd <PATH>` | Working directory (default: current directory) |
| `-m` / `--model <MODEL>` | Model ID for this session |
| `--rules <TEXT>` | Extra rules appended to the system prompt |
| `--always-approve` (aliases: `--yolo`, `--dangerously-skip-permissions`) | Auto-approve tool executions |
| `-c` / `--continue` | Continue the most recent session for this cwd |
| `-r` / `--resume [SESSION_ID]` | Resume a session (most recent if ID omitted) |
| `-w` / `--worktree [NAME]` | Start in a new git worktree; use `--worktree=name` when also passing a positional prompt |
| `--worktree-ref` / `--ref` | Base ref for the worktree (requires `--worktree`) |
| `--minimal` / `--fullscreen` | Sticky screen mode preference under `[ui] screen_mode` in `~/.grok/config.toml` |

```bash
grok --cwd ~/projects/my-app -m grok-build --rules "Prefer TypeScript. Prefer functional components."
```

<Warning>
With `--worktree`, pass the name as `--worktree=feat` (equals form) when a positional prompt follows. `grok -w "refactor X"` treats `"refactor X"` as the worktree label, not the prompt.
</Warning>
</Step>

<Step title="Authenticate">
On first launch without credentials, Grok starts browser OAuth (SpaceXAI at `auth.x.ai` / grok.com sign-in). Successful login writes credentials to `~/.grok/auth.json`. Tokens refresh in the background; when refresh fails, the TUI prompts you to sign in again. Credentials without a server-provided expiry fall back to a **30-day** lifetime.

Re-auth or switch account:

```bash
grok login
```

| Flow | Command | When to use |
|------|---------|-------------|
| Browser OAuth (default) | `grok` or `grok login` / `grok login --oauth` | Local machine with a browser |
| Device code | `grok login --device-auth` (alias `--device-code`) | Headless / remote / no browser on the target machine |
| API key | `export XAI_API_KEY="xai-..."` then `grok` | CI and non-interactive hosts ([console.x.ai](https://console.x.ai)) |
| Sign out | `grok logout` | Clears cached credentials |

<Note>
A stored interactive session token takes precedence over `XAI_API_KEY`. Use `grok logout` (or remove `~/.grok/auth.json`) when you need the API key path.
</Note>

Verification: the welcome/agent screen loads without a login loop, and a short prompt returns a model response.
</Step>

<Step title="Send a prompt">
The TUI has two panes:

- **Scrollback** — conversation history: user prompts, agent markdown, thinking blocks, tool calls (including shell output and file diffs), task lists.
- **Prompt** — composer at the bottom.

| Action | Binding |
|--------|---------|
| Send | `Enter` (prompt focused) |
| Newline | `Shift+Enter` or `Alt+Enter` |
| Multiline toggle | `Ctrl+M` (prompt focused) |
| Focus scrollback ↔ prompt | `Tab` (`Space` or letter keys also focus the prompt from scrollback in simple mode) |
| Command palette | `Ctrl+P` or `?` |

Type a task and press `Enter`. The agent may read files, run shell commands, search, and edit code. Each tool invocation streams into scrollback in real time.

Slash commands (type `/` for autocomplete):

```text
/model grok-build
/compact
/always-approve
/new
```
</Step>

<Step title="Attach files with @">
In the prompt, type `@` to open the fuzzy file picker. Select with `Tab` or `Enter`.

```text
@src/main.rs              # whole file
@src/main.rs:10-50        # line range
@src/                     # directory browse (trailing /)
```

Defaults:

- Respects `.gitignore`
- Hides dotfiles

Prefix the query with `!` to include ignored/hidden paths:

```text
@!.github
@!.env
@!.vscode/settings.json
```

Attached paths are included with the user message so the model can ground on that content without an extra discovery turn.
</Step>

<Step title="Cancel a running turn">
While a turn is running:

| State | Key | Effect |
|-------|-----|--------|
| Turn running, empty prompt | `Ctrl+C` | Cancel the current turn |
| Turn running, non-empty draft | `Ctrl+C` | Clears the draft first; second `Ctrl+C` on empty prompt cancels |
| Turn running | `Esc` | **No-op** (does not cancel) |
| Turn cancelling | `Esc` | Re-sends cancel (retry if first ack was lost) |
| Idle, non-empty prompt | `Esc` twice within **800ms** | Clear prompt (text saved to prompt history) |
| Idle, empty prompt + messages | `Esc` twice within **800ms** | Open rewind picker (same as `/rewind`) |

Always-approve (skip per-tool prompts): `Ctrl+O`, `/always-approve`, or launch with `--always-approve` / `--yolo`.

Mid-turn follow-ups: plain `Enter` with text **queues** a message for after the current turn; platform-specific **send now** chords cancel-and-send (see [Keyboard shortcuts](/keyboard-shortcuts)).
</Step>

<Step title="Confirm tool output in scrollback">
Focus the scrollback (`Tab`) and inspect streamed tool blocks.

| Scrollback content | What to check |
|--------------------|---------------|
| Tool calls | Command, paths, status, streamed stdout/stderr |
| File edits | Inline diffs before/after apply |
| Agent messages | Markdown answers and follow-up questions |
| Thinking blocks | Collapsible reasoning (optional) |
| Task lists | `todo_write` progress |

| Action | Simple mode | Vim mode (`/vim-mode` or `[ui].vim_mode = true`) |
|--------|-------------|--------------------------------------------------|
| Select entry | `Up` / `Down` | `k` / `j` |
| Collapse / expand | `Left` / `Right` | `h` / `l` (or `e`) |
| Fullscreen viewer | `Enter` | `Enter` |
| Copy content / metadata | — | `y` / `Y` |
| Jump prev/next user turn | `Shift+Left` / `Shift+Right` | `H` / `L` |

By default Grok **asks before** shell commands and file edits. Approve when prompted, or use always-approve for trusted local work.

Common built-in tools you will see in scrollback:

| Tool | Role |
|------|------|
| `read_file` / `search_replace` | Read and line-precise edits |
| `grep` | Regex search (ripgrep) |
| `list_dir` | Directory listing |
| `run_terminal_command` | Shell execution |
| `web_search` / `web_fetch` | Web research (fetch may be gated by config/env) |
| `todo_write` | Task list |
| `spawn_subagent` | Parallel child agents |
| `memory_search` | Cross-session memory (when memory is enabled) |

Verification signals for a healthy first turn:

1. Your prompt appears as a sticky user header in scrollback.
2. Tool blocks stream while the agent works (not only after completion).
3. Shell/file tools either prompt for permission or run under always-approve.
4. Final agent text renders with markdown/syntax highlighting.
5. Session data appears under `~/.grok/sessions/` (grouped by encoded cwd).
</Step>
</Steps>

## Session basics after the first turn

Every conversation is a **session**, auto-saved under `~/.grok/sessions/` (override base with `GROK_HOME`).

| Goal | Interactive | CLI |
|------|-------------|-----|
| New session | `Ctrl+N` (double-press) or `/new` | Start `grok` again |
| Resume picker | `Ctrl+S` or `/resume` | `grok --resume [ID]` |
| Continue latest for cwd | — | `grok -c` |
| Compact history | `/compact` | — |

Project instructions: place `AGENTS.md` (or `CLAUDE.md` for compatibility) in the repo; deeper paths win. See [Project rules and memory](/project-rules-and-memory).

## Troubleshooting the first session

| Symptom | What to try |
|---------|-------------|
| Login loop / expired credentials | `grok login`, or `grok logout` then login; check `~/.grok/auth.json` |
| No browser on remote host | `grok login --device-auth` or `XAI_API_KEY` |
| API key ignored | Interactive session token wins — `grok logout` first |
| Tools hang waiting for approval | Approve prompts, or `Ctrl+O` / `--always-approve` |
| Cancel does nothing with Esc | Use `Ctrl+C`; Esc does not cancel a running turn |
| Bad terminal colors / keys | `/terminal-setup`; see [Troubleshooting](/troubleshooting) |
| Wrong project files | Relaunch with `--cwd` or start from the repo root |

## Related pages

<CardGroup>
<Card title="Installation" href="/installation">
Install and update the `grok` binary; `~/.grok/bin` PATH layout; `grok --version`.
</Card>
<Card title="Authentication" href="/authentication">
Browser OAuth, device-code, `XAI_API_KEY`, `grok login` / `logout`, `auth.json` refresh.
</Card>
<Card title="Keyboard shortcuts" href="/keyboard-shortcuts">
Focus, cancel vs Esc, vim mode, send-now chords, mouse.
</Card>
<Card title="Slash commands" href="/slash-commands">
`/model`, `/new`, `/resume`, `/compact`, `/always-approve`, and skill-provided commands.
</Card>
<Card title="Sessions" href="/sessions">
Resume, continue, fork, compact, export, and on-disk layout under `~/.grok/sessions/`.
</Card>
<Card title="Permissions and safety" href="/permissions-and-safety">
Allow/deny/ask rules, always-approve modes, hooks, and sandbox interaction.
</Card>
<Card title="Headless mode" href="/headless-mode">
Non-interactive `-p` / `--single` for scripts and CI after the interactive path works.
</Card>
<Card title="CLI reference" href="/cli-reference">
Full flag and subcommand surface for the `grok` binary.
</Card>
</CardGroup>

---

## 04. Authentication

> Browser OAuth (auth.x.ai), device-code flow, XAI_API_KEY for CI, grok login/logout, credential storage in ~/.grok/auth.json, and token refresh behavior.

- Page Markdown: https://grok-wiki.com/public/docs/xai-org-grok-build-90205de50458/pages/04-authentication.md
- Generated: 2026-07-16T01:44:10.541Z

### Source Files

- `crates/codegen/xai-grok-pager/docs/user-guide/02-authentication.md`
- `crates/codegen/xai-grok-shell/src/auth/manager.rs`
- `crates/codegen/xai-grok-shell/src/auth/storage.rs`
- `crates/codegen/xai-grok-shell/src/auth/device_code.rs`
- `crates/codegen/xai-grok-shell/src/auth/flow.rs`
- `crates/codegen/xai-grok-pager/src/app/cli.rs`
- `crates/codegen/xai-grok-auth/src/lib.rs`

---
title: "Authentication"
description: "Browser OAuth (auth.x.ai), device-code flow, XAI_API_KEY for CI, grok login/logout, credential storage in ~/.grok/auth.json, and token refresh behavior."
---

Grok authenticates through `AuthManager` and `~/.grok/auth.json`: interactive login (`grok login`, first-launch TUI), non-interactive `XAI_API_KEY` / per-model keys, and mid-session refresh on expiry or 401. Default interactive transport is loopback OAuth against `https://auth.x.ai`; device-code and external provider binaries are first-class alternatives for headless and enterprise setups.

## Auth surfaces

| Surface | When it applies | Entry points |
|---------|-----------------|--------------|
| **Browser OAuth (loopback)** | Default interactive login | First `grok` launch, `grok login`, `grok login --oauth` |
| **Device code (RFC 8628)** | Headless/SSH/remote when browser cannot open on the machine | `grok login --device-auth` (alias `--device-code`) |
| **API key** | CI, automation, BYOK, no browser | `XAI_API_KEY` (legacy: `GROK_CODE_XAI_API_KEY`), per-model `api_key` / `env_key` |
| **Enterprise OIDC** | Customer IdP (Okta, Azure AD, Auth0, …) | `[grok_com_config.oidc]` or `GROK_OIDC_*` |
| **External auth provider** | Sandboxed/CI/air-gapped custom mint | `[auth] auth_provider_command` / `GROK_AUTH_PROVIDER_COMMAND` |

```text
  CLI / TUI / ACP                 AuthManager                 Disk / IdP
  ───────────────                 ───────────                 ──────────
  grok login  ──────────────────► run_auth_flow ────────────► ~/.grok/auth.json
  first launch / reauth           refresh_chain                auth.json.lock
  HTTP 401 / near-expiry  ──────► OIDC / external refresh ──► token endpoint
  resolve_credentials  ─────────► session | XAI_API_KEY       (scope map)
```

## Browser OAuth (default)

Default interactive path uses OAuth 2.1 authorization code + PKCE against the xAI OAuth2 issuer `https://auth.x.ai` (local-dev override: `GROK_LOCAL_AUTH=1` → `http://localhost:22255`).

```bash
grok              # first launch prompts sign-in when no usable session exists
grok login        # re-run sign-in; replaces cached session
grok login --oauth
```

<Steps>
  <Step title="Start login">
    Run `grok` or `grok login`. The CLI opens the authorization URL in the default browser (loopback callback on `http://127.0.0.1/callback` with a random port).
  </Step>
  <Step title="Complete consent">
    Sign in at the IdP / accounts UI. Grok exchanges the code for access and refresh tokens.
  </Step>
  <Step title="Persist and verify">
    Credentials land in `~/.grok/auth.json` under an issuer+client scope key. Successful CLI login prints a signed-in confirmation and may apply managed/team config immediately.
  </Step>
</Steps>

Default OAuth2 scopes include `openid`, `profile`, `email`, `offline_access`, `grok-cli:access`, `api:access`, and conversation read/write scopes. Enterprise OIDC uses a slightly different default set (`offline_access` + `api:access` among others) configured under `[grok_com_config.oidc]`.

## Device-code flow

For environments without a local browser (SSH, containers, remote VMs):

```bash
grok login --device-auth
# alias:
grok login --device-code
```

Grok implements RFC 8628:

1. `POST {issuer}/oauth2/device/code` with `client_id`, scopes, and client surface headers
2. Prints verification URL + user code to stderr (CLI) or the TUI
3. Polls `POST {issuer}/oauth2/token` with grant type `urn:ietf:params:oauth:grant-type:device_code`
4. On success, persists credentials and continues

| Poll error | Behavior |
|------------|----------|
| `authorization_pending` | Keep polling |
| `slow_down` | Increase interval by 5s |
| `access_denied` | Fail: user rejected |
| `expired_token` | Fail: re-run `grok login --device-auth` |
| HTTP 404 on device code | Device flow not enabled for this deployment |

### Device vs loopback selection

Interactive transport precedence (xAI OAuth2 only; enterprise OIDC always uses loopback):

1. CLI: `--oauth` (loopback) wins over `--device-auth`
2. Env: `GROK_LOGIN_DEVICE_FLOW`
3. Config: `[auth] login_device_flow`
4. Remote feature flag `grok_build_login_device_flow` (2s fetch timeout → loopback)
5. Default: **loopback**

```toml
# ~/.grok/config.toml
[auth]
login_device_flow = true
```

```bash
export GROK_LOGIN_DEVICE_FLOW=true
```

<Note>
Enterprise OIDC (`[grok_com_config.oidc]`) has no device endpoint. `grok login --device-auth` falls back to browser sign-in with a short stderr notice.
</Note>

## API key (`XAI_API_KEY`)

For CI/CD and non-interactive runs:

```bash
export XAI_API_KEY="xai-..."
grok -p "summarize this repo"
```

| Variable | Role |
|----------|------|
| `XAI_API_KEY` | Primary API key env |
| `GROK_CODE_XAI_API_KEY` | Legacy fallback if `XAI_API_KEY` is unset |
| Per-model `api_key` / `env_key` in `config.toml` | Highest priority for that model (BYOK / OpenAI-compatible) |

`XAI_API_KEY` is used when no active session token applies. An interactive session stored in `auth.json` is preferred as the default auth method when both exist, so OIDC refresh stays alive. To force the env key only, run `grok logout` or delete the session entry.

Admin kill switch: `GROK_DISABLE_API_KEY_AUTH` / `[grok_com_config] disable_api_key_auth` (or pinning `force_login_team_uuid`) stops first-party API-key auth so deployments can force IdP login. BYOK to non–first-party base URLs is not swapped by that kill switch.

## `grok login` / `grok logout`

### Login

```bash
grok login                    # reauth; default loopback (or resolved device transport)
grok login --oauth            # force loopback
grok login --device-auth      # force device code
```

| Flag | Effect |
|------|--------|
| `--oauth` (alias `--oidc`) | Force loopback-callback OAuth |
| `--device-auth` (`--device-code`) | Force RFC 8628 device flow |
| `--legacy` | Hidden no-op (OAuth-only era) |

Loopback reauth clears the current scope (and legacy `accounts.x.ai` scope) up front so abandoned logins do not leave mixed credentials. Device-branch login uses force-interactive mode so abandoning the prompt does not wipe an existing session.

After successful login, Grok may sync managed/team configuration and print whether team or deployment config was applied.

### Logout

```bash
grok logout
```

Takes no flags. Clears the active session from memory and `auth.json`, resets external telemetry identity, and clears orphan managed-config files when no principal remains.

| Outcome | stderr |
|---------|--------|
| Was signed in | `Logged out` or `Logged out (was signed in as {email})` |
| No session | `No cached session to log out of.` |
| Env key still set | Notes that `XAI_API_KEY` remains and will still authenticate |

TUI `/logout` and ACP logout use the same `perform_logout` core as the CLI.

## Credential storage (`~/.grok/auth.json`)

Path: `{GROK_HOME}/auth.json` (default home is `~/.grok`).

The file is a JSON object (scope key → credential). Scope keys include:

| Scope pattern | Meaning |
|---------------|---------|
| `{issuer}::{client_id}` | Active OIDC / OAuth2 session |
| `xai::api_key` | Stored plain API key scope |
| `https://accounts.x.ai/sign-in` | Legacy WebLogin (ignored for use; migration requires re-login) |

### Credential fields (`GrokAuth`)

| Field | Purpose |
|-------|---------|
| `key` | Access token / API key bearer |
| `auth_mode` | `oidc`, `external`, `api_key` (`web_login` deserialize-only, skipped) |
| `create_time` | Local mint time |
| `expires_at` | Server `expires_in` when present |
| `refresh_token` | Silent OIDC / reference for external providers |
| `oidc_issuer` / `oidc_client_id` | Refresh discovery and first-party classification |
| User / team / org profile fields | Enriched from `/user` after login |

Writes are owner-only (`0o600`), prefer atomic temp+rename, fall back to in-place rewrite on disk full, and use advisory locking via sibling `auth.json.lock` so concurrent processes do not double-spend refresh tokens. Corrupt JSON is renamed to `auth.json.corrupt.<millis>` before recovery writes.

`AuthManager` re-reads disk on refresh/recovery paths, so external updates to `auth.json` are picked up without restarting when a request path reloads from disk.

## Token refresh behavior

```mermaid
stateDiagram-v2
  [*] --> Valid: login / load auth.json
  Valid --> NearExpiry: within early-invalidation buffer
  NearExpiry --> Refreshing: proactive refresh task
  Valid --> Refreshing: HTTP 401/403 (ServerRejected)
  Refreshing --> Valid: Success (persist + hot_swap)
  Refreshing --> StickyFail: permanent IdP rejection
  Refreshing --> Valid: transient failure (retry later)
  StickyFail --> [*]: re-login required (or credential change)
  Valid --> [*]: logout / clear scope
```

### When refresh runs

| Trigger | Mechanism |
|---------|-----------|
| **Pre-request / proactive** | Background task wakes before `expires_at` (or `create_time + 30d` when no server expiry) minus early-invalidation buffer |
| **Server rejected** | 401/403 → `AuthManager` recovery with `RefreshReason::ServerRejected` |
| **External provider** | Re-runs the configured binary with `GROK_AUTH_EXPIRED=1` (not OAuth refresh grant) |
| **OIDC / OAuth2** | Uses stored `refresh_token` against the issuer token endpoint |

### Timing knobs

| Knob | Default | Meaning |
|------|---------|---------|
| Server `expires_in` → `expires_at` | From IdP | Hard expiry when present |
| Fallback lifetime | **30 days** from `create_time` | When `expires_at` is absent |
| Early invalidation | **300s** | Treat token expired this many seconds early |
| `GROK_AUTH_EARLY_INVALIDATION_SECS` | overrides buffer | Set `0` to disable proactive early buffer |
| `GROK_AUTH_TOKEN_TTL` / `auth_token_ttl` | optional | Synthesize expiry for bare-string external tokens |

Refresh holds a cross-process file lock (refresh path timeout ~45s) so sibling processes adopt a peer-refreshed token instead of reusing the same refresh token. Permanent refresh failures are sticky per credential for ~5 minutes of real time (wall clock + monotonic).

## Auth precedence

### Per-request credentials (`resolve_credentials`)

Highest to lowest:

1. **Per-model `api_key` or `env_key`** under `[model.<name>]`
2. **Active session token** from `AuthManager` / `auth.json`
3. **`XAI_API_KEY`** (then legacy `GROK_CODE_XAI_API_KEY`)

### Interactive login chain (`run_auth_flow`)

When obtaining or refreshing a session:

1. Compatible cached credential (unless reauth / force interactive)
2. Disk re-read + silent refresh if expired
3. **External auth provider** (`auth_provider_command`)
4. Devbox auto-mint when applicable (skipped if `preferred_method = api_key`)
5. **Enterprise OIDC** loopback when configured
6. **xAI OAuth2** loopback or device flow

### Preferred method pin

```toml
[auth]
preferred_method = "api_key"   # or "oidc"
```

When set, automatic selection is fail-closed: only that method family is used; no silent fallthrough. Config-toml only (not env).

## Enterprise OIDC

```toml
# ~/.grok/config.toml
[grok_com_config.oidc]
issuer = "https://acme.okta.com"
client_id = "0oa1b2c3d4e5f6g7h8i9"
# scopes default: openid, profile, email, offline_access, api:access
# audience = "..."   # if required by the IdP
```

```bash
export GROK_OIDC_ISSUER="https://acme.okta.com"
export GROK_OIDC_CLIENT_ID="0oa1b2c3d4e5f6g7h8i9"
# optional:
export GROK_OIDC_SCOPES="openid,profile,email,offline_access,api:access"
export GROK_OIDC_AUDIENCE="..."
export GROK_CLI_CHAT_PROXY_BASE_URL="https://grok-proxy.acme.com/v1"
```

Register a public client with Authorization Code + PKCE and loopback redirect `http://127.0.0.1/callback` (port-agnostic per RFC 8252). Discovery uses `{issuer}/.well-known/openid-configuration`.

## External auth provider

When browser login is not possible, point Grok at a command that mints tokens:

```toml
[auth]
auth_provider_command = "/usr/local/bin/my-auth-provider"
auth_provider_label = "Acme Corp"   # optional TUI label
auth_token_ttl = 3600               # optional for bare-string tokens
```

| Stream / exit | Contract |
|---------------|----------|
| **stdout** | Token only: bare string **or** JSON |
| **stderr** | Human UX (URLs, progress); TUI surfaces first `https://` link |
| Exit `0` | Success |
| Non-zero / empty stdout / timeout | Fall through to interactive login (interactive path); refresh path fails |

JSON stdout shape:

```json
{
  "access_token": "eyJ...",
  "refresh_token": "optional",
  "expires_in": 3600,
  "issuer": "https://idp.example.com"
}
```

On refresh, Grok re-runs the binary with `GROK_AUTH_EXPIRED=1`. Interactive external runs time out at 300s async; sync refresh uses a shorter timeout (~5s).

| Variable | Description |
|----------|-------------|
| `GROK_AUTH_PROVIDER_COMMAND` | Path/command (`sh -c`) |
| `GROK_AUTH_PROVIDER_LABEL` | Login button label |
| `GROK_AUTH_TOKEN_TTL` | Bare-token lifetime seconds |
| `GROK_AUTH_EXPIRED` | Set to `1` by Grok on refresh invocations |
| `GROK_AUTH_EARLY_INVALIDATION_SECS` | Proactive refresh buffer |

## Environment and config reference

| Key / env | Role |
|-----------|------|
| `XAI_API_KEY` / `GROK_CODE_XAI_API_KEY` | Global API key |
| `GROK_LOGIN_DEVICE_FLOW` | Prefer device login |
| `GROK_OIDC_ISSUER` / `GROK_OIDC_CLIENT_ID` | Enterprise OIDC |
| `GROK_OAUTH2_ISSUER` / `GROK_OAUTH2_CLIENT_ID` | Override OAuth2 provider |
| `GROK_AUTH_PROVIDER_*` | External binary auth |
| `GROK_DISABLE_API_KEY_AUTH` | Force IdP (sticky over user config) |
| `GROK_LOCAL_AUTH` | Local accounts-app issuer |
| `[auth] preferred_method` | Fail-closed method pin |
| `[auth] login_device_flow` | Device transport preference |

## Failure modes and recovery

| Symptom | Likely cause | Fix |
|---------|--------------|-----|
| Auth fails after long idle | Refresh rejected or sticky permanent failure | `grok logout` then `grok login` |
| Device code unavailable | Deployment without device endpoint / enterprise OIDC | Use loopback or `XAI_API_KEY` |
| API key ignored | Active session preferred, or kill switch | `grok logout` or disable kill switch if intentional |
| Corrupt auth file | Partial write / crash | Look for `auth.json.corrupt.*` backup; re-login |
| External provider silent | Wrote progress to stdout | Token-only stdout; URLs on stderr |
| OIDC redirect fails | IdP disallows loopback | Allow `http://127.0.0.1/callback` |
| CI needs auth without browser | No TTY / no session | Export `XAI_API_KEY` or external provider + non-interactive mode |

Debug logging:

```bash
# TUI file log
GROK_LOG_FILE=/tmp/grok.log RUST_LOG=debug grok

# Headless stderr
RUST_LOG=debug grok -p "hello" 2> /tmp/grok.log
```

Useful log lines: `auth: running external auth provider`, `auth: external auth provider returned fresh token`, `auth: using cached credentials`, `login: resolved interactive transport`.

## Next

<CardGroup>
  <Card title="Quickstart" href="/quickstart">
    First interactive session after install and sign-in.
  </Card>
  <Card title="Headless mode" href="/headless-mode">
    Non-interactive `-p` runs, CI patterns, and credential expectations.
  </Card>
  <Card title="Custom models" href="/custom-models">
    Per-model `api_key` / `env_key` and provider-neutral endpoints (BYOK).
  </Card>
  <Card title="Configure Grok" href="/configure-grok">
    `config.toml` precedence, feature flags, and `grok inspect`.
  </Card>
  <Card title="CLI reference" href="/cli-reference">
    Top-level `login` / `logout` and shared runtime flags.
  </Card>
  <Card title="Troubleshooting" href="/troubleshooting">
    Auth recovery, terminal issues, and diagnostics.
  </Card>
</CardGroup>

---

## 05. Sessions

> Session lifecycle: create, resume, continue, fork, compact, export; persistence layout; /new /resume /compact; grok sessions subcommands and ID constraints.

- Page Markdown: https://grok-wiki.com/public/docs/xai-org-grok-build-90205de50458/pages/05-sessions.md
- Generated: 2026-07-16T01:21:10.806Z

### Source Files

- `crates/codegen/xai-grok-pager/docs/user-guide/17-sessions.md`
- `crates/codegen/xai-grok-pager/src/sessions_cmd.rs`
- `crates/codegen/xai-grok-shell/src/session/mod.rs`
- `crates/codegen/xai-grok-shell/src/session/chat_persistence.rs`
- `crates/codegen/xai-grok-shell/src/session/compaction.rs`
- `crates/codegen/xai-chat-state/src/lib.rs`
- `crates/codegen/xai-grok-paths/src/lib.rs`

---
title: "Sessions"
description: "Session lifecycle: create, resume, continue, fork, compact, export; persistence layout; /new /resume /compact; grok sessions subcommands and ID constraints."
---

Grok Build persists every conversation as a **session** under `$GROK_HOME/sessions` (default `~/.grok/sessions`). The same on-disk layout backs the interactive TUI, headless `-p` runs, and ACP agent stdio. Session selection is resolved once from CLI flags (`--resume` / `-r`, `--continue` / `-c`, `--session-id` / `-s`, `--fork-session`) into a single startup intent, then materialized against the local JSONL store before the first turn. In-process chat history is owned by `ChatStateActor`; disk writes go through JSONL adapters (`updates.jsonl`, `chat_history.jsonl`, `summary.json`).

## What a session contains

A session is a durable conversation record, not just scrollback. It includes:

- User prompts and model responses
- Tool calls and tool results
- Plan / TODO state
- File snapshots for rewind
- Token and turn signals
- Optional subagent metadata (child sessions live in the normal sessions tree)

Grok assigns a **UUIDv7** when it creates the ID. Clients may supply their own ID with `-s` / `--session-id` only when creating or naming a fork (see [Session ID constraints](#session-id-constraints)).

## Persistence layout

Base directory: `grok_home()/sessions/…` where `grok_home()` is `$GROK_HOME` or `~/.grok`.

Sessions are **grouped by working directory**. The cwd is encoded into a single path component:

| Encoding | When | Form |
| --- | --- | --- |
| URL-encode | Encoded name ≤ 255 bytes | Reversible via `urlencoding::decode` |
| Slug + BLAKE3 | Encoded name > 255 bytes | `{slug}-{16 hex}` (≤ 57 bytes); original path in `.cwd` inside the group dir |

```text
~/.grok/sessions/<encoded-cwd>/<session-id>/
  summary.json              # index metadata (title, model, parent, counts)
  updates.jsonl             # ACP session/update stream (authoritative for resume/export)
  chat_history.jsonl        # ConversationItems sent to the model API
  plan.json                 # TODO / task list
  plan_mode.json            # plan-mode UI state (when used)
  rewind_points.jsonl       # file snapshots for /rewind
  signals.json              # usage / turn counters
  feedback.jsonl            # user ratings and feedback
  btw_history.jsonl         # /btw side-question log (when used)
  compaction_checkpoints/   # post-compact restore points
  subagents/                # per-child meta; child sessions use the normal tree
```

<Info>
`updates.jsonl` is the source of truth for restore, replay, and `grok export`. `chat_history.jsonl` is the model-facing history (format version 1 = `ConversationItem`). `summary.json` is the index row used by pickers and `grok sessions list`.
</Info>

### `summary.json` fields (selected)

| Field | Role |
| --- | --- |
| `info` | Session id + cwd |
| `session_summary` / `generated_title` | Display title (manual rename sets `title_is_manual`) |
| `created_at` / `updated_at` / `last_active_at` | Timestamps |
| `num_messages` / `num_chat_messages` | Update vs chat counts |
| `current_model_id` | Model in use |
| `parent_session_id` | Fork / restore parent |
| `session_kind` | e.g. `fork`, `worktree`, `subagent` |
| `source_workspace_dir` | Worktree sessions: original workspace for grouping |
| `agent_name` | Agent definition to restore on resume |
| `worktree_label` | Human label for worktree groups in `grok sessions list` |

Chat messages append through a persistence channel (`ChannelChatPersistence` → `PersistenceMsg::Chat` / `ReplaceChatHistory` / `Flush`). Compaction replaces history and may write checkpoints under `compaction_checkpoints/`.

## Session ID constraints

| Rule | Behavior |
| --- | --- |
| Generated IDs | UUIDv7 (36-char string) |
| `-s` / `--session-id` | Must parse as a UUID; must **not** already exist under the target session directory |
| Resume | Use `-r` / `--resume` or `-c` / `--continue` — **not** `-s` |
| `-s` + resume/continue | Only valid with `--fork-session` (names the **child** UUID) |
| Overwrite model | Create-only for client IDs; resume never upserts over an existing id |

Preflight error strings include:

- `Error: --session-id must be a valid UUID (got '…').`
- `Error: Session ID … is already in use.`
- `Error: --session-id can only be used with --continue or --resume if --fork-session is also specified.`
- `Error: --fork-session requires --resume or --continue.`
- `Error: --fork-session cannot be combined with --worktree.`

## Create and leave sessions

### Interactive

| Action | Command / UI |
| --- | --- |
| Launch | `grok` creates a new session (or welcome → pick recent) |
| Fresh mid-session | `/new` (alias `/clear`) |
| Leave Grok | `/quit` (alias `/exit`) |
| Leave session, stay in app | `/home` (welcome screen) |
| Inspect current | `/session-info` (id, cwd, model, context usage) |
| Rename | `/rename <title>` (alias `/title`) |

### CLI create with fixed UUID

```bash
grok -s 019f6881-e1d4-7362-bdf8-73b3ab84a480 -p "Start automation run"
```

Fails if the value is not a UUID or if that id already has a session under the write cwd.

## Resume and continue

Startup flags classify into one intent: **NewAuto**, **NewWithId**, **Resume**, or **ForkFrom**.

| Flag | Meaning |
| --- | --- |
| `-r` / `--resume <id>` | Strict load of that session (error if missing) |
| `-r` / `--resume` (no value) | Most recent session for the **current cwd** |
| `-c` / `--continue` | Same as resume-most-recent for cwd |
| `--load` | Hidden alias of `--resume` |

`-r` and `-c` conflict with each other.

### Interactive resume

```text
/resume
```

Opens a session picker for the current workspace (no arguments). Type to filter by title; extended search also matches conversation content. `Ctrl+/` runs content search immediately. Active parent/fork tabs are managed with `/dashboard` (alias `/sessions` in the TUI — distinct from the `grok sessions` CLI).

Welcome-screen recent list also resumes by selection.

### Headless resume patterns

```bash
# New session (default)
grok -p "Hello"

# Resume by id (must exist)
grok -p "Continue where we left off" -r <session-id>

# Continue most recent in this directory
grok -p "What were we doing?" -c
```

Capture the id from JSON output:

```bash
grok -p "Hello" --output-format json | jq -r '.sessionId'
```

<Tip>
For multi-step CI, always pass the previous `sessionId` to `-r`. Do not reuse `-s` to open an existing session.
</Tip>

Sandbox profile on resume prefers the profile stored with the session; a mismatched `--sandbox` is refused rather than silently changing isolation mid-history.

## Fork

Fork copies conversation (and related state) into a **new** session id with `parent_session_id` set. Fork ids are also plain UUIDv7 (no source prefix), so length stays fixed across fork depth.

### Interactive

```text
/fork [--worktree|--no-worktree] [directive]
```

Optional `directive` becomes the fork’s first prompt. Worktree mode can isolate the fork in a new git worktree; omit both flags to be prompted. `--at <turn>` is not supported in current builds.

### CLI

```bash
# Fork most recent for cwd into a new id
grok -c --fork-session -p "Try an alternate approach"

# Fork a specific parent; name the child UUID
grok -r <parent-id> --fork-session -s <new-uuid> -p "Branching work"
```

`--fork-session` requires `-r` or `-c`. It cannot combine with `--worktree` at startup.

### Programmatic / ACP

Extension method `x.ai/session/fork` with payload fields such as `sourceSessionId`, `sourceCwd`, `newCwd`, optional `newSessionId`, and `sessionKind` (`fork` or `worktree`). Copy includes chat history, updates, plan state, and compaction segment archives when present.

Resume a session in a fresh worktree:

```bash
grok -w -r <session-id>
```

## Compact

Long histories are compressed so later turns fit the model context window.

### Manual

```text
/compact
/compact focus on the auth migration decisions
```

Optional text steers what the summarizer should preserve. Manual compact does not arm auto-compact suppression the same way failed auto-compact paths do.

### Auto-compact

When estimated tokens approach the model’s `context_window`, Grok auto-compacts (with UI notification). Related knobs:

- Model `context_window`
- Prefire lead: background first-pass of two-pass compact, default 10 percentage points early (`GROK_PREFIRE_LEAD_PERCENT`)
- Hidden CLI: `--compaction-mode` (`summary` \| `transcript` \| `segments`), `--compaction-detail`

Compaction rewrites `chat_history.jsonl`, records markers in `updates.jsonl`, and can write under `compaction_checkpoints/`. Forked sessions may keep an **inherited prefix** (`inherited_prefix_len`) that is preserved through later compacts.

## Export and delete

### Export transcript (Markdown)

```bash
grok export <session-id>                 # stdout
grok export <session-id> ./out.md        # file
grok export <session-id> --clipboard     # clipboard
```

Replays `updates.jsonl` through the ACP tracker and renders scrollback blocks to Markdown. Empty conversation content errors out.

### Trace / diagnostics bundle

```bash
grok trace <session-id>
```

Packages the session directory (and related artifacts) under `$GROK_HOME/trace-exports/` by default.

### Delete

```bash
grok sessions delete <session-id>
```

Removes local history (found by id across workspaces). When authenticated and not on a ZDR team, also attempts remote delete (idempotent; 404 is success).

## `grok sessions` CLI

Requires a subcommand:

| Subcommand | Flags | Behavior |
| --- | --- | --- |
| `list` | `-n` / `--limit` (default 20) | Recent sessions for **current cwd**, grouped by `worktree_label` |
| `search` | query, `-n` / `--limit` | Local SQLite FTS5 over titles/prompts + remote results (timeout falls back to local) |
| `delete` | `id` | Permanent delete (see above) |

```bash
grok sessions list
grok sessions list --limit 50
grok sessions search "rate limit"
grok sessions delete 019f6881-e1d4-7362-bdf8-73b3ab84a480
```

List columns: session id, created, updated, source status, summary (or first prompt line). Search prints score/snippet; remote-only hits are marked `(remote)`.

Auth for remote merge is best-effort: cached credentials or deployment key; interactive login is not forced for enterprise proxy setups.

## ACP agent sessions

Over agent stdio, clients own create/load:

```typescript
// Create
const { sessionId } = await connection.request("session/new", {
  cwd: "/path/to/project",
  mcpServers: [],
});

// Load
await connection.request("session/load", {
  sessionId: "existing-session-id",
  cwd: "/path/to/project",
  mcpServers: [],
});
```

The agent appends session updates automatically. Clients reconnect with `session/load` using the same id. Optional client `_meta.sessionId` on `session/new` must be a UUID and unused under the target cwd (same preflight as CLI `-s`).

## Interactive command map

| Command | Role |
| --- | --- |
| `/new` / `/clear` | New session |
| `/resume` | Picker → load prior session |
| `/dashboard` / TUI `/sessions` | Switch/rename/close active tabs (parent + forks) |
| `/fork` | Branch conversation (+ optional worktree) |
| `/compact` | Summarize history |
| `/rewind` | Restore files + truncate history to a prior prompt snapshot |
| `/rename` / `/title` | Set display title |
| `/session-info` | Current id, model, context fill |
| `/quit` / `/exit` | Exit Grok |
| `/home` | Welcome without quitting process |

<Warning>
`/rewind` restores **on-disk** file snapshots and truncates conversation. Changes after the chosen point are lost unless still recoverable from git. Esc Esc (within ~800ms, idle, empty prompt) can open the same rewind flow.
</Warning>

## Operational notes

- **Disk size**: Rewind snapshots dominate usage when many files change. Compact reduces model history size but does not always delete rewind artifacts.
- **Search index**: Keyword search uses a local FTS5 index over titles and prompts; remote search is additive and may time out.
- **ZDR teams**: Remote upload/delete is skipped; sessions stay local-only.
- **Cwd binding**: Resume-most-recent and `sessions list` are scoped to the process cwd’s encoded group. The same UUID under another encoded cwd is a different directory.
- **Subagents**: Child metadata under `subagents/`; full child histories use normal session paths (or explicit dirs under the parent when configured).

## Related pages

<CardGroup>
  <Card title="Headless mode" href="/headless-mode">
    `-p`, output formats, max-turns, and CI resume/continue patterns that depend on session ids.
  </Card>
  <Card title="Agent mode (ACP)" href="/agent-mode">
    JSON-RPC `session/new` and `session/load`, streaming updates, and shared runtime flags.
  </Card>
  <Card title="Slash commands" href="/slash-commands">
    Full interactive command surface including session, model, and memory builtins.
  </Card>
  <Card title="CLI reference" href="/cli-reference">
    Top-level `grok` commands and shared flags (`sessions`, `export`, `trace`, resume, worktree).
  </Card>
  <Card title="Subagents and personas" href="/subagents">
    Child agents, isolation, and how forked/worktree sessions relate to parent context.
  </Card>
  <Card title="Project rules and memory" href="/project-rules-and-memory">
    Cross-session memory vs per-session history; when AGENTS.md injects into the system prompt.
  </Card>
</CardGroup>

---

## 06. Permissions and safety

> Tool authorization pipeline: PreToolUse hooks, allow/deny/ask rules, remembered grants, permission modes (including bypassPermissions/dontAsk), safe-bash defaults, and interaction with sandbox.

- Page Markdown: https://grok-wiki.com/public/docs/xai-org-grok-build-90205de50458/pages/06-permissions-and-safety.md
- Generated: 2026-07-16T01:20:48.724Z

### Source Files

- `crates/codegen/xai-grok-pager/docs/user-guide/22-permissions-and-safety.md`
- `crates/codegen/xai-grok-shell/src/util/config/permissions.rs`
- `crates/codegen/xai-grok-shell/src/util/config/resolve/tool_approvals.rs`
- `crates/codegen/xai-grok-config-types/src/permission.rs`
- `crates/codegen/xai-grok-hooks/src/dispatcher.rs`
- `crates/codegen/xai-grok-tools/src/lib.rs`

---
title: "Permissions and safety"
description: "Tool authorization pipeline: PreToolUse hooks, allow/deny/ask rules, remembered grants, permission modes (including bypassPermissions/dontAsk), safe-bash defaults, and interaction with sandbox."
---

Grok authorizes every model-requested tool call through a fixed pipeline in the workspace permission manager: **`PreToolUse` hooks** run first (explicit deny only blocks; failures are fail-open), then merged **allow / deny / ask** rules from CLI flags, native `[permission]` config, managed/enterprise layers, and Claude-compatible settings, then **remembered grants** (when enabled), then built-in **safe-bash / read-only auto-approvals**, and finally the **prompt policy** from the active permission mode (`default`, `dontAsk`, `bypassPermissions`, `acceptEdits`, `plan`, `auto`). OS-level sandbox profiles are a separate enforcement layer after approval.

## Authorization pipeline

When the model requests a tool, checks run in this order:

1. **`PreToolUse` hooks** — Hooks matching the tool name run sequentially. An explicit `deny` stops the call. An allow (or no matching hooks) does **not** skip later checks. Hook crashes, timeouts, missing commands, and malformed output are **fail-open**: the failure is logged and shown in the UI, and the call continues.
2. **Permission rules** (CLI `--allow` / `--deny`, `~/.grok/config.toml`, project `.grok/config.toml`, managed config, `.claude/settings.json` / `settings.local.json`)
   - Matching **`deny`** rejects immediately. Deny wins over every other rule action, regardless of source or file order.
   - Matching **`ask`** forces a user prompt (including for tools that would otherwise auto-approve).
   - Matching **`allow`** approves.
3. **Remembered grants** — Per-project “Always allow” / “never allow” entries saved from earlier prompts can satisfy an `ask` floor without re-prompting. Dangerous commands (below) still prompt. Grants are not consulted under always-approve mode.
4. **Built-in auto-approvals** — Read-only tools and a fixed set of read-only shell command prefixes run without prompting unless a deny/hook/ask rule blocks them.
5. **Prompt policy** — Driven by permission mode: prompt (`default`), auto-deny without a prior allow (`dontAsk`), auto-approve (`bypassPermissions`), auto-allow edits (`acceptEdits`), or classifier-based auto (`auto`).

**Always-approve (`bypassPermissions`)** short-circuits after step 2: deny rules, hooks, and shell-segment `ask` rules still apply; remembered grants (including remembered denials) are skipped; non-shell `ask` rules do not re-prompt.

```mermaid
flowchart TD
  T[Tool request] --> H[PreToolUse hooks]
  H -->|explicit deny| X[Reject]
  H -->|allow / fail-open| R[Merged allow/deny/ask rules]
  R -->|deny| X
  R -->|ask| G{Remembered grant?}
  R -->|allow| OK[Execute]
  R -->|no match| S[Safe tools / safe-bash]
  G -->|yes, not dangerous| OK
  G -->|no| S
  S -->|auto-approve| OK
  S -->|needs decision| P[Prompt policy]
  P -->|default: prompt| UI[User / headless cancel]
  P -->|dontAsk: deny| X
  P -->|bypassPermissions: allow*| OK
  UI -->|allow| OK
  UI -->|reject| X
```

\*Subject to deny rules, hooks, and shell `ask` segment checks.

## Built-in auto-approvals

These run without prompting in every mode (including `dontAsk`) unless a matching **deny** rule or hook blocks them. An **`ask`** rule still forces a prompt for file reads, searches, and shell commands.

### Read-only tools

Mapped as read-class access (no prompt by default):

| Surface | Notes |
|---------|--------|
| `read_file`, `list_dir` | Path-scoped reads |
| `grep` | Content search |
| `web_search` | Search queries |
| `todo_write` | Session task list |
| Subagent control | `get_command_or_subagent_output` / wait / kill task variants |
| Skills | Skill invocation |

### Safe-bash defaults

After shell parsing splits on `&&`, `||`, `;`, `|`, and newlines, **each non-setup segment** is checked with word-boundary prefix matching (so `ls` does not match `lsof`; `tr` does not match `truncate`).

**Filesystem (viewing):** `ls`, `cat`, `pwd`, `date`, `whoami`, `hostname`, `uptime`, `ps`, `head`, `tail`, `wc`, `sort`, `uniq`, `tr`, `cut`

**Git (read-only):** `git status`, `git branch`, `git log`, `git diff`, `git ls-files`, `git show`, `git rev-parse`

**Search:** `grep`, `rg` — except `rg --pre` / `rg --pre=…` (spawns a per-file preprocessor)

**Build / cluster read-only:** `cargo check`; `kubectl get`, `kubectl logs`, `kubectl describe`

**Not safe:** `tee` is intentionally excluded (can write stdin to arbitrary paths).

Chained example: `ls && rm -rf /` — `ls` is safe; `rm` is not. In `default` mode the chain prompts; under `dontAsk` it is denied unless an allow rule covers the dangerous segment.

Segment evaluation peels environment assignments and wrappers (`timeout`, `nice`, `ionice`, `chrt`, `stdbuf`, `env`) and recurses into `bash -c` scripts for **deny/ask** and safe-list checks. Wrappers such as `sudo`, `xargs`, and `nohup` are **not** peeled; write rules that name them explicitly. Unparseable constructs (subshells, `$(…)`, backticks, background `&`, complex control flow) fail closed to a single conservative prompt when Bash restrictions apply.

### Dangerous commands

These always need a prompt when covered only by a remembered grant or the safe-command list:

`rm`, `chmod`, `chown`, `chgrp`, `chattr`, `pkill`, `kill`, `killall`, `git push`

An explicit config **allow** rule still approves them; always-approve mode auto-approves them. Use **deny** rules for unconditional blocks. Treat `Bash(rm *)` allow rules with care.

## Permission modes

| Mode | Behavior | Typical use |
|------|----------|-------------|
| `default` | Prompt for anything not pre-approved or built-in-safe | Daily interactive work |
| `dontAsk` | Deny anything without an explicit allow rule or built-in auto-approval | Headless, CI, high security |
| `bypassPermissions` | Auto-approve after deny/hook/shell-ask checks | Trusted local machines |
| `acceptEdits` | Synthetic allow for file edits (`search_replace`, `write`, …) | Fast edit workflows |
| `plan` | Accepted for compatibility; plan sessions are a separate feature | Structured planning |
| `auto` | Classifier-based decisions for non-fast-path tools | Assisted auto-approval |

### Setting the mode

- **Claude-compatible:** `permissions.defaultMode` in `.claude/settings.json` (or nested discovery up to the repo root). Supported values: `default`, `acceptEdits`, `bypassPermissions`, `dontAsk`, `plan`, `auto`. A top-level `defaultMode` is accepted when the nested key is absent.
- **CLI:** `--permission-mode` applies `bypassPermissions` (always-approve) and `default`; an explicit flag wins over config. Passing `dontAsk`, `acceptEdits`, or `plan` to the flag is accepted but does **not** enable those policies — set them via `defaultMode`.
- **Native UI keys:** `[ui] permission_mode` with values `always-approve`, `auto`, `ask`, `default` (maps to ask). Legacy `approval_mode` and `yolo = true` still map to always-approve.
- **Headless (`-p`):** a tool call that would prompt is cancelled and reported to the model. Prefer `defaultMode: "dontAsk"` plus narrow `--allow` rules for automation.

### Locking off always-approve

Administrators can prevent always-approve (`bypassPermissions` / `--always-approve` / `/always-approve`) from being enabled:

```toml
# requirements.toml (prefer root-owned /etc/grok/requirements.toml for non-overridable lock)
[ui]
disable_bypass_permissions_mode = true
```

Do not use user-switchable `permission_mode` as a lock. Legacy `[ui] yolo = false` in **requirements** also disables the mode; in user `config.toml`, `yolo` remains a preference. Grok honors Claude managed **permission rules** but **not** Claude’s `disableBypassPermissionsMode` lock — use Grok `requirements.toml` instead.

`~/.grok/requirements.toml` is user-editable. For org enforcement users cannot remove, deploy `/etc/grok/requirements.toml`.

## Configuring permission rules

Rules from all sources merge into one set. Evaluation is by **severity**, not order: **deny > ask > allow**. A global deny cannot be overridden by a project allow.

### Scopes

| Scope | Location | Shared? |
|-------|----------|---------|
| Global | `~/.grok/config.toml` | No |
| Project (committed) | `<project>/.grok/config.toml` (every level from repo root to cwd) | Yes if committed |
| Project personal | `<project>/.claude/settings.local.json` | No (gitignore) |
| Managed | `/etc/grok/managed_config.toml`, `~/.grok/managed_config.toml` | Org-managed |
| Interactive grants | Internal state dir, per project launch cwd | No |

There is no native `config.local.toml`; use Claude local settings for personal project rules. Rules load at **session start**; changes apply on the next session.

### CLI flags

```bash
grok -p "Review the API changes" \
  --allow 'Bash(git *)' \
  --allow 'Bash(gh *)' \
  --allow 'Read' \
  --allow 'Grep' \
  --deny 'Bash(rm -rf *)'
```

`--allow` and `--deny` may be repeated and are always enforced.

### Native TOML

Structured rules:

```toml
[permission]
rules = [
  { action = "allow", tool = "bash", pattern = "git *" },
  { action = "allow", tool = "bash", pattern = "gh *" },
  { action = "allow", tool = "read" },
  { action = "allow", tool = "grep" },
  { action = "deny",  tool = "bash", pattern = "rm -rf *" },
  { action = "ask",   tool = "edit" },
]
```

Compact string arrays (same DSL as CLI / Claude settings):

```toml
[permission]
deny = [
  "Read(/Users/you/private/**)",
  "Edit(/Users/you/private/**)",
  "Bash(rm -rf *)",
]
allow = [
  "Bash(git *)",
  "Bash(gh *)",
]
```

Structured `tool` values: `bash`, `read`, `edit`, `grep`, `mcp`, `webfetch`, `websearch` (and `any` when omitted/default). Omitting `action` defaults to **deny** (safe default).

A catch-all `deny` on `bash` plus narrow `allow` rules does **not** implement “only git”: deny wins for all bash. For deny-by-default, use `defaultMode: "dontAsk"` or a `PreToolUse` allow-list hook.

Managed `allow` rules lose to user/project `deny` and `ask` (severity). Catch-all managed allows are ignored when always-approve is policy-locked off.

### Claude Code compatibility

```json
{
  "permissions": {
    "defaultMode": "dontAsk",
    "allow": ["Read", "Grep", "Bash(git *)", "Bash(gh *)"],
    "deny": ["Bash(rm -rf *)"]
  }
}
```

Grok loads `~/.claude/settings.json`, `settings.local.json`, and project `.claude` settings walking up to the repo root. Notes:

- MCP rules must use `MCPTool(server__tool)` — the `mcp__server__tool` form never matches.
- Unknown tool names and parameter rules such as `Agent(model:opus)` are skipped with a warning.
- `permissions.additionalDirectories` is parsed but not supported.
- Interactive import: **Ctrl+I** (“Import Claude settings”).

## Rule matching reference

### Bash rules

- **Prefix:** character prefix match with no word boundary by default — prefer `Bash(git *)` over `Bash(git)` (the latter also matches `gitleaks`).
- **Glob:** full-command glob; `*` matches any characters including spaces; `?` and `[...]` supported.
- Case-sensitive; leading whitespace trimmed; no other normalization.
- Trailing `:*` becomes a plain prefix (`Bash(git commit:*)` → prefix `git commit`).

**Chains:**

| Action | Matching |
|--------|----------|
| `deny` / `ask` | Every segment **and** the whole string; one denied segment rejects the whole command |
| `allow` | Whole command string only — `Bash(git *)` allows `git status && rm -rf /` |

Pair narrow allows with denials for patterns you never want.

### Path rules (`Read`, `Edit`, `Grep`)

- `*` / `?` do not cross `/`; `**` does.
- Bare filenames match only that exact string; use `**/.env` for any depth.
- No special treatment for `//` or `~/` (literal glob text). Cover both absolute and relative forms for boundary rules.
- Paths match as invoked (no canonicalization for direct tool checks).
- `Read` rules also govern grep; `Grep(...)` matches only grep.
- `Read`/`Edit` **deny** rules also apply to paths touched by shell commands (`cat`, `sed`, …) with **symlink resolution** at the shell-access gate; direct `read_file` / `search_replace` checks do **not** resolve symlinks. For OS-wide enforcement, combine with the sandbox.

### MCP and WebFetch

- `MCPTool(linear__*)` matches Grok’s `server__tool` names (no `mcp__` prefix).
- `WebFetch(domain:example.com)` matches that host and subdomains (case-insensitive; ignores leading `www.`; no wildcards inside `domain:`).
- Bare URL patterns glob the full URL: `WebFetch(https://api.example.com/*)`.

### Tool name prefixes

Recognized: `Bash`, `Read` / `NotebookRead`, `Edit` / `Write` / `NotebookEdit`, `Grep` / `Glob`, `MCPTool`, `WebFetch`, `WebSearch`. Bare `*` matches every tool. Globs are not supported in the tool-name position.

## Interactive approvals and remembered grants

When a prompt is required, common choices:

- **Allow once** / **Reject once** (optional message back to the model)
- **Enable always-approve** for the session (policy pin may block this)
- **Allow all edits this session** (in-memory only; not persisted)

### Per-command “Always allow”

Off by default. Enable:

```toml
# ~/.grok/config.toml
[ui]
remember_tool_approvals = true
```

Or env: `GROK_REMEMBER_TOOL_APPROVALS=1`. Precedence: **requirements > env > config > managed > remote > default false**.

When enabled, prompts can offer always-allow / never-allow for a **short command prefix**, MCP tool, or web-fetch domain. Read-only commands remember the listed prefix (e.g. `git status`), not the full argument list. Dangerous commands re-prompt rather than reuse a remembered prefix.

Grants are stored under Grok’s home state directory, scoped to the project directory used at launch — not in the repo, not shared across projects, not for hand-editing. For reviewable team policy, put declarative rules in `.grok/config.toml`.

## PreToolUse hooks as a hard allow-list

Hooks run **before** permission rules. Use them when you need an allow-list that holds in **every** permission mode (including always-approve). Only an explicit hook `deny` blocks; hook **allow** still falls through to deny rules.

Hooks are **fail-open**. A security-boundary hook must handle its own errors and inspect every chain segment.

### Example: only `git` and `gh`

**`~/.grok/hooks/git-gh-only.json`**

```json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "git-gh-only.sh",
            "timeout": 5
          }
        ]
      }
    ]
  }
}
```

**`~/.grok/hooks/git-gh-only.sh`**

```bash
#!/bin/sh
set -eu

deny() {
  echo '{"decision": "deny", "reason": "'"$1"'"}'
  exit 2
}

INPUT=$(cat)
CMD=$(echo "$INPUT" | jq -r '.toolInput.command // empty')
[ -n "$CMD" ] || deny "Empty command is not allowed"

CMD=$(echo "$CMD" | sed 's/&&/;/g; s/||/;/g')
case "$CMD" in
  *'$('*|*'`'*|*'&'*|*'>'*|*'<'*) deny "Substitution, background, and redirection are not permitted" ;;
esac

echo "$CMD" | tr ';|' '\n\n' | while IFS= read -r SEGMENT; do
  SEGMENT=$(echo "$SEGMENT" | sed 's/^[[:space:]]*//')
  [ -n "$SEGMENT" ] || continue
  case "$SEGMENT" in
    git\ *|git|gh\ *|gh) ;;
    *) deny "Only git and gh commands are permitted. Blocked segment: $SEGMENT" ;;
  esac
done
```

```bash
chmod +x ~/.grok/hooks/git-gh-only.sh
```

Hook discovery, trust for project hooks, and other lifecycle events are covered on the hooks page.

## Example configurations

### Headless git / gh only

```bash
grok -p "Implement the feature using only git and GitHub CLI" \
  --allow 'Read' \
  --allow 'Grep' \
  --allow 'Bash(git *)' \
  --allow 'Bash(gh *)'
```

Add the `git-gh-only` hook to deny other bash. For deny-by-default on all tools:

```json
{ "permissions": { "defaultMode": "dontAsk" } }
```

### Read-only code reviewer

```toml
# .grok/config.toml
[permission]
rules = [
  { action = "allow", tool = "read" },
  { action = "allow", tool = "grep" },
  { action = "deny",  tool = "edit" },
  { action = "deny",  tool = "bash" },
]
```

### Interactive development

Use `default` mode plus narrow `Bash(...)` allows for common commands (`git *`, `cargo test *`, `rg *`). Optionally enable `remember_tool_approvals` for personal friction reduction.

## Interaction with the sandbox

| Layer | Controls |
|-------|----------|
| Permissions + hooks | What the agent is **allowed to request** |
| Sandbox (`--sandbox`, profiles) | What the **process** can do after approval |

Recommended stack for untrusted checkouts:

1. `dontAsk` + narrow allow rules, and/or a restrictive `PreToolUse` hook  
2. `--sandbox strict` (or a custom deny-glob profile)  
3. Review project hooks and permission rules before trusting an unfamiliar repo (project allow rules apply without a separate trust prompt)

## TUI and session controls

- Permission decisions appear in the transcript / scrollback.
- `/always-approve` toggles always-approve when not policy-locked; other modes come from `defaultMode` / config.
- With remembered approvals enabled, prompts show per-command always/never rows.
- Manage hooks and plugins via `/hooks` and `/plugins` (on most terminals **Ctrl+L** opens Extensions; in VS Code / Cursor / Windsurf / Zed, `Ctrl+L` is mid-turn interject instead).

## Failure modes and practices

| Risk | Mitigation |
|------|------------|
| Allow rule matches only the whole chain string | Add segment-level **deny** / **ask** rules or a hook that inspects every segment |
| Safe-bash list treated as a security boundary | Treat it as convenience only; use `dontAsk` + denies + sandbox for security |
| Hook fails open under crash/timeout | Make hooks defensive; monitor UI hook failures; pair with deny rules |
| Project config from unknown source | Review `.grok/config.toml` and `.claude/settings.json` (and hooks) before work |
| `Bash(git)` matching `gitleaks` | Use `Bash(git *)` (trailing space + wildcard) |
| Symlink escape via shell | Combine path deny rules with sandbox; shell path gate resolves symlinks, direct tools may not |
| Always-approve in shared environments | Pin `disable_bypass_permissions_mode` in system requirements |

**Practices:** prefer narrow patterns; stack independent layers; test under `dontAsk` with representative commands; keep shared policy in committed config, not personal grants.

## Related pages

<CardGroup cols={2}>
  <Card title="Hooks" href="/hooks">
    PreToolUse / PostToolUse discovery, runners, and fail-open semantics.
  </Card>
  <Card title="Sandbox profiles" href="/sandbox">
    OS isolation profiles, --sandbox, deny globs, platform differences.
  </Card>
  <Card title="Headless mode" href="/headless-mode">
    -p runs, tool allow/deny lists, max-turns, CI patterns.
  </Card>
  <Card title="Configure Grok" href="/configure-grok">
    Config precedence, requirements, and grok inspect.
  </Card>
  <Card title="Configuration reference" href="/configuration-reference">
    Schema for [permission], [ui], sandbox, and env counterparts.
  </Card>
  <Card title="Plan mode" href="/plan-mode">
    Structured planning and how it interacts with tools and approvals.
  </Card>
  <Card title="Agent mode (ACP)" href="/agent-mode">
    ACP permission surface for IDEs and custom clients.
  </Card>
  <Card title="Subagents and personas" href="/subagents">
    Capability modes and isolation for child agents.
  </Card>
</CardGroup>

---

## 07. Project rules and memory

> AGENTS.md discovery and precedence; cross-session memory (/flush, /dream, hybrid search); when rules inject into the system prompt versus when memory retrieval runs.

- Page Markdown: https://grok-wiki.com/public/docs/xai-org-grok-build-90205de50458/pages/07-project-rules-and-memory.md
- Generated: 2026-07-16T01:19:32.725Z

### Source Files

- `crates/codegen/xai-grok-pager/docs/user-guide/12-project-rules.md`
- `crates/codegen/xai-grok-pager/docs/user-guide/13-memory.md`
- `crates/codegen/xai-grok-memory/src/lib.rs`
- `crates/codegen/xai-grok-memory/src/search.rs`
- `crates/codegen/xai-grok-memory/src/dream.rs`
- `crates/codegen/xai-grok-pager/src/memory_cmd.rs`
- `crates/codegen/xai-grok-shell/src/util/config/resolve/system_prompt.rs`

---
title: "Project rules and memory"
description: "AGENTS.md discovery and precedence; cross-session memory (/flush, /dream, hybrid search); when rules inject into the system prompt versus when memory retrieval runs."
---

Grok Build loads **project rules** from `AGENTS.md` (and compat aliases / rules directories) on every session, and optionally maintains **cross-session memory** under `~/.grok/memory/` with hybrid search. Rules and memory enter the model through different conversation slots: session CLI rules fold into the **system prompt**; discovered project instruction files inject as a tagged **user** item; memory search hits inject into the **system** message as `<memory-context>` on the first turn (and again after compaction when needed).

## Rules vs memory at a glance

| Concern | Project rules (`AGENTS.md`, rules dirs) | Cross-session memory |
|---------|----------------------------------------|----------------------|
| Default | Always discovered when present | **Off** (experimental) |
| On-disk location | Repo tree + `~/.grok/` (+ optional `~/.claude/`, `~/.cursor/`) | `~/.grok/memory/` only |
| When loaded | Session spawn (full file contents); later path access may **remind** of new files | Storage init when enabled; index + search on demand |
| Primary injection slot | **User** message: `SyntheticReason::ProjectInstructions` (`<system-reminder>` block) | **System** message: `<memory-context>…</memory-context>` on first turn |
| System prompt role | Template may note tools; **not** the full AGENTS body | `memory_enabled` placeholder enables tool guidance; retrieved chunks are separate |
| Session-only override | `--rules` / `--append-system-prompt` → `<human_rules>` on system prompt | `/memory on\|off` (session-scoped, does not rewrite `config.toml`) |
| Full replace | `--system-prompt-override` / `--system-prompt` replaces the agent system prompt | N/A |
| Model tools | None for rules (agent reads files if needed) | `memory_search`, `memory_get` when enabled |

```mermaid
flowchart TB
  subgraph sources [On-disk sources]
    AG["AGENTS.md / Claude.md / rules/*.md"]
    MEM["~/.grok/memory/**/*.md"]
    CLI["--rules / --system-prompt-override"]
  end

  subgraph spawn [Session spawn]
    DISC["read_agents_config_with_options"]
    SYS["Rendered system prompt"]
    PI["ConversationItem::project_instructions"]
  end

  subgraph turn [First turn / tools]
    INJ["hybrid_search → inject_memory_reminder"]
    TOOLS["memory_search / memory_get"]
  end

  AG --> DISC
  DISC --> PI
  CLI --> SYS
  MEM --> INJ
  MEM --> TOOLS
  INJ --> SYS
```

## Project rules

### Recognized filenames

In each scanned directory, Grok checks these names **in order** (all matches load; case-insensitive filesystems dedupe by canonical path):

| Order | Name | Notes |
|-------|------|--------|
| 1 | `Agents.md` | |
| 2 | `Claude.md` | Claude Code compat |
| 3 | `CLAUDE.md` | Claude Code compat |
| 4 | `CLAUDE.local.md` | Often gitignored for personal overrides |
| 5 | `AGENT.md` | |
| 6 | `AGENTS.md` | Common project default |
| 7–8 | `.claude/CLAUDE.md`, `.claude/CLAUDE.local.md` | Only when Claude **agents** compat is on |

Only these top-level names are discovered as project instruction files. Custom names (for example `AGENTS.local.md`) are **not** loaded unless they live inside a rules directory as `*.md`.

### Rules directories

At each directory level `<dir>` from the discovery chain:

| Path | When scanned |
|------|----------------|
| `<dir>/.grok/rules/*.md` | Always |
| `<dir>/.claude/rules/*.md` | Claude **rules** compat on (default) |
| `<dir>/.cursor/rules/*.md` | Cursor **rules** compat on (default) |

Files are sorted alphabetically within each rules directory. YAML frontmatter is stripped for `.grok/rules/` and `.claude/rules/` content before injection so globs/metadata do not leak into the prompt.

Disable vendor surfaces via `[compat]` in config or the corresponding env vars (see [Configure Grok](/configure-grok)).

### Discovery order and precedence

Implementation: `xai_grok_agent::prompt::agents_md::read_agents_config_with_options`.

1. **Global** — `~/.grok/` (always first).
2. **Home vendor dirs** — `~/.claude/`, `~/.cursor/` when the matching compat **agents** cell is enabled and the directory exists.
3. **Git chain** — if `cwd` is inside a git workdir, every directory from **repo root → cwd** (inclusive), root first so **deeper files appear later and win on conflicts**.
4. **Non-git** — only `cwd` (plus globals).
5. Optional **workspace user dir** may insert after the repo root (higher priority than root AGENTS, lower than intermediate/cwd).

```text
~/.grok/AGENTS.md
repo/AGENTS.md
repo/src/AGENTS.md
repo/src/components/AGENTS.md   ← later in the reminder = higher precedence
```

**Gitignore:** paths ignored by `.gitignore` are skipped during discovery.

**Size:** each matched file is loaded in full (no character cap / truncation). Keep rules concise; they consume context.

**`.grok/` project layout (related, not all rules):** project-scoped `config.toml`, `skills/`, `plugins/`, `agents/`, `hooks/`, `lsp.json` live under `.grok/` but are separate surfaces from AGENTS discovery.

### How project rules enter the conversation

Project instruction files are **not** merged into the default system prompt template body.

| Stage | Behavior |
|-------|----------|
| Spawn | Full contents render into a `<system-reminder>` block (“deeper files take precedence…”). Inserted as `ConversationItem::project_instructions` near the head of the conversation (user-role synthetic item). |
| Subagents | Same full AGENTS reminder as the primary agent (`agents_md_user_reminder`). |
| Permission classifier | AGENTS text can be forwarded as project instructions for auto-mode classification. |
| Mid-session access | `AgentsMdTracker` walks parents (max depth 10, within git root) when tools touch paths outside the initial chain. New files produce **path-only** reminders once per session/compaction cycle; the model decides whether to read them. Initial discovery paths are never re-reminded. |

Inspect loaded files and approximate token cost:

```bash
grok inspect
```

### Session CLI rules (system prompt)

| Flag | Effect |
|------|--------|
| `--rules` / `--append-system-prompt` | Appends text to the **system** prompt wrapped in `<human_rules>…</human_rules>` (fresh session only; not re-applied on resume unless the full override path is used). |
| `--system-prompt-override` / `--system-prompt` | Uses the text **verbatim** as the system prompt; skips default template and `--rules`. |

ACP clients can pass the same fields via session/init `_meta` (`rules`, `systemPromptOverride`). Override wins over rules; blank override is ignored.

```bash
grok --rules "Always use TypeScript. Prefer functional components."
grok --system-prompt-override "You are a release bot. Only answer about versioning."
```

### Authoring guidance (concise)

- Put monorepo-wide conventions in root `AGENTS.md`; package-specific rules in package dirs.
- Prefer actionable commands (`cargo test -p foo`) over essay-style docs.
- Commit shared rules; keep personal overrides in `~/.grok/` or gitignored `CLAUDE.local.md`.
- Do not duplicate the README; link out when needed.

## Cross-session memory

Memory is **experimental and disabled by default**. When enabled, Markdown files under `~/.grok/memory/` are chunked, indexed (SQLite FTS5 + optional vec0 embeddings), and searchable via hybrid search and model tools.

### Enable / disable

| Priority | Source | Effect |
|----------|--------|--------|
| 1 (highest) | `--no-memory` or `GROK_MEMORY=0`/`false` | Force off |
| 2 | `--experimental-memory` or `GROK_MEMORY=1`/`true` | Force on |
| 3 | `[memory] enabled` in config (plus remote settings when local section absent) | Persistent preference |
| 4 | Default | `enabled = false` |

```bash
grok --experimental-memory
export GROK_MEMORY=1
```

```toml
# ~/.grok/config.toml
[memory]
enabled = true
```

Mid-session (TUI): `/memory on`, `/memory off`, or `t` inside the `/memory` modal. Toggle is **session-scoped**; it does not write `config.toml`. Off unregisters memory tools but leaves files on disk.

### Storage layout

```text
~/.grok/memory/
├── MEMORY.md                          # Global curated knowledge
└── {project-slug}-{hash8}/            # Workspace scope
    ├── MEMORY.md                      # Project curated knowledge
    ├── sessions/
    │   └── YYYY-MM-DD-{slug}-{sid8}.md
    └── index.sqlite                   # FTS + optional embeddings
```

**Workspace directory naming:** `{slug}-{hash8}` where `slug` is the repo name (or directory name) and `hash8` is the first 8 hex chars of a blake3 hash. Identity prefers git remote `origin` as `org/repo` so clones and worktrees of the same remote share one memory directory; otherwise the canonical filesystem path is used.

Writes from ephemeral temp CWDs are skipped. Optional GC under `[memory.gc]` can remove orphan workspace dirs after age thresholds.

### What gets written

| Mechanism | Content | When |
|-----------|---------|------|
| Session-end auto-save | Metadata summary: message counts, up to five topic prompts, UTC time — **no** tool I/O, paths, or shell commands | Session end if `session.save_on_end = true` (default). Skips trivial sessions (&lt;3 substantive prompts or &lt;50 bytes user text). |
| `/flush` | LLM-written rich summary of decisions/patterns into a dated session log | Manual, or pre-compaction flush when configured under `[compaction.memory_flush]` |
| `/remember` or natural-language “remember …” | Append to global or workspace `MEMORY.md` (review panel; confirm before write) | User-initiated |
| “forget …” | Best-effort model edit of matching entries | User-initiated; edit files directly for guaranteed removal |
| `/dream` / auto-dream | Consolidate session logs into durable workspace topics; may delete consolidated session files | Manual or gated auto at session end (and optional interval) |

Background flush/dream/session-end saves do **not** post scrollback confirmations. `/remember` does: `Memory saved to …`.

### Dream consolidation

`/dream` and auto-dream synthesize recent session logs into organized markdown (merge, resolve contradictions, drop ephemeral noise). Gates (`[memory.dream]`):

| Key | Default | Role |
|-----|---------|------|
| `enabled` | `true` | Master switch for auto-dream |
| `min_hours` | `4` | Minimum hours since last consolidation |
| `min_sessions` | `3` | Minimum sessions since last consolidation |
| `stale_lock_secs` | `3600` | Reclaim stale dream lock |
| `check_interval_secs` | unset | If set, also check on an interval; default is session-end only |

Input to the dream model is capped (~32k chars). Empty/scaffold results yield no write.

### Hybrid search

Pipeline (`xai_grok_memory::search::hybrid_search`):

1. FTS5 BM25 (always)
2. Vector KNN when embeddings exist (else FTS-only with `text_weight = 1.0`)
3. Merge / normalize scores; drop empty/scaffold chunks
4. Temporal decay on **session** sources only (global/workspace evergreen)
5. Source weights + min score filter
6. Optional MMR diversity re-rank
7. Cap at `max_results`

Defaults (`[memory.search]`):

| Key | Default |
|-----|---------|
| `max_results` | `6` |
| `min_score` | `0.35` |
| `vector_weight` | `0.7` |
| `text_weight` | `0.3` |
| `temporal_decay.enabled` | `true` |
| `temporal_decay.half_life_days` | `7.0` |
| `mmr.enabled` | `false` |
| `mmr.lambda` | `0.7` |
| `source_weights.*` | `1.0` for `workspace`, `session`, `global` |

Session hits can include **staleness notes** in formatted output; global/workspace never do.

### When memory retrieval runs

| Trigger | Behavior |
|---------|----------|
| **First turn** (if `initial_injection.enabled`, default true) | Hybrid search on the last real user query (or a project-conventions fallback for greetings / short openers). Results upserted into the **system** message as `<memory-context>`. Skipped if a prior block already exists (prompt-cache preservation) or injection already ran this process. Default injection min score is **unset → 0.0** (no threshold) unless configured. |
| **After compaction** | Search again to recover context lost from history. |
| **Model tools** | `memory_search` (hybrid), `memory_get` (read by path) whenever memory is enabled for the session. |
| **File watcher** | Optional watch on `~/.grok/memory/`; dirty files reindex on the next search. |

Memory-enabled sessions also pass `memory_enabled` (and global/workspace paths) into system-prompt template placeholders so the base prompt can describe memory tools — that is **capability guidance**, not the retrieved snippets.

### TUI and CLI surfaces

| Surface | Role |
|---------|------|
| `/memory` | Browse global / workspace / session files (preview pane; filter `/`; copy path `y`; delete session files with `x`; toggle `t`) |
| `/remember [text]` | Confirm-and-save note |
| `/flush` | Rich session capture (requires memory on) |
| `/dream` | Manual consolidation (requires memory on) |
| `grok memory clear` | Shell clear: workspace (default), `--global`, `--all`, `--yes` |

```bash
grok memory clear              # workspace MEMORY.md, sessions/, index.sqlite
grok memory clear --global
grok memory clear --all --yes
```

### Configuration map

**Core (`[memory]`)**

| Key | Default | Description |
|-----|---------|-------------|
| `enabled` | `false` | Master enable |
| `session.save_on_end` | `true` | Metadata summary on end |
| `watcher.enabled` | `true` | External edit reindex |

**Index (`[memory.index]`)** — `max_chunk_chars` 1600, `chunk_overlap_chars` 320.

**Embedding (`[memory.embedding]`)** — `provider` `"api"`, `model` optional (unset disables vectors), `dimensions` 1024.

**Initial injection (`[memory.initial_injection]`)** — `enabled` true; `min_score` optional.

**Flush / pruning** live under **`[compaction]`**, not `[memory]`:

| Section | Purpose |
|---------|---------|
| `[compaction.memory_flush]` | Pre-compaction / idle flush limits, model, semantic dedup |
| `[compaction.pruning]` | Soft-trim / hard-clear old tool results in the live transcript (not memory files) |

Remote settings can override search/injection/dream/flush when the matching local TOML subsection is absent.

## Injection comparison (operational)

```text
System prompt (identity + tools template)
  ├─ optional <human_rules> from --rules          [spawn only]
  ├─ optional full override from --system-prompt-override
  └─ optional <memory-context> from first-turn / post-compact search

User conversation prefix
  └─ project_instructions: full AGENTS/rules <system-reminder>   [spawn]
       + later path-only reminders from AgentsMdTracker

Tool loop (memory on)
  └─ memory_search / memory_get against ~/.grok/memory index
```

**Practical rules of thumb**

- Put **stable project law** in `AGENTS.md` (always on, no feature flag).
- Put **learned decisions across sessions** in memory (opt-in; searchable; decays for session logs).
- Use `--rules` for one-shot session constraints without editing the repo.
- Use `/flush` before compaction or at the end of a high-value session; use `/dream` when session logs get noisy.
- After enabling memory mid-life of a session, start `/new` if you need first-turn injection for that enablement path.

## Troubleshooting

| Symptom | Check |
|---------|--------|
| Rules not applied | `grok inspect`; confirm filename is recognized; ensure file is not gitignored; run from intended cwd inside the git tree. |
| Conflicting instructions | Deeper directory wins; move conflicting guidance or delete the lower-priority file. |
| Memory “not working” | Confirm not overridden by `--no-memory` / `GROK_MEMORY=0`; enable with flag, env, or config; verify `grok inspect`. |
| No memory on first turn | Injection only on first turn of a session with memory already enabled; use `/new` after enabling. |
| Stale or empty search | Empty MEMORY.md scaffolds are filtered; run `/flush` or edit MEMORY.md; wait for watcher reindex on next search. |
| Shared memory across clones missing | Ensure shared `origin` remote identity; otherwise path-based hashes diverge. |

```bash
RUST_LOG=debug GROK_LOG_FILE=/tmp/grok.log grok --experimental-memory
grep -i memory /tmp/grok.log
```

## Related pages

<CardGroup>
  <Card title="Sessions" href="/sessions">
    Session lifecycle, resume/fork, and compaction — where flush and re-injection interact with history.
  </Card>
  <Card title="Configure Grok" href="/configure-grok">
    Config precedence, `[compat]` vendor toggles, and `grok inspect` for resolved settings.
  </Card>
  <Card title="Configuration reference" href="/configuration-reference">
    Full schema for `[memory.*]`, `[compaction.memory_flush]`, and related keys.
  </Card>
  <Card title="Slash commands" href="/slash-commands">
    `/memory`, `/flush`, `/dream`, `/remember`, and availability gates.
  </Card>
  <Card title="CLI reference" href="/cli-reference">
    `--experimental-memory`, `--no-memory`, `--rules`, `--system-prompt-override`, `grok memory clear`.
  </Card>
  <Card title="Skills" href="/skills">
    Skill packages under `.grok` / vendor trees — complementary to AGENTS rules directories.
  </Card>
  <Card title="Permissions and safety" href="/permissions-and-safety">
    Project instructions can feed the permission classifier alongside tool allow/deny rules.
  </Card>
</CardGroup>

---

## 08. Configure Grok

> How to set config.toml and pager.toml, precedence (CLI > env > user config > managed/requirements > defaults), feature flags, and grok inspect to dump resolved settings.

- Page Markdown: https://grok-wiki.com/public/docs/xai-org-grok-build-90205de50458/pages/08-configure-grok.md
- Generated: 2026-07-16T01:32:28.606Z

### Source Files

- `crates/codegen/xai-grok-pager/docs/user-guide/05-configuration.md`
- `crates/codegen/xai-grok-config/src/loader.rs`
- `crates/codegen/xai-grok-config/src/paths.rs`
- `crates/codegen/xai-grok-shell/src/util/config/load.rs`
- `crates/codegen/xai-grok-shell/src/util/config/resolve/mod.rs`
- `crates/codegen/xai-grok-shell/src/inspect/mod.rs`
- `crates/codegen/xai-grok-paths/src/lib.rs`

---
title: "Configure Grok"
description: "How to set config.toml and pager.toml, precedence (CLI > env > user config > managed/requirements > defaults), feature flags, and grok inspect to dump resolved settings."
---

Grok resolves runtime settings by deep-merging layered TOML (`managed_config.toml`, user `config.toml`, `requirements.toml`, optional macOS MDM) under `$GROK_HOME` (default `~/.grok`) or `/etc/grok`, then applying environment variables and CLI flags on top. Appearance and TUI layout live mainly in `pager.toml`; agent behavior, models, tools, features, and MCP live in `config.toml`. Missing files yield empty tables and built-in defaults—only override what you need.

## Config home and file map

| Path | Role |
|------|------|
| `$GROK_HOME` or `~/.grok` | User config root (`GROK_HOME` overrides home) |
| `~/.grok/config.toml` | Primary user settings |
| `~/.grok/pager.toml` | TUI appearance / layout |
| `~/.grok/managed_config.toml` | User-tier managed policy (org/sync) |
| `~/.grok/requirements.toml` | Requirements / policy pin (often signed cloud cache) |
| `/etc/grok/managed_config.toml` | System managed (Unix) |
| `/etc/grok/requirements.toml` | System requirements (Unix) |
| `.grok/config.toml` | Project MCP, plugins, `[permission]`, some tool caps |
| macOS MDM `ai.x.grok` (`requirements_toml_base64`) | Highest requirements tier when admin-forced |

On Unix, system config is `/etc/grok`. On Windows there is no system dir. Loaders never promote a cwd-relative `.grok/` to the **user** tier when no home resolves—project files stay project-scoped.

String values support `$VAR` / `${VAR}` expansion at load time.

## Precedence

### Operator-facing order (highest first)

1. **CLI flags** — e.g. `--model`, `--sandbox`, `--yolo`, `--minimal` / `--fullscreen`
2. **Environment variables** — e.g. `XAI_API_KEY`, `GROK_MEMORY`, `GROK_TELEMETRY_ENABLED`
3. **User `config.toml`** — `$GROK_HOME/config.toml`
4. **Managed / requirements** — `managed_config.toml` and `requirements.toml` (system + user + MDM)
5. **Built-in defaults**

Use this mental model for day-to-day overrides. A few knobs invert managed vs user so org policy cannot be re-armed from user TOML (see feature flags below).

### Disk layer merge (lowest → highest)

Effective base config merges:

1. `/etc/grok/managed_config.toml` (system managed)
2. `$GROK_HOME/managed_config.toml` (user managed)
3. `$GROK_HOME/config.toml` (user)
4. `$GROK_HOME/requirements.toml`
5. `/etc/grok/requirements.toml`
6. macOS MDM requirements (when present)

Each layer may apply `[[version_overrides]]` before merge. Campaign patches can overlay fields after the base merge; requirements are re-applied so admin pins beat campaigns. The shell’s full path (`load_effective_config`) also honors remote campaign cache and `GROK_CAMPAIGNS_OVERRIDE`; one-shot CLI paths may use disk-only resolution.

### Project vs global

| Surface | Rule |
|---------|------|
| `[mcp_servers]`, `[plugins]` | Nearest project `.grok/config.toml` wins by name (full replace, not deep merge) over repo-root then user global |
| `[permission]` | Project sections override global wholesale for the chosen file; rule evaluation still merges deny > ask > allow across sources |
| Most other sections | Loaded only from effective global layers (`~/.grok/config.toml` + managed/requirements), not project files |

## `config.toml` (behavior)

Location: `~/.grok/config.toml`.

```toml
[cli]
auto_update = true

[models]
default = "grok-build"
web_search = "grok-4.20-multi-agent"
temperature = 0.7
top_p = 0.95
max_completion_tokens = 8192
max_retries = 8
stream_tool_calls = true

[ui]
simple_mode = true              # prompt editor: true = readline, false = vim-style
vim_mode = false                # scrollback vim keys (independent of simple_mode)
screen_mode = "fullscreen"      # sticky "minimal" | "fullscreen"; CLI flags rewrite this
theme = "auto"                  # or groknight, tokyonight, etc. (see Theming)
remember_tool_approvals = false
show_thinking_blocks = true
group_tool_verbs = true
default_selected_permission = "always_allow_all_sessions"

[features]
telemetry = false
feedback = true
lsp_tools = false
codebase_indexing = true
two_pass_compaction = false
remote_fetch = true             # model catalog / remote settings; air-gap: set false
# managed_config = true         # separate gate for background managed-config sync

[session]
auto_compact_threshold_percent = 85
load_envrc = true

[tools]
respect_gitignore = false
```

### Common sections

| Section | Purpose |
|---------|---------|
| `[cli]` | Auto-update and launch behavior |
| `[models]` / `[model.<id>]` | Default model, sampling, BYOK / OpenAI-compatible endpoints |
| `[ui]` | Prompt/scrollback UX, theme, sticky screen mode, notifications, scroll speed |
| `[features]` | Product feature toggles (telemetry, LSP tool, indexing, remote fetch, …) |
| `[session]` | Compact threshold, `.envrc` loading |
| `[tools]` | Cross-tool defaults (e.g. gitignore respect) |
| `[toolset.*]` | Per-tool timeouts and limits (`bash`, `ask_user_question`, `web_fetch`, …) |
| `[mcp_servers.<name>]` | MCP stdio/HTTP servers |
| `[skills]`, `[plugins]`, `[subagents]`, `[memory]` | Discovery paths, disables, routing |
| `[compat.cursor\|claude\|codex]` | Vendor skill/rule/MCP/hooks discovery cells (default on; env > TOML > default) |
| `[auth]`, `[grok_com_config]` | Auth provider binary, OIDC, enterprise login pins |
| `[permission]` | Allow/deny/ask rules (also project `.grok/config.toml`) |
| `[hints]` | Persisted “don’t ask again” UI prefs (TUI writes user file only) |
| `[telemetry]` | Collector redirects and optional external OTLP |

Credential resolution for models: `api_key` > `env_key` > signed-in session > `XAI_API_KEY`.

### Env overrides (selected)

| Variable | Effect |
|----------|--------|
| `GROK_HOME` | Config directory root |
| `XAI_API_KEY` | API key for CI/headless |
| `GROK_MEMORY` | `1`/`0` cross-session memory |
| `GROK_SUBAGENTS` | Enable/disable subagents |
| `GROK_WEB_FETCH` | Enable/disable `web_fetch` |
| `GROK_SANDBOX` | Sandbox profile name |
| `GROK_TELEMETRY_ENABLED` / `GROK_FEEDBACK_ENABLED` | Telemetry / feedback |
| `GROK_RESPECT_GITIGNORE` | Force `[tools] respect_gitignore` |
| `GROK_DEFAULT_SELECTED_PERMISSION` | First approval-menu cursor row |
| `GROK_SCROLL_SPEED`, `GROK_SCROLL_MODE`, `GROK_SCROLL_LINES`, `GROK_INVERT_SCROLL` | Scroll UX (load-time) |
| `GROK_CAMPAIGNS` | `0`/`false` disables campaign overlays |
| `GROK_DISABLE_API_KEY_AUTH` | Enterprise login hardening |

Env bool parsing accepts `1/true/yes/on/enabled` and `0/false/no/off/disabled`.

## Feature flags (`[features]`)

Feature keys are TOML booleans under `[features]`, often mirrored by `GROK_*` env vars. Important behaviors:

| Flag | Default (typical) | Notes |
|------|-------------------|-------|
| `telemetry` | off/on per build docs | Master anonymous usage telemetry |
| `feedback` | `true` | `/feedback` and related UI |
| `lsp_tools` | `false` | Expose the `lsp` tool |
| `codebase_indexing` | `true` | Code-graph indexing |
| `two_pass_compaction` | `false` | Opt-in two-pass compact |
| `remote_fetch` | `true` | Online model catalog + remote settings; **no env re-arm**; managed/requirements beat user for air-gapped pins |
| `managed_config` | (sync gate) | Background managed-config sync, independent of `remote_fetch` |
| `campaigns` | on unless disabled | Also killable with `GROK_CAMPAIGNS=0` |

**Policy knobs** such as `remote_fetch` walk layers as requirements (MDM > system > user) → managed → system managed → user, so a managed/requirements `false` cannot be undone by a user `true`. Ordinary effective merge still puts user over managed for preference fields.

`[compat.*]` cells use **env > config.toml > default (on)** and are reported by `grok inspect`.

## `pager.toml` (appearance)

Location: `~/.grok/pager.toml` (`$GROK_HOME/pager.toml`).

Controls TUI layout and chrome. Theme **names** primarily live under `config.toml` `[ui] theme` (and `/theme`); `pager.toml` owns terminal mode, animation, prompt chrome, scrollback geometry, and per-block styling. Changes apply on restart (dev builds may hot-reload `pager.toml`).

```toml
[terminal]
alt_screen = "auto"           # auto | always | never

[animation]
fps = 30
wave_rows = 32

[prompt]
collapse_unfocused = true
mouse_hover = true
show_prefix = true

[scrollback.layout]
outer_vpad = 1
outer_hpad_left = 2
outer_hpad_right = 2

[scrollback.scrollbar]
enabled = true

[scrollback.scroll]
follow_indicator = "center"
follow_auto_select = true
respect_manual_folds = false  # opt-in pin for manual folds

[scrollback.display]
sticky_headers = true
tab_width = 4

[scrollback.blocks.edit]
# expanded_by_default / line_summary: unset follows [ui] collapsed_edit_blocks
indent = true

[scrollback.blocks.thinking]
animate = true
truncate_lines = 3

[todo]
badge_format = "default"      # default | colon | comma

disable_plugins = false
```

**Screen mode interaction:** `[ui] screen_mode` in `config.toml` is the sticky fullscreen/minimal preference written by `--minimal` / `--fullscreen` and `/minimal` / `/fullscreen`. It outranks legacy `[terminal] minimal` in `pager.toml`. Explicit CLI flags win for that process and update the sticky key.

For color themes and truecolor constraints, see [Theming](/theming).

## `grok inspect` — dump resolved discovery

```bash
grok inspect
grok inspect --json
```

One-shot introspection of **cwd** (not a live session). Builds an `InspectReport` with:

- Version / channel / cwd / git project root
- **Folder trust** — when untrusted, project hooks, plugins, and project MCP/LSP are gated like runtime
- Project instructions (`AGENTS.md` and vendor peers) with scope and approx tokens
- Permissions sources, skipped rules, managed-settings path/active flags, enforced policies
- Login policy (`disable_api_key_auth`, forced team UUID)
- Hooks, skills, agents, plugins, marketplaces, MCP, LSP
- **`config_sources.layers`** — each contributing layer with role and path
- External compat cell resolution
- Model-override parse warnings

Layer roles reported:

| Role | Source |
|------|--------|
| `system-managed` | `/etc/grok/managed_config.toml` |
| `managed` | `$GROK_HOME/managed_config.toml` |
| `user` | `$GROK_HOME/config.toml` |
| `requirements` | `$GROK_HOME/requirements.toml` |
| `system-requirements` | `/etc/grok/requirements.toml` |
| `mdm` | macOS forced preferences (synthetic path label) |
| `project` | Ancestor `.grok/config.toml` files |

Layers that exist but contribute nothing after load processing get a `note` (`empty` or parse error). Use `--json` for machine-readable camelCase fields in CI or support bundles.

`grok inspect` does **not** print the fully merged TOML blob of every key; it dumps discovery surfaces and which config files participate. For key-level schema, see the configuration reference.

## Project-scoped files (quick map)

| Path | Contributes |
|------|-------------|
| `.grok/config.toml` | MCP, plugins, permission rules, limited tool caps |
| `.grok/skills/`, `.grok/hooks/`, `.grok/agents/`, `.grok/plugins/` | Local discovery |
| `.grok/lsp.json` / `~/.grok/lsp.json` | LSP servers |
| `.grok/sandbox.toml` | Custom sandbox profiles |
| `AGENTS.md` | Project instructions (system prompt) |

## Minimal workflow

1. Create or edit `~/.grok/config.toml` with only the sections you need.
2. Optionally tune `~/.grok/pager.toml` for scrollback/terminal chrome.
3. Prefer env vars for CI-safe overrides that must not rewrite disk config.
4. Run `grok inspect` (and `--json` if scripting) from the project directory to verify trust, layers, MCP, skills, and permissions.
5. For org pins, deploy `managed_config.toml` / `requirements.toml` under `/etc/grok` or MDM rather than relying on user files alone.

## Related pages

<CardGroup cols={2}>
  <Card title="Configuration reference" href="/configuration-reference">
    Schema-oriented keys for config.toml and pager.toml sections and env counterparts.
  </Card>
  <Card title="CLI reference" href="/cli-reference">
    Top-level commands including inspect, setup, and shared runtime flags.
  </Card>
  <Card title="Theming" href="/theming">
    Theme names, /theme, color support, and screen modes.
  </Card>
  <Card title="Permissions and safety" href="/permissions-and-safety">
    Allow/deny/ask rules, modes, and how config interacts with authorization.
  </Card>
  <Card title="MCP servers" href="/mcp-servers">
    Register stdio and HTTP MCP servers in config.toml.
  </Card>
  <Card title="Custom models" href="/custom-models">
    -m, /model, BYOK, and OpenAI-compatible endpoints.
  </Card>
  <Card title="Authentication" href="/authentication">
    OAuth, device code, XAI_API_KEY, and credential storage.
  </Card>
  <Card title="Troubleshooting" href="/troubleshooting">
    Terminal, color, auth recovery, and diagnostic probes.
  </Card>
</CardGroup>

## Next

<CardGroup cols={2}>
  <Card title="Configuration reference" href="/configuration-reference">
    Full key inventory after you know the load order.
  </Card>
  <Card title="Headless mode" href="/headless-mode">
    Non-interactive -p runs with env and tool allowlists for CI.
  </Card>
</CardGroup>

---

## 09. Custom models

> Select models via -m and /model; configure BYOK and OpenAI-compatible or Ollama endpoints; per-model overrides; grok models listing and provider-neutral endpoint settings.

- Page Markdown: https://grok-wiki.com/public/docs/xai-org-grok-build-90205de50458/pages/09-custom-models.md
- Generated: 2026-07-16T01:45:55.788Z

### Source Files

- `crates/codegen/xai-grok-pager/docs/user-guide/11-custom-models.md`
- `crates/codegen/xai-grok-models/src/lib.rs`
- `crates/codegen/xai-grok-models/src/catalog.rs`
- `crates/codegen/xai-grok-shell/src/cli_models.rs`
- `crates/codegen/xai-grok-shell/src/sampling/mod.rs`
- `crates/codegen/xai-grok-pager/src/models.rs`

---
title: "Custom models"
description: "Select models via -m and /model; configure BYOK and OpenAI-compatible or Ollama endpoints; per-model overrides; grok models listing and provider-neutral endpoint settings."
---

Grok Build resolves a **model catalog** from built-in defaults, optional remote `/v1/models` prefetch, and user `[model.*]` tables in `~/.grok/config.toml`, then routes inference through `api_backend` (`chat_completions`, `responses`, or `messages`) and per-model credentials (BYOK `api_key` / `env_key`, session token, or `XAI_API_KEY`). Selection is CLI (`-m` / `--model`), TUI (`/model`, `/m`, model picker), or persistent `[models].default`.

## Default catalog

Without custom config, new sessions use the baked-in default from `default_models.json` (currently `grok-build`). Aux roles also have baked-in pins (for example web search and image description) that fall back to the primary default when unset.

```bash
grok models
```

`grok models` prints auth banner status (API key, OAuth host, per-model BYOK, deployment key, or unauthenticated), the resolved default, and the available model list (current marked with `*`).

## Selecting a model

### CLI

```bash
grok -m grok-build
grok -p "Hello" --model my-model
grok -m my-model --reasoning-effort high
```

`-m` / `--model` sets the session model. `--reasoning-effort` / `effort` sets reasoning effort when the selected model supports it.

### Slash command

In the TUI:

```
/model grok-build
/m grok-build
/model reasoning-model high
```

`/model` (alias `/m`) switches the active model. Optional trailing effort tokens are accepted for models that expose a reasoning-effort menu. Exact catalog name/id match is preferred so multi-word display names resolve correctly.

### Model picker

From the scrollback pane, open the model picker (documented as `Ctrl+M` in the user guide). With the prompt focused, that binding toggles multiline input instead—use `/model` from the prompt.

### Persistent default

```toml
[models]
default = "grok-build"
```

Resolution order for the active model is typically CLI/session selection over config default over built-in default. Use `grok inspect` (see [Configure Grok](/configure-grok)) to dump the effective config when debugging precedence.

## API backends

Set `api_backend` on a `[model.*]` entry. Omitted value defaults to OpenAI Chat Completions.

| Config value | Protocol | Path shape |
|--------------|----------|------------|
| `chat_completions` (default) | OpenAI Chat Completions | `/v1/chat/completions` |
| `responses` | OpenAI Responses | `/v1/responses` |
| `messages` | Anthropic Messages | `/v1/messages` |

Provider-specific auth headers (for example Anthropic `x-api-key` and `anthropic-version`) go in `extra_headers`; they are sent verbatim on inference requests.

## Per-model configuration

Declare custom or override entries under `[model.<catalog-key>]` in `~/.grok/config.toml`:

```toml
[model.my-model]
model = "model-id"                        # routing slug sent to the API
base_url = "https://api.example.com/v1"   # OpenAI-compatible or other endpoint
name = "Display Name"                     # picker / UI label
description = "Optional description"
api_key = "sk-..."                        # inline BYOK (optional)
env_key = "PROVIDER_API_KEY"              # string or array of env names
api_backend = "chat_completions"          # chat_completions | responses | messages
temperature = 0.7
top_p = 0.95
max_completion_tokens = 8192
context_window = 128000                   # tokens; drives auto-compaction
extra_headers = { "x-api-key" = "sk-..." }
stream_tool_calls = false                 # opt out if endpoint rejects the field
```

### Field notes

| Field | Role |
|-------|------|
| `model` | Wire-level model id (may differ from the TOML table key). |
| `base_url` | Inference base URL for this catalog entry. |
| `api_base_url` | Optional alternate base when using global API-key auth (session still uses `base_url`). |
| `api_key` / `env_key` | BYOK credentials; `env_key` may be one name or an ordered list (first non-empty wins). |
| `context_window` | Token budget for compaction. New models without this field default to **200,000**. Overrides of known models inherit the donor window when possible. |
| `hidden` / `supported_in_api` | Picker visibility. `hidden` hides for all auth; `supported_in_api = false` hides for API-key users but still shows for OAuth. |
| `supports_backend_search` | Enables server-side web search for that model when the build supports it (independent of `api_backend`). |
| `reasoning_effort` / `reasoning_efforts` | Default and menu for reasoning models. |

### Credential resolution (sampling)

For a request, credentials resolve in this order:

1. Model BYOK: non-empty `api_key`, else first set non-empty `env_key`
2. Signed-in session token (`grok login`)
3. Global `XAI_API_KEY` (also accepts legacy `GROK_CODE_XAI_API_KEY`)

When only a global API key applies, `api_base_url` is preferred over `base_url` if set.

**Note:** The `grok models` banner uses a different display order (env key → session → BYOK → deployment → none) so logged-in users see the auth host even when BYOK is also configured.

### Catalog merge priority

Final map assembly (highest wins):

1. User `[model.*]` overrides and additions
2. Prefetched remote models from the models list endpoint
3. Hardcoded defaults from `default_models.json` (skipped when a custom models endpoint is active)

When `models_base_url` or `models_list_url` is set, built-in xAI defaults are **not** loaded; the catalog comes from the remote list plus your `[model.*]` tables.

### Global `[models]` defaults

Apply the same scalar defaults or headers to every catalog entry:

```toml
[models]
default = "my-model"
temperature = 0.7
top_p = 0.95
max_completion_tokens = 8192
max_retries = 8
inference_idle_timeout_secs = 600
stream_tool_calls = true
extra_headers = { "X-Request-Tags" = "team=example,env=prod" }
web_search = "grok-4.20-multi-agent"
image_description = "grok-build"
session_summary = "grok-build"
allowed_models = ["grok-*", "my-model"]
hidden_models = ["internal-slug"]
disabled_models = ["retired-slug"]
```

- Per-model values always win over global defaults.
- Global `extra_headers` merge as a base; per-model keys override case-insensitively.
- `allowed_models` globs restrict what appears in the picker and what `-m` / `/model` may select for normal chat (internal/aux roles are exempt). `disabled_models` removes entries from the catalog entirely and wins over `hidden_models`.
- Identity fields (`model`, `base_url`, `api_key`, `context_window`, …) cannot be set as globals—only per model.
- `[models].agent_type` is deprecated; set `agent_type` on each `[model.X]` instead.

## Overriding built-in models

Specify only the fields to change:

```toml
[model.grok-build]
temperature = 0.5
api_key = "sk-custom"
```

Unspecified fields inherit from the default/prefetched entry (including the correct `base_url` for first-party models).

## Provider-neutral endpoint examples

Grok is **BYOK / BYOC friendly**: any OpenAI-compatible Chat Completions or Responses server, Anthropic Messages API, or local runtime can be a catalog entry. No proprietary connector is required beyond `base_url`, credentials, and `api_backend`.

### Anthropic Messages

```toml
[model.claude-opus]
model = "claude-opus-4-6"
base_url = "https://api.anthropic.com/v1"
name = "Claude Opus 4.6"
api_backend = "messages"
context_window = 200000
extra_headers = { "x-api-key" = "sk-ant-...", "anthropic-version" = "2023-06-01" }
```

### OpenAI Chat Completions

```toml
[model.gpt-4o]
model = "gpt-4o"
base_url = "https://api.openai.com/v1"
name = "GPT-4o"
env_key = "OPENAI_API_KEY"
```

### OpenAI Responses

```toml
[model.gpt-4o-responses]
model = "gpt-4o"
base_url = "https://api.openai.com/v1"
api_backend = "responses"
env_key = "OPENAI_API_KEY"
```

### Ollama (local)

```toml
[model.ollama-codellama]
model = "codellama"
base_url = "http://localhost:11434/v1"
name = "CodeLlama (Ollama)"
context_window = 16384
```

Run `ollama serve` and `ollama pull codellama` first. Many local servers need no API key.

### Any OpenAI-compatible gateway

```toml
[model.local-llama]
model = "llama-3.1-70b"
base_url = "http://localhost:8080/v1"
name = "Local Llama"
temperature = 0.8
context_window = 128000
```

### Together AI (and similar hosts)

```toml
[model.together-mixtral]
model = "mistralai/Mixtral-8x7B-Instruct-v0.1"
base_url = "https://api.together.xyz/v1"
name = "Mixtral 8x7B"
env_key = "TOGETHER_API_KEY"
```

## Custom models list endpoint

Point the whole client at a corporate gateway or self-hosted catalog instead of the public default.

### Environment

| Variable | Required | Description |
|----------|----------|-------------|
| `GROK_MODELS_BASE_URL` | Yes* | Inference base; list defaults to `{base}/models`. |
| `GROK_MODELS_LIST_URL` | No | Override list URL when it is not `{base}/models`. |
| `XAI_API_KEY` | Typical | Bearer key for list + inference when not using session auth. |

\*Either env or `[endpoints]` config.

### Config

```toml
[endpoints]
models_base_url = "https://api.acme.com/v1"
# models_list_url = "https://api.acme.com/v1/custom/models"  # optional

[model.company-grok]
model = "grok-build"
name = "Grok via proxy"
context_window = 128000
```

With a custom endpoint active:

- Built-in defaults are skipped; models come from the remote list + your overrides.
- Partial `[model.*]` entries inherit `base_url` from endpoint resolution when not specified.
- API-key auth is sufficient; `grok login` is not required for that path.

List URL resolution: `models_list_url` → `{models_base_url}/models` → `{proxy}/models`.

## Auxiliary model pins

Separate catalog keys for non-chat roles:

```toml
[models]
web_search = "my-search-model"
image_description = "my-vision-model"
session_summary = "my-summary-model"
prompt_suggestion = "my-suggest-model"
```

Environment counterparts exist for several pins (for example `GROK_WEB_SEARCH_MODEL`). If the pin is a custom id, define a matching `[model.*]` entry so credentials and `base_url` resolve.

Backend web search additionally needs:

```toml
[model.my-search-model]
model = "my-search-model"
supports_backend_search = true
```

## Everyday usage

```bash
# List catalog + auth banner
grok models

# Headless with explicit model
grok -p "Refactor this module" -m my-model

# Interactive switch
# /model my-model

# Persist default
# [models] default = "my-model"
```

## Enterprise sketch

```toml
[cli]
auto_update = false

[models]
default = "company-grok"

[model.company-grok]
model = "grok-build"
base_url = "https://grok-proxy.acme.com/"
name = "Grok Build (Proxy)"
context_window = 128000

[endpoints]
models_base_url = "https://grok-proxy.acme.com/v1"
deployment_key = "..."  # management / telemetry attribution

[features]
telemetry = false
```

## Troubleshooting

| Symptom | Checks |
|---------|--------|
| Model not found | `grok models`; typos in `[model.*]` keys vs `-m` id; `allowed_models` / `disabled_models` filters. |
| Connection / 401 | Curl `{base_url}/models` with the same Bearer or headers; confirm `api_key` / `env_key` / `extra_headers`. |
| Wrong protocol | Set `api_backend` to match the host (`messages` for Anthropic, `responses` where required). |
| Unexpected compaction | Set `context_window` explicitly on new custom models (default 200k if omitted). |
| `stream_tool_calls` errors | Disable globally or per model for providers that reject the field. |
| Custom endpoint empty catalog | Verify `GROK_MODELS_BASE_URL` / `models_list_url` and that remote fetch is not disabled by managed policy. |

Debug tracing:

```bash
RUST_LOG=debug GROK_LOG_FILE=/tmp/grok.log grok
# look for model_key, base_url, sampling, resolve_model_list
```

## Related pages

<CardGroup cols={2}>
  <Card title="Configure Grok" href="/configure-grok">
    config.toml precedence, feature flags, and `grok inspect`.
  </Card>
  <Card title="Authentication" href="/authentication">
    OAuth, device code, and `XAI_API_KEY` for CI.
  </Card>
  <Card title="Configuration reference" href="/configuration-reference">
    Schema-oriented `[models]`, `[model.*]`, and `[endpoints]` keys.
  </Card>
  <Card title="CLI reference" href="/cli-reference">
    Top-level flags including `-m` and `grok models`.
  </Card>
  <Card title="Slash commands" href="/slash-commands">
    `/model` and other interactive commands.
  </Card>
  <Card title="Headless mode" href="/headless-mode">
    Non-interactive runs with explicit model and output formats.
  </Card>
</CardGroup>

## Next

<CardGroup cols={2}>
  <Card title="MCP servers" href="/mcp-servers">
    Register stdio/HTTP MCP servers alongside custom inference endpoints.
  </Card>
  <Card title="Troubleshooting" href="/troubleshooting">
    Terminal, auth recovery, and diagnostics probes.
  </Card>
</CardGroup>

<code-citation><citation-definition file="crates/codegen/xai-grok-pager/docs/user-guide/11-custom-models.md" start="1" end="382" repo="https://github.com/xai-org/grok-build" /></code-citation>
<code-citation><citation-definition file="crates/codegen/xai-grok-models/src/lib.rs" start="1" end="70" repo="https://github.com/xai-org/grok-build" /></code-citation>
<code-citation><citation-definition file="crates/codegen/xai-grok-models/default_models.json" start="1" end="18" repo="https://github.com/xai-org/grok-build" /></code-citation>
<code-citation><citation-definition file="crates/codegen/xai-grok-shell/src/cli_models.rs" start="1" end="90" repo="https://github.com/xai-org/grok-build" /></code-citation>
<code-citation><citation-definition file="crates/codegen/xai-grok-pager/src/models.rs" start="1" end="40" repo="https://github.com/xai-org/grok-build" /></code-citation>
<code-citation><citation-definition file="crates/codegen/xai-grok-shell/src/agent/config.rs" start="140" end="161" repo="https://github.com/xai-org/grok-build" /></code-citation>
<code-citation><citation-definition file="crates/codegen/xai-grok-shell/src/agent/config.rs" start="273" end="538" repo="https://github.com/xai-org/grok-build" /></code-citation>
<code-citation><citation-definition file="crates/codegen/xai-grok-shell/src/agent/config.rs" start="973" end="1043" repo="https://github.com/xai-org/grok-build" /></code-citation>
<code-citation><citation-definition file="crates/codegen/xai-grok-shell/src/agent/config.rs" start="3106" end="3226" repo="https://github.com/xai-org/grok-build" /></code-citation>
<code-citation><citation-definition file="crates/codegen/xai-grok-shell/src/agent/config.rs" start="3413" end="3529" repo="https://github.com/xai-org/grok-build" /></code-citation>
<code-citation><citation-definition file="crates/codegen/xai-grok-shell/src/agent/config.rs" start="4263" end="4324" repo="https://github.com/xai-org/grok-build" /></code-citation>
<code-citation><citation-definition file="crates/codegen/xai-grok-sampling-types/src/types.rs" start="1010" end="1021" repo="https://github.com/xai-org/grok-build" /></code-citation>
<code-citation><citation-definition file="crates/codegen/xai-grok-pager/src/app/cli.rs" start="235" end="244" repo="https://github.com/xai-org/grok-build" /></code-citation>
<code-citation><citation-definition file="crates/codegen/xai-grok-pager/src/slash/commands/model.rs" start="1" end="99" repo="https://github.com/xai-org/grok-build" /></code-citation>

---

## 10. MCP servers

> Register MCP servers in config.toml (stdio and HTTP), timeouts and env, enable/disable, OAuth-related MCP credentials, and grok mcp management commands.

- Page Markdown: https://grok-wiki.com/public/docs/xai-org-grok-build-90205de50458/pages/10-mcp-servers.md
- Generated: 2026-07-16T01:20:22.540Z

### Source Files

- `crates/codegen/xai-grok-pager/docs/user-guide/07-mcp-servers.md`
- `crates/codegen/xai-grok-mcp/src/servers.rs`
- `crates/codegen/xai-grok-mcp/src/lib.rs`
- `crates/codegen/xai-grok-mcp/src/wire.rs`
- `crates/codegen/xai-grok-shell/src/util/config/mcp.rs`
- `crates/codegen/xai-grok-pager/src/mcp_cmd.rs`
- `crates/codegen/xai-grok-shell/src/mcp_doctor.rs`

---
title: "MCP servers"
description: "Register MCP servers in config.toml (stdio and HTTP), timeouts and env, enable/disable, OAuth-related MCP credentials, and grok mcp management commands."
---

Grok Build loads MCP (Model Context Protocol) servers from `config.toml` and vendor-compatible files, spawns or connects them at session start, and exposes their tools to the model as namespaced integration tools via `search_tool` and `use_tool`. Native configuration lives under `[mcp_servers.<name>]` in `~/.grok/config.toml` and optional project `.grok/config.toml` files. CLI management is `grok mcp` (`list`, `add`, `remove`, `doctor`). OAuth tokens for remote MCP servers are stored separately from xAI login credentials in `$GROK_HOME/mcp_credentials.json` (default `~/.grok/mcp_credentials.json`).

## Architecture

```mermaid
flowchart TB
  subgraph sources [Config sources]
    UT["~/.grok/config.toml<br/>[mcp_servers]"]
    PT[".grok/config.toml<br/>project chain"]
    CL["~/.claude.json"]
    CU[".cursor/mcp.json"]
    MJ[".mcp.json"]
    PL["plugins"]
  end

  subgraph shell [Shell load and merge]
    MERGE["load_mcp_servers / merge<br/>name winner by priority"]
    DIS["disabled_mcp_servers<br/>enabled = false"]
  end

  subgraph runtime [MCP runtime xai-grok-mcp]
    STDIO["stdio TokioChildProcess"]
    HTTP["HTTP / SSE streamable"]
    OAUTH["OAuth + mcp_credentials.json"]
    CLIENT["McpClient<br/>handshake + tools"]
  end

  subgraph model [Model surface]
    ST["search_tool"]
    UT2["use_tool<br/>server__tool"]
  end

  UT --> MERGE
  PT --> MERGE
  CL --> MERGE
  CU --> MERGE
  MJ --> MERGE
  PL --> MERGE
  MERGE --> DIS
  DIS --> CLIENT
  CLIENT --> STDIO
  CLIENT --> HTTP
  HTTP --> OAUTH
  CLIENT --> ST
  CLIENT --> UT2
```

Merge priority for name conflicts (higher wins; lower sources use insert-if-absent):

| Priority | Source | Notes |
|----------|--------|--------|
| Highest | `config.toml` chain | User `~/.grok/config.toml`, then project `.grok/config.toml` from repo root toward cwd (closest wins) |
| | `~/.claude.json` | Gated by `[compat.claude] mcps`; skipped after Claude import marker |
| | Cursor `.cursor/mcp.json` | Project then global; gated by `[compat.cursor] mcps` |
| Lowest | `.mcp.json` | Walk cwd → git root; skipped after Claude import marker |

Same-name project entries **replace** user entries entirely (no field-level merge). Plugins can also contribute MCP servers; `grok mcp doctor` reports them as `plugin: <name>` sources.

## Prerequisites

- Grok installed and on `PATH` (`grok --version`)
- For remote OAuth servers: a browser-capable session (interactive TUI or doctor with interactive OAuth)
- For stdio servers: the command binary on `PATH` (or an absolute path)
- Project-scoped MCP from working-tree configs may require folder trust before spawn

## Register a server

### stdio (local process)

```toml
[mcp_servers.filesystem]
command = "npx"
args = ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/dir"]
env = { API_KEY = "${MY_API_KEY}" }
enabled = true
startup_timeout_sec = 30
tool_timeout_sec = 6000
tool_timeouts = { slow_op = 120 }
```

| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `command` | string | required | Executable or bare launcher (`npx`, `uvx`, …) |
| `args` | string[] | `[]` | Arguments after the command |
| `env` | map | optional | Extra environment for the child process |
| `cwd` | string | optional | Working directory (MCP JSON supports it; ACP stdio may not forward it) |
| `enabled` | bool | `true` | When false, server is omitted from the live set |
| `startup_timeout_sec` | u64 | global default **30** | Handshake timeout (seconds) |
| `tool_timeout_sec` | u64 | **6000** | Default per-tool call timeout (seconds) |
| `tool_timeouts` | map | optional | Per bare tool name → seconds |
| `expose_image_base64` | bool | `false` | Keep raw base64 in tool-result text for path-based forwarding |

String fields (`command`, `args`, `env` values, `url`, `headers` values) expand `${VAR}` / `${VAR:-default}` at load time.

On Windows, bare commands such as `npx` resolve via `PATH` / `PATHEXT` (including `.cmd` shims) before spawn.

### HTTP / SSE (remote)

```toml
[mcp_servers.sentry]
url = "https://mcp.sentry.dev/mcp"
enabled = true
headers = { "Authorization" = "Bearer ${SENTRY_TOKEN}" }

# Or inject a bearer token from an env var without putting it in headers:
# bearer_token_env_var = "SENTRY_TOKEN"
```

| Field | Type | Description |
|-------|------|-------------|
| `url` | string | Remote MCP endpoint (`http://` or `https://`) |
| `type` | string | Set to `"sse"` for SSE; also inferred when URL ends with `/sse` |
| `headers` | map | Static HTTP headers (values support `${VAR}`) |
| `bearer_token_env_var` | string | Env var whose value becomes `Authorization: Bearer …` |
| `oauth_client_id` | string | BYO OAuth client ID (non-DCR providers) |
| `oauth_client_secret_env_var` | string | Env var holding client secret |
| `oauth_scopes` | string[] | Scopes requested during authorization |
| `oauth` | table | Alternate OAuth block: `client_id`, `client_secret_env_var`, `scopes`, `callback_port` |

Prefer native `url` over wrapping a remote endpoint in a stdio proxy (`npx mcp-remote …`). Grok handles HTTP/SSE and OAuth directly.

Session-id placeholder in headers (when supported by the host wiring):

```toml
headers = { "x-mcp-session-id" = "{{session_id}}" }
```

### Global MCP knobs

```toml
[mcp]
startup_timeout_sec = 60   # global handshake default (seconds)
max_output_bytes = 40000   # inline tool-result cap (bytes)
```

**Startup timeout precedence** (global fallback; per-server `startup_timeout_sec` still wins):

1. `requirements.toml` `[mcp].startup_timeout_sec`
2. Env: `MCP_TIMEOUT` (milliseconds, rounded up) then `GROK_MCP_STARTUP_TIMEOUT_SECS` (seconds)
3. Effective `config.toml` `[mcp].startup_timeout_sec`
4. Remote settings
5. Built-in **30** seconds

**Inline MCP output cap** (default **20_000** bytes; full payload may spill under the session `mcp/` folder):

1. `requirements.toml` `[mcp] max_output_bytes`
2. Env: `GROK_MAX_MCP_OUTPUT_BYTES` then `MAX_MCP_OUTPUT_BYTES` (Grok-native wins if both set)
3. Effective / project `config.toml` `[mcp] max_output_bytes` (cwd-aware path when applicable)
4. Remote settings
5. Default 20_000

Related feature flags (defaults true unless requirements/managed override):

| Config / env | Effect |
|--------------|--------|
| `[features] mcp_liveness_watchers` / `GROK_MCP_LIVENESS_WATCHERS` | Liveness watchers |
| `[features] mcp_auto_restart` / `GROK_MCP_AUTO_RESTART` | Auto-restart failed servers |
| `[features] mcp_push_server_status` / `GROK_MCP_PUSH_SERVER_STATUS` | Push server status to UI |
| `[features] mcp_recursive_config_watch` / `GROK_MCP_RECURSIVE_CONFIG_WATCH` | Recursive config file watch |

## CLI: `grok mcp`

### List

```bash
grok mcp list
grok mcp list --json
```

Lists servers from user and project scopes (nearest project definition wins). Human output marks `(project)` and `(disabled)`.

### Add

```bash
# stdio — everything after -- is the server command (flags like -y reach the server)
grok mcp add filesystem -- npx -y @modelcontextprotocol/server-filesystem /path/to/dir

# stdio with env (-e / --env is one KEY=value per flag)
grok mcp add postgres -e DATABASE_URL=postgres://localhost/mydb -- npx -y @modelcontextprotocol/server-postgres

# HTTP / SSE
grok mcp add --transport http sentry https://mcp.sentry.dev/mcp
grok mcp add --transport http api https://mcp.example.com/mcp --header "Authorization: Bearer TOKEN"
grok mcp add --transport sse linear https://mcp.linear.app/sse

# Project config (./.grok/config.toml)
grok mcp add --scope project github -- npx -y @modelcontextprotocol/server-github
```

| Flag | Description |
|------|-------------|
| `--transport` / `-t` | `stdio` (default), `http`, or `sse` |
| `--scope` / `-s` | `user` (default → `~/.grok/config.toml`) or `project` (→ `./.grok/config.toml`) |
| `-e` / `--env` | Repeatable `KEY=value` (stdio only) |
| `-H` / `--header` | Repeatable `Name: value` (HTTP/SSE only) |

Constraints:

- Server names: letters, numbers, hyphens, underscores only
- Default transport is stdio; a URL-looking command without `--transport http` stays stdio and emits a warning
- `--env` is not greedy (`-e A=1 -e B=2`, not multi-value `--env`)
- Newly added servers start enabled and are cleared from `disabled_mcp_servers` if present

### Remove

```bash
grok mcp remove github
grok mcp remove github --scope user
grok mcp remove github --scope project
```

- Exit **0** when the entry is removed
- Exit **1** if the name is missing, or defined in **both** scopes without `--scope`
- Also removes matching OAuth credentials for that server name from the global credential store
- If a definition remains in another scope/ancestor, stderr notes that the name still resolves

### Doctor

```bash
grok mcp doctor
grok mcp doctor github
grok mcp doctor --json
```

Discovers servers from the same merge path as a session (including plugins and, when eligible, managed sources), then for each server:

1. Command exists (stdio) or URL present (remote)
2. Start / connect
3. Handshake
4. `tools/list` (fails the check if zero tools)

Exit **1** when any checked server fails or a named server is not found. Use doctor to catch cold-start `npx`/`uvx` timeout issues before a session.

## Project-scoped servers

```
my-project/
  .grok/
    config.toml    # [mcp_servers.*] shared with the team
  src/
```

| Location | Scope | Override strength |
|----------|-------|-------------------|
| `~/.grok/config.toml` | User | Lowest |
| `<repo-root>/.grok/config.toml` | Project | Medium |
| `<cwd>/.grok/config.toml` | Project (nearest) | Highest |

Project files contribute `[mcp_servers]`, `[plugins]`, and `[permission]` (most other sections stay user-global). Prefer `${VAR}` secrets in committed project configs.

## Enable, disable, and tool filters

Two disable paths:

1. Per-server `enabled = false` under `[mcp_servers.<name>]`
2. Top-level list:

```toml
disabled_mcp_servers = ["filesystem", "github"]
```

Runtime toggles from the TUI `/mcps` modal write `disabled_mcp_servers` (and may also flip `enabled` on local TOML servers). Disabled tool names persist under:

```toml
[disabled_mcp_tools]
github = ["create_issue", "delete_repo"]
```

### Runtime modal (`/mcps`)

- Open with `/mcps`, or via the plugins/MCP UI tab (`Ctrl+L` on non–VS Code family terminals)
- Space: enable/disable
- Expand: list tools
- `r`: refresh after editing config
- `i`: authenticate OAuth servers
- `a` / `x`: add / remove

## Tool naming and model tools

Qualified tool names use the delimiter `__`:

| Server | Tool | Model-facing name |
|--------|------|-------------------|
| `filesystem` | `read_file` | `filesystem__read_file` |
| `github` | `create_issue` | `github__create_issue` |

Built-in meta tools:

| Tool | Role |
|------|------|
| `search_tool` | Keyword/schema discovery across enabled MCP servers (status may be `"partial"` while servers still connect) |
| `use_tool` | Invoke a discovered tool by fully qualified name; arguments go in `tool_input` |

Permissions treat the **underlying** MCP tool as the effective tool for hooks and allow/deny rules when the call goes through `use_tool`.

## OAuth and credentials

Remote MCP OAuth is separate from xAI `auth.json` login:

| Item | Location / behavior |
|------|---------------------|
| Credential store | `$GROK_HOME/mcp_credentials.json` (keys `"{server_name}:{server_url}"`) |
| Lock file | `mcp_credentials.json.lock` (cross-process safe insert) |
| Flow | Prefer refresh; else browser consent on loopback `http://127.0.0.1:<port>/callback` |
| BYO client | `oauth_client_id` / `oauth.*` plus optional `callback_port` |
| Cleanup | `grok mcp remove` deletes credentials for that server name |

```toml
[mcp_servers.linear]
url = "https://mcp.linear.app/mcp"
enabled = true

# Providers without Dynamic Client Registration:
# oauth_client_id = "…"
# oauth_client_secret_env_var = "LINEAR_MCP_CLIENT_SECRET"
# oauth_scopes = ["read", "write"]
```

Authenticate from `/mcps` (`i`) or during an interactive connect. After editing `config.toml`, refresh the modal with `r`.

## Vendor compatibility

| Source | Format | Gate |
|--------|--------|------|
| Grok TOML | `[mcp_servers]` | Always |
| Claude | `mcpServers` in `~/.claude.json` (+ per-project) | `[compat.claude] mcps` / `GROK_CLAUDE_MCPS_ENABLED` |
| Cursor | `mcpServers` in `.cursor/mcp.json` | `[compat.cursor] mcps` / `GROK_CURSOR_MCPS_ENABLED` |
| MCP JSON | project `.mcp.json` | Loaded until Claude import is marked |

Inspect resolved load with:

```bash
grok inspect
grok inspect --json
```

## Example configurations

<Tabs>
  <Tab title="Hosted HTTP + OAuth">

```toml
[mcp_servers.linear]
url = "https://mcp.linear.app/mcp"
enabled = true

[mcp_servers.sentry]
url = "https://mcp.sentry.dev/mcp"
enabled = true
```

  </Tab>
  <Tab title="Static bearer">

```toml
[mcp_servers.internal-tools]
url = "https://mcp.internal.example.com/mcp"
enabled = true
headers = { "Authorization" = "Bearer ${INTERNAL_MCP_TOKEN}" }
```

  </Tab>
  <Tab title="Local stdio">

```toml
[mcp_servers.filesystem]
command = "npx"
args = ["-y", "@modelcontextprotocol/server-filesystem", "/allowed/dir"]
startup_timeout_sec = 60

[mcp_servers.postgres]
command = "npx"
args = ["-y", "@modelcontextprotocol/server-postgres", "postgresql://user:pass@localhost/db"]
env = { PGSSLMODE = "disable" }
```

  </Tab>
</Tabs>

Confirm current package names and OAuth endpoints with each provider before shipping project config.

## Verification

```bash
# 1. Config parses and lists
grok mcp list

# 2. Connectivity and tools/list
grok mcp doctor --json

# 3. Resolved multi-source view
grok inspect

# 4. Manual stdio smoke (outside Grok)
npx -y @modelcontextprotocol/server-filesystem /path
```

Expected doctor outcome for a healthy server: command/URL check pass, start under the startup timeout, handshake OK, non-zero tool count.

## Troubleshooting

| Symptom | What to check |
|---------|----------------|
| Server missing from session | `enabled`, `disabled_mcp_servers`, project override shadowing user, folder trust, compat gates |
| Startup timeout | Raise `startup_timeout_sec` or global `MCP_TIMEOUT` / `GROK_MCP_STARTUP_TIMEOUT_SECS`; cold `npx`/`uvx` often needs >30s |
| Spawn / handshake fail (stdio) | `~/.grok/logs/mcp/<server>.stderr.log` (truncated each launch) |
| OAuth loop / unauthorized | Re-auth in `/mcps`; inspect `mcp_credentials.json`; verify `bearer_token_env_var` is set |
| Empty tool list | Server config or upstream connector; doctor reports `0 tools discovered` |
| Large results truncated | Raise `[mcp] max_output_bytes` or `GROK_MAX_MCP_OUTPUT_BYTES` |
| Remove fails with multi-scope | Pass `--scope user` or `--scope project` |
| Debug log stream | `RUST_LOG=debug GROK_LOG_FILE=/tmp/grok.log grok` and filter lines containing `mcp` |

```bash
tail -f ~/.grok/logs/mcp/filesystem.stderr.log
```

## Related pages

<CardGroup>
  <Card title="Configure Grok" href="/configure-grok">
    Config precedence, feature flags, and `grok inspect`.
  </Card>
  <Card title="Configuration reference" href="/configuration-reference">
    Schema keys for `[mcp_servers]`, `[mcp]`, `[compat]`, and env counterparts.
  </Card>
  <Card title="Permissions and safety" href="/permissions-and-safety">
    How MCP / `use_tool` calls enter the allow–deny–ask pipeline.
  </Card>
  <Card title="Plugins" href="/plugins">
    Plugins that bundle additional MCP servers.
  </Card>
  <Card title="CLI reference" href="/cli-reference">
    Full `grok mcp` surface and shared runtime flags.
  </Card>
  <Card title="Troubleshooting" href="/troubleshooting">
    MCP startup failures and broader diagnostics.
  </Card>
</CardGroup>

---

## 11. Skills

> SKILL.md package layout, discovery paths (.grok, .agents, .claude, .cursor), priority and ignore rules, slash-command surface, and config toggles for vendor skill trees.

- Page Markdown: https://grok-wiki.com/public/docs/xai-org-grok-build-90205de50458/pages/11-skills.md
- Generated: 2026-07-16T01:35:33.719Z

### Source Files

- `crates/codegen/xai-grok-pager/docs/user-guide/08-skills.md`
- `crates/codegen/xai-grok-tools/src/implementations/skills`
- `crates/codegen/xai-grok-shell/skills`
- `crates/codegen/xai-grok-shell/src/extensions/mod.rs`
- `crates/codegen/xai-hooks-plugins-types/src`
- `crates/codegen/xai-grok-pager/docs/user-guide/05-configuration.md`

---
title: "Skills"
description: "SKILL.md package layout, discovery paths (.grok, .agents, .claude, .cursor), priority and ignore rules, slash-command surface, and config toggles for vendor skill trees."
---

Skills are reusable prompt packages: a directory with a `SKILL.md` (YAML frontmatter plus markdown body) that Grok discovers from `.grok`, `.agents`, `.claude`, and `.cursor` trees, optional `[skills].paths`, bundled/server injections, and plugins. Discovery is owned by `list_skills` / `list_skills_with_plugins` in the agent prompt layer, with parsing and walk limits in the tools skills implementation; each skill is user-invocable as a slash command (when `user-invocable` is true) and/or model-invocable via the Skill tool unless `disable-model-invocation` is set.

## What a skill is

A skill packages task-specific instructions that are too specialized for `AGENTS.md` and too long to retype each session. Grok loads skill **metadata** (name, description, flags) for listing and matching, and injects the full markdown body only when you run the slash command or the model invokes the Skill tool.

Typical layout:

```text
~/.grok/skills/
  commit/
    SKILL.md
    scripts/          # optional helpers
    references/       # optional supporting docs
  review-pr/
    SKILL.md
```

Flat `*.md` files under a `commands/` directory next to a config root (for example `.claude/commands/frontend.md`) become user-invocable slash commands whose name is the filename stem. That matches Claude Code’s legacy custom-command layout and is discovered alongside `skills/` trees.

**Limits (parser):** skill names up to 64 characters after normalization; description capped at 1024 characters; frontmatter read capped at 4096 bytes; recursive `SKILL.md` walk depth capped at 5.

## Package format: `SKILL.md`

```markdown
---
name: commit
description: >
  Create well-formatted git commits following conventional commit standards.
  Use when the user wants to commit changes or asks for /commit.
when-to-use: commit my changes, write a commit message
argument-hint: commit message
user-invocable: true
disable-model-invocation: false
allowed-tools: [run_terminal_command]
metadata:
  short-description: "Conventional git commits"
  author: team
---

# Git Commit Skill

1. Run `git diff --staged`
2. Summarize what changed and why
3. Commit with a conventional message
```

### Core frontmatter

| Field | Role |
|-------|------|
| `name` | Slash-command identity. Lowercase letters, digits, hyphens; normalized from spaces/underscores/dots. If omitted or invalid after normalize, the parent directory name is used. |
| `description` | Primary signal for automatic / Skill-tool matching. If absent, Grok derives text from the first body heading or prose paragraph. |

### Optional frontmatter

| Field | Role |
|-------|------|
| `when-to-use` | Extra trigger phrases (also accepts `when_to_use`). |
| `allowed-tools` | YAML list or comma/space-separated string (paren groups kept whole, e.g. `Bash(git diff:*)`). |
| `argument-hint` | Autocomplete hint for slash args. |
| `user-invocable` | Default `true`. Set `false` to hide from slash / user skill menus. |
| `disable-model-invocation` | Default `false`. When `true`, only explicit slash invocation runs the skill. |
| `model` / `effort` | Optional model and reasoning-effort overrides for the skill turn. |
| `paths` | Gitignore-style globs; when set, the skill stays out of the listing until a matching file is touched. |
| `license`, `compatibility`, `metadata` | Display/metadata; `metadata.author` and `metadata.short-description` are promoted for UI. |

Frontmatter keys for multi-word fields use **kebab-case**. Boolean fields accept YAML `true` or the string `"true"`.

### Body variable expansion

When a skill is expanded, the body may use:

| Token | Alias | Meaning |
|-------|-------|---------|
| `$ARGUMENTS` | — | Full args string |
| `$ARGUMENTS[N]` / `$N` | — | Nth whitespace-split arg (0-based) |
| `${SKILL_DIR}` | `${CLAUDE_SKILL_DIR}` | Directory containing `SKILL.md` |
| `${SESSION_ID}` | `${CLAUDE_SESSION_ID}` | Current session id |
| `${GROK_PLUGIN_ROOT}` | `${CLAUDE_PLUGIN_ROOT}` | Plugin root (plugin skills) |
| `${GROK_PLUGIN_DATA}` | `${CLAUDE_PLUGIN_DATA}` | Plugin data dir (plugin skills) |

If the body contains no argument tokens, arguments are appended as a traditional `**ARGUMENTS:** …` suffix. Path/session tokens expand without suppressing that suffix.

Injected skill content is wrapped in a stable envelope for the model, for example:

```text
<skill name="commit" description="…" path="…">
…body…
</skill>
```

Slash expansion can also wrap blocks in `<skill_information>` with a `<skills_referenced>` index.

## Discovery paths

At startup (and when skill roots change on disk), Grok collects config directories that may contain `skills/` and `commands/`, then walks for `SKILL.md` and flat command markdown.

### Config directory names

Always scanned:

- `.grok`
- `.agents`

Vendor trees, gated by harness compatibility (default **on**):

- `.claude` when `[compat.claude] skills = true`
- `.cursor` when `[compat.cursor] skills = true`

Codex skill cells exist in the compat registry but are **reserved/inert** — they do not enable `.codex` skill discovery.

### Where those directories are found

| Layer | Examples | `SkillScope` |
|-------|----------|--------------|
| Working tree walk | From CWD up to git root: `<dir>/.grok`, `<dir>/.agents`, optional `.claude` / `.cursor` | Local if parent is CWD; otherwise Repo under the git root |
| User home | `~/.grok`, `~/.agents`, `~/.claude`, `~/.cursor` (vendor gated) | User |
| Optional workspace-user dir | Same config names under the launcher-provided workspace user root | User-adjacent |
| `[skills].paths` | Explicit files or recursive skill dirs (`~` expanded) | Config path source |
| Server | Launcher-injected `server_skill_dirs` | Server |
| Bundled | Launcher `bundled_skill_dirs` + `~/.grok/bundled` | Bundled |
| Plugins | Skills from trusted/enabled plugins | Plugin (qualified names) |

Under each config root, Grok looks for a `skills/` subdirectory and walks nested skill packages (max depth 5). A config path that *is* a skill directory (contains `SKILL.md` at its root) is accepted as a single skill. The old Cursor-only `skills-cursor/` layout is **not** scanned.

### Priority and name collisions

Bare-name resolution prefers higher priority scopes:

**Local → Repo → User → Server → Bundled → Plugin (lowest for bare names)**

- Same skill name: higher-priority location wins.
- Plugin skills of the same bare name do **not** replace a native skill; they remain available under a **qualified** name (`plugin-name:skill-name`).
- Scope-qualified forms used in menus and the Skill tool: `local:…`, `repo:…`, `user:…`, or `plugin-name:…`.

Collision handling among same-scope files is deterministic (walk order is lexicographic; first-seen-wins on path identity).

### Gitignore vs config ignore

Skill and command discovery **does not consult `.gitignore`**. Roots under `.grok/`, `.agents/`, `.claude/`, and `.cursor/` load when present on disk even if the repo ignores them — teams often gitignore vendor config while still expecting `/frontend`-style project commands to work.

To hide a skill, use config:

```toml
[skills]
ignore = ["~/my-team-skills/wip"]   # path prefixes; skill hidden entirely
disabled = ["wip-skill"]            # name stays listed but inactive
```

| Control | Listed? | In system prompt / Skill tool? |
|---------|---------|--------------------------------|
| `ignore` | No | No |
| `disabled` | Yes (tagged disabled) | No |

`paths` and `ignore` take filesystem paths with `~` expansion. `disabled` takes **skill names**.

### Vendor default denylist

Regardless of compat toggles, skills **physically under** `/.cursor/` or `/.claude/` whose names match known vendor builtins are dropped. Examples:

- Cursor: `shell`, `canvas`, `statusline`, `create-skill`, `babysit`, …
- Claude: `pdf`, `docx`, `xlsx`, `pptx`, `skill-creator`

A user skill of the same name under `~/.grok/skills/` is **not** dropped.

### Mid-session discovery

When files are accessed during a turn, Grok can walk upward from those paths toward CWD and pick up newly visible `.grok` / `.agents` (and `.claude` if enabled) skill roots. **`.cursor` is not scanned on this dynamic path** by design; the Cursor skills toggle applies to the startup directory list only.

## Configuration

### `[skills]` in `config.toml`

```toml
[skills]
paths = ["~/my-team-skills"]
ignore = ["~/my-team-skills/wip"]
disabled = ["wip-skill"]
```

Each `paths` entry is a `SKILL.md` file or a directory walked for skills. These appear in `grok inspect` as source `config`.

### Vendor skill trees: `[compat.*]`

```toml
[compat.cursor]
skills = true     # ~/.cursor/skills/ and project .cursor/skills/

[compat.claude]
skills = true     # ~/.claude/skills/ and project .claude/skills/
```

Resolution order for each compat cell: **environment variable → `config.toml` → default (on)**.

| Env var | Effect |
|---------|--------|
| `GROK_CURSOR_SKILLS_ENABLED` | `false` / `0` stops scanning Cursor skill dirs |
| `GROK_CLAUDE_SKILLS_ENABLED` | `false` / `0` stops scanning Claude skill dirs |

`.grok` and `.agents` cannot be turned off via these cells.

Managed / remote settings may also supply `cursor_skills_enabled` and `claude_skills_enabled`; see configuration precedence on the configure and reference pages.

## Slash-command and model surfaces

### User invocation

- Type `/` in the TUI: builtins and every **user-invocable** skill appear; filter as you type.
- Run by bare name: `/commit`, `/review-pr`.
- Pass arguments after the name: `/commit fix the build`.
- Disambiguate collisions with qualified names: `/local:commit`, `/user:commit`, `/my-plugin:deploy`.

`/create-skill` (bundled) interactively scaffolds a project (`<repo>/.grok/skills/<name>/`) or user (`~/.grok/skills/<name>/`) package and reloads when files change on disk.

### Model invocation

The Skill tool takes:

```json
{ "skill": "commit", "args": "optional string" }
```

`skill` may be bare or fully qualified (`user:commit`). Skills with `disable-model-invocation: true` or `enabled: false` are excluded from model invocation while remaining slash-runnable if user-invocable and enabled.

Automatic invocation depends on prompt match against `description` and `when-to-use`. Write specific trigger language in those fields.

### Bundled skills

Grok ships platform skills (for example `create-skill`, `help`, `check-work`) extracted into the user/bundled skill tree on startup. `grok inspect` labels them `bundled`. A same-named **local or repo** skill overrides the bundled bare name; a plugin twin remains under its qualified form.

### Plugin skills

Plugins may bundle skills. After install and trust, they list with source `plugin: <name>` and resolve as `plugin-name:skill-name`. See the plugins page for marketplace install, `plugin.json`, and `GROK_PLUGIN_ROOT` / `GROK_PLUGIN_DATA`.

## Inspect and verify

```bash
grok inspect          # human summary, Skills section with source tags
grok inspect --json   # name, description, source path, userInvocable, …
```

Sources you may see: `project`, `user`, `bundled`, `config`, `server`, `plugin: <name>`. Disabled skills (config or vendor surface off) are tagged `[disabled]` in the human report. The inspect path honors the same `[skills]` and compat rules as a live session.

## Operational notes

1. **Descriptions drive auto-invoke** — name the task and slash form (`Use when … or /commit`).
2. **One workflow per skill** — prefer `deploy` and `rollback` over a combined mega-skill.
3. **Commit project skills** under `<repo>/.grok/skills/` for team sharing; keep personal packs in `~/.grok/skills/`.
4. **Do not rely on `.gitignore` to hide skills** — use `[skills].ignore` or `disabled`.
5. **Vendor noise** — turn off `compat.cursor.skills` / `compat.claude.skills` (or the env vars) if foreign trees pollute the slash menu; vendor builtins are already denylisted by path.
6. **Test with `/name`** before depending on automatic matching.
7. Skill packs are **portable files and directories** (or catalog/plugin sources), not a model-provider dependency — the same tree works with Grok-native, Claude-compat, or Cursor-compat roots depending on which roots you enable.

## Related pages

<CardGroup cols={2}>
  <Card title="Plugins" href="/plugins">
    Install skill bundles, marketplace trust, plugin roots and data dirs.
  </Card>
  <Card title="Slash commands" href="/slash-commands">
    Interactive `/` menu, builtins vs skill-provided commands.
  </Card>
  <Card title="Configure Grok" href="/configure-grok">
    config.toml precedence, feature flags, inspect dumps.
  </Card>
  <Card title="Configuration reference" href="/configuration-reference">
    Schema for `[skills]`, `[compat]`, and related keys.
  </Card>
  <Card title="Project rules and memory" href="/project-rules-and-memory">
    AGENTS.md vs skills: always-on rules versus on-demand packages.
  </Card>
  <Card title="Subagents and personas" href="/subagents">
    Child agents that can preload or constrain skill sets.
  </Card>
</CardGroup>

## Next

<CardGroup cols={2}>
  <Card title="Plugins" href="/plugins">
    Ship and consume multi-skill packages.
  </Card>
  <Card title="Slash commands" href="/slash-commands">
    How skill names show up next to pager and shell builtins.
  </Card>
</CardGroup>

---

## 12. Plugins

> Install and trust plugins that bundle skills, commands, agents, hooks, MCP, and LSP; marketplace sources; plugin.json conventions; GROK_PLUGIN_ROOT and GROK_PLUGIN_DATA.

- Page Markdown: https://grok-wiki.com/public/docs/xai-org-grok-build-90205de50458/pages/12-plugins.md
- Generated: 2026-07-16T01:22:15.923Z

### Source Files

- `crates/codegen/xai-grok-pager/docs/user-guide/09-plugins.md`
- `crates/codegen/xai-grok-plugin-marketplace/src/lib.rs`
- `crates/codegen/xai-grok-plugin-marketplace/src/installer.rs`
- `crates/codegen/xai-grok-plugin-marketplace/src/index.rs`
- `crates/codegen/xai-grok-pager/src/plugin_cmd.rs`
- `crates/codegen/xai-grok-shell/src/plugin.rs`

---
title: "Plugins"
description: "Install and trust plugins that bundle skills, commands, agents, hooks, MCP, and LSP; marketplace sources; plugin.json conventions; GROK_PLUGIN_ROOT and GROK_PLUGIN_DATA."
---

Plugins are installable directories that package skills, slash commands, agents, hooks, MCP servers, and LSP servers as one unit. Grok discovers them from CLI overrides, project trees, user home, managed installs under `~/.grok/installed-plugins/`, and `[plugins].paths`; loads an optional `plugin.json` (or convention layout); then applies separate **enable** and **trust** gates before components enter a session.

## What a plugin contains

A plugin root is a directory with any combination of:

| Component | Convention path | Manifest override |
|-----------|-----------------|-------------------|
| Skills | `skills/` | `skills` path or list of paths |
| Slash commands | `commands/` | `commands` |
| Agents | `agents/` | `agents` |
| Hooks | `hooks/hooks.json` | `hooks` (path string or inline JSON) |
| MCP servers | `.mcp.json` | `mcpServers` (path or inline) |
| LSP servers | `.lsp.json` | `lspServers` (path or inline) |

`plugin.json` is optional. Without it, Grok still treats the directory as a plugin if at least one convention component exists, and derives the name from the directory name (sanitized to kebab-case).

:::files
my-plugin/
├── plugin.json                 # optional; also .grok-plugin/ or .claude-plugin/
├── skills/
│   └── deploy/SKILL.md
├── commands/
├── agents/
├── hooks/hooks.json
├── .mcp.json
└── .lsp.json
:::

## plugin.json conventions

Manifest search order inside a plugin root:

1. `plugin.json`
2. `.grok-plugin/plugin.json`
3. `.claude-plugin/plugin.json`

Unknown fields are ignored (forward-compatible). Field names use camelCase in JSON (`mcpServers`, `lspServers`).

| Field | Type | Notes |
|-------|------|--------|
| `name` | string, required | Kebab-case: 1–64 chars, lowercase alphanumerics and hyphens, no leading/trailing `-` |
| `version` | string | Optional semver-style label |
| `description`, `author`, `homepage`, `repository`, `license`, `keywords` | metadata | Optional |
| `skills`, `commands`, `agents` | string or string[] | Extra dirs relative to plugin root; paths that escape the root are skipped |
| `hooks`, `mcpServers`, `lspServers` | string path or inline object | Path resolved under root with containment check; inline values load at runtime |

Validate a tree before publishing:

```bash
grok plugin validate              # default: current directory
grok plugin validate ./my-plugin
```

Create a release tag from the manifest version:

```bash
grok plugin tag [--push] [--force] [--dry-run]
```

## Discovery locations and priority

Scan order (higher priority wins on name collision; duplicate canonical paths are deduped):

| Priority | Location | Scope label | Trust default | Enable default |
|----------|----------|-------------|-----------------|----------------|
| Highest | `--plugin-dir` (repeatable; agent processes, not leader mode) | `cli` | Trusted | Enabled |
| | `_meta.pluginDirs` on ACP `session/new` / `session/load` | `cli` | Trusted | Session-only |
| | `.grok/plugins/*/` (walk cwd → git root) | `project` | Folder trust | Needs `[plugins].enabled` |
| | `.claude/plugins/*/` (compat) | `project` | Folder trust | Needs enable |
| | `~/.grok/plugins/*/`, `~/.grok/installed-plugins/` (registry installs) | `user` | Trusted | Needs enable (install auto-enables) |
| | `~/.claude/plugins/*/` and compat install JSON | `user` | Trusted | Needs enable |
| Lowest | `[plugins].paths` in config | `config` | Auto-trusted if under `$HOME`; else explicit trust store | Auto-enabled |

Plugin identity format:

```text
<scope>/<hex8>/<name>
```

- `scope`: `cli` | `project` | `user` | `config`
- `hex8`: first 8 hex chars of SHA-256 of the canonical plugin root path
- `name`: manifest name (or sanitized directory name)

Use the full ID in `[plugins].enabled` / `disabled` when two plugins share a display name.

## Enable vs trust

Two independent gates:

| Gate | Controls | Storage / API |
|------|----------|---------------|
| **Enabled** | Whether skills, commands, and agents load into the session | `[plugins].enabled` / `[plugins].disabled` in `~/.grok/config.toml`; `grok plugin enable` / `disable` |
| **Trusted** | Whether hooks, MCP servers, and LSP servers may run code | Scope rules above; project folder trust; `~/.grok/trusted-plugins` for non-home config paths |

Untrusted project plugins still appear in inventories (metadata, skill/agent names). Hooks and MCP show as blocked until trust is satisfied.

<Warning>
`grok plugin install` requires `--trust` or it exits without installing. That flag is an explicit install confirmation (hooks/MCP/skills may run after load), separate from the on-disk trust store for project/config paths.
</Warning>

Disabled plugins stay in the registry list but do not contribute components. If a name appears in both lists, **disabled wins**.

## Install, update, and uninstall

Managed installs land under `~/.grok/installed-plugins/` with a `registry.json` index. Local sources are **copied** into that tree (not live symlinks); trusted home local installs can be re-copied on session spawn/reload.

### CLI

```bash
grok plugin list [--json] [--available]   # --available requires --json
grok plugin install <source> --trust
grok plugin uninstall <name> [--confirm] [--keep-data]   # aliases: rm, remove
grok plugin update [<name>]               # omit name = all
grok plugin enable <name>
grok plugin disable <name>
grok plugin details <name>
```

Without `--trust`, install prints the source and:

```text
To proceed, re-run with --trust:
  grok plugin install <source> --trust
```

then exits non-zero.

### Source forms

| Input | Meaning |
|-------|---------|
| `owner/repo` | GitHub shorthand → `https://github.com/owner/repo` |
| `owner/repo@v1.0` | Pin git ref |
| `owner/repo#subdir` | Plugin subdirectory inside the repo |
| `https://…` / `git@…` | Full git URL (`@ref` and `#subdir` supported) |
| `./dir`, `/abs`, `~/dir` | Local path (`#subdir` supported) |
| `plugin-name` | Marketplace install by name |
| `plugin-name@owner/repo` | Marketplace pin by git qualifier |
| `plugin-name@local/<slug>` | Marketplace pin for a local source |

Successful install writes plugin names into `[plugins].enabled` so the next session load activates them.

Uninstall removes the managed repo path and, unless `--keep-data`, deletes `~/.grok/plugin-data/<plugin_id>/`. Multi-plugin repos require `--confirm`.

### Inspect loaded plugins

```bash
grok inspect
grok inspect --json
```

Plugin-backed skills, agents, hooks, and MCP servers are labeled with `plugin: <name>` in the dump.

## Marketplace sources

Official default source (auto-registered identity):

| Field | Value |
|-------|--------|
| Display name | `xAI Official` |
| Git URL | `https://github.com/xai-org/plugin-marketplace.git` |

### Configure sources

**config.toml**

```toml
[[marketplace.sources]]
name = "My Team Plugins"
git = "https://github.com/my-org/plugins.git"
# branch = "main"   # optional

[[marketplace.sources]]
name = "Local Dev"
path = "~/dev/my-plugins"
```

**settings.json** (`~/.grok/settings.json` or `~/.claude/settings.json`)

```json
{
  "extraKnownMarketplaces": {
    "my-marketplace": {
      "source": { "source": "git", "url": "git@github.com:my-org/plugins.git" }
    }
  }
}
```

Supported settings source discriminators: `git` + `url`, `github` + `repo`, `local` + `path`.

### Marketplace CLI

```bash
grok plugin marketplace list [--json]
grok plugin marketplace add <url>       # git URL, owner/repo, or local path
grok plugin marketplace remove <url>
grok plugin marketplace update [<name>] # omit = refresh all caches
```

### Marketplace index layout

Browse prefers a catalog index over a full filesystem walk:

| Preference | Path |
|------------|------|
| 1 | `.grok-plugin/marketplace.json` |
| 2 | `.grok-plugin/plugin.json` |
| 3 | `.claude-plugin/marketplace.json` |
| 4 | `.claude-plugin/plugin.json` |

If no index exists, Grok walks `plugins/*/`. Optional `default-skills/` is treated as a virtual plugin when it contains root-level skill dirs with `SKILL.md`.

Index entries may point at:

- Local path: `"./plugins/foo"` or `{ "type": "local", "path": "..." }`
- Remote URL: `{ "source": "url", "url": "https://...", "ref": "main", "sha": "..." }` (cloned at install time)

Relative paths must stay inside the marketplace root (no `..`, absolute, or drive prefixes).

Optional component catalog `plugin-index.json` (version `1`) under `.grok-plugin/` or `.claude-plugin/` enriches list/UI with structured component inventories. Catalog failures degrade to “no components shown”; they never fail listing. SHA mismatches hide components for URL-sourced pins.

Install provenance is recorded on the registry entry (`source_url_or_path`, display name, plugin subdir) so updates and reinstalls stay source-correct.

## GROK_PLUGIN_ROOT and GROK_PLUGIN_DATA

| Variable / token | Value | Writable? |
|------------------|-------|-----------|
| `GROK_PLUGIN_ROOT` | Absolute path to the installed plugin directory | No (plugin content) |
| `GROK_PLUGIN_DATA` | `~/.grok/plugin-data/<plugin_id>/` | Yes (state, caches, logs) |
| `CLAUDE_PLUGIN_ROOT` / `CLAUDE_PLUGIN_DATA` | Same values | Compatibility aliases |

Injection rules:

- Set as process environment on plugin hooks; **override** any same-key values in the hook JSON `env` map.
- Substituted in hook commands, skill/command bodies, agent templates, and MCP/LSP config strings as `${GROK_PLUGIN_ROOT}`, `${GROK_PLUGIN_DATA}`, and the Claude aliases.
- Hook names are prefixed `plugin/<plugin-name>/…` for identification.

Example hook command:

```json
{
  "hooks": {
    "PreToolUse": [
      {
        "type": "command",
        "command": "${GROK_PLUGIN_ROOT}/hooks/check.sh"
      }
    ]
  }
}
```

## Configuration surface

```toml
[plugins]
paths = ["~/my-plugins/custom-tools"]
disabled = ["user/a1b2c3d4/noisy-plugin"]
enabled = ["project/9f8e7d6c/team-tools"]
```

<ParamField body="paths" type="string[]">
Extra plugin root directories (scope `config`). Trust depends on whether the canonical path is under the user home directory.
</ParamField>

<ParamField body="enabled" type="string[]">
Plugin names or full IDs to load. Install auto-appends here. CLI/config-path scopes auto-populate when missing from both lists.
</ParamField>

<ParamField body="disabled" type="string[]">
Discover but skip loading components. Wins over `enabled` on conflict.
</ParamField>

Hide the hooks/plugins UI (slash commands and scrollback annotations) in pager config:

```toml
# ~/.grok/pager.toml
disable_plugins = true
```

## Interactive management

| Action | Opens |
|--------|-------|
| `Ctrl+L` (non–VS Code family) | Plugins tab |
| `/plugins` (required on VS Code family terminals) | Plugins tab |
| `/marketplace`, `/hooks`, `/skills`, `/mcps` | Matching tabs of the same modal |

Plugins tab actions include reload (`r`), add (`a`), enable/disable (`Space`), uninstall (`x`), filter (`f`), search (`/`). Marketplace tab supports install (`i`), uninstall (`d`), add/remove sources, refresh (`r`), and update (`u`).

## Storage layout

| Path | Role |
|------|------|
| `~/.grok/installed-plugins/` | Managed install snapshots + `registry.json` |
| `~/.grok/plugins/` | User drop-in plugin directories |
| `.grok/plugins/` | Project plugins (VCS-friendly) |
| `~/.grok/plugin-data/<plugin_id>/` | Per-plugin writable data (`GROK_PLUGIN_DATA`) |
| `~/.grok/trusted-plugins` | Canonical paths trusted for non-auto-trust config paths |
| `~/.grok/config.toml` `[plugins]` / `[[marketplace.sources]]` | Enable lists and marketplace sources |

## Failure modes

| Symptom | Likely cause |
|---------|----------------|
| Install exits with “re-run with --trust” | Missing `--trust` |
| Plugin listed but no skills/hooks | Not in `[plugins].enabled`, or present in `disabled` |
| Skills load, MCP/hooks blocked | Untrusted project/config scope |
| `validate` fails on name | Name not kebab-case / too long / leading hyphen |
| Marketplace install ambiguous | Same plugin name in multiple sources — pin with `@owner/repo` or `@local/<slug>` |
| Multi-plugin uninstall refused | Repo contains multiple plugins — pass `--confirm` |
| `--plugin-dir` ignored | Running in leader mode; use `--no-leader` for dedicated agent processes |

## Related pages

<CardGroup>
  <Card title="Skills" href="/skills">
    SKILL.md layout, discovery paths, and how plugin skill trees merge with project and user skills.
  </Card>
  <Card title="Hooks" href="/hooks">
    Lifecycle events, shell/HTTP runners, and env vars (including plugin root/data).
  </Card>
  <Card title="MCP servers" href="/mcp-servers">
    User-registered MCP vs plugin-bundled servers, timeouts, and management commands.
  </Card>
  <Card title="Configure Grok" href="/configure-grok">
    config.toml / pager.toml precedence and `grok inspect` for resolved plugin state.
  </Card>
  <Card title="Permissions and safety" href="/permissions-and-safety">
    How PreToolUse hooks and allow/deny rules interact with plugin-provided hooks.
  </Card>
  <Card title="CLI reference" href="/cli-reference">
    Top-level `grok plugin` and shared runtime flags.
  </Card>
  <Card title="Slash commands" href="/slash-commands">
    `/plugins`, `/marketplace`, and skill-provided commands in autocomplete.
  </Card>
</CardGroup>

---

## 13. Hooks

> Lifecycle hooks (PreToolUse, PostToolUse, SessionStart, and peers): JSON discovery, shell vs HTTP runners, deny/allow results, environment variables, and safety patterns.

- Page Markdown: https://grok-wiki.com/public/docs/xai-org-grok-build-90205de50458/pages/13-hooks.md
- Generated: 2026-07-16T01:24:22.941Z

### Source Files

- `crates/codegen/xai-grok-pager/docs/user-guide/10-hooks.md`
- `crates/codegen/xai-grok-hooks/src/discovery.rs`
- `crates/codegen/xai-grok-hooks/src/dispatcher.rs`
- `crates/codegen/xai-grok-hooks/src/runner`
- `crates/codegen/xai-grok-hooks/src/event.rs`
- `crates/codegen/xai-grok-shell/src/util/hooks.rs`
- `crates/codegen/xai-grok-pager/docs/custom-hooks.md`

---
title: "Hooks"
description: "Lifecycle hooks (PreToolUse, PostToolUse, SessionStart, and peers): JSON discovery, shell vs HTTP runners, deny/allow results, environment variables, and safety patterns."
---

Grok Build’s hooks runtime (`xai-grok-hooks`) discovers JSON hook definitions from global, project, plugin, and custom paths, indexes them by lifecycle event, and runs matching handlers as either local shell/command processes or HTTPS POST requests. Only **`PreToolUse`** is blocking (explicit `allow` / `deny`); every other event is passive and fail-open. Project hooks are gated by the same folder-trust store as repo-local MCP and LSP (`~/.grok/trusted_folders.toml`).

## What hooks do

A hook is a command or HTTPS endpoint Grok invokes when a session lifecycle event fires. Typical uses:

| Goal | Event | Pattern |
|------|--------|---------|
| Block dangerous tool calls | `PreToolUse` | Matcher on tool name; emit `{"decision":"deny",...}` |
| Log / notify after tools | `PostToolUse`, `PostToolUseFailure` | Passive script or HTTP webhook |
| Session setup / teardown | `SessionStart`, `SessionEnd` | Env setup, audit lines |
| Turn and agent boundaries | `Stop`, `SubagentStart`, `SubagentStop` | Metrics, cleanup |
| Compaction | `PreCompact`, `PostCompact` | Archive or react to context shrink |

Hooks sit **before** the permission pipeline’s final tool execution for `PreToolUse` denials. They complement allow/deny rules and sandbox profiles rather than replacing them — see [Permissions and safety](/permissions-and-safety) and [Sandbox profiles](/sandbox).

## Quick start

```sh
mkdir -p ~/.grok/hooks
```

`~/.grok/hooks/session-start.json`:

```json
{
  "hooks": {
    "SessionStart": [
      {
        "hooks": [
          { "type": "command", "command": "echo \"Grok session started in $(pwd)\"" }
        ]
      }
    ]
  }
}
```

Restart or start a session, then open the Hooks UI (`/hooks`, or `Ctrl+L` on non–VS Code family terminals) to confirm load. Built-in examples live under `crates/codegen/xai-grok-hooks/examples/hooks/` (`safe-shell.json`, `no-recursive-grep.json`, `session-log.json`, `tool-logger.json`).

## Discovery and merge order

Session startup and mid-session reload both go through shell discovery (`discover_hooks` / `discover_hook_source_paths`), then `load_hooks_from_sources`.

| Scope | Typical paths | Trust |
|-------|----------------|-------|
| Global | `~/.grok/hooks/*.json` | Always trusted |
| Global (compat) | `~/.claude/settings.json`, `settings.local.json` | Always trusted when scanned |
| Global (compat) | `~/.cursor/hooks.json` | Always trusted when scanned |
| Global (custom) | Paths listed in `~/.grok/hooks-paths` (one path per line) | Always trusted |
| Project | `<git-root>/.grok/hooks/*.json` | Requires folder trust |
| Project (compat) | `<git-root>/.claude/settings.json` (+ local), `.cursor/hooks.json` | Requires folder trust |
| Plugin | Bundled with installed plugins | Per-plugin trust / install model |

**Merge rules:**

- Sources are **additive**. Global specs load first, then project; names are prefixed `global/…` or `project/…`.
- Dedup key: `(event, command_raw, url_raw, configured_matcher)`. Same content + matcher from a later source is skipped; earlier (global) wins.
- Claude/Cursor vendor trees are on by default. Disable with `[compat.claude] hooks = false` or `[compat.cursor] hooks = false` (or matching env) in config — see [Configure Grok](/configure-grok) and [Configuration reference](/configuration-reference).
- After a Claude import mark, live scanning of `.claude/settings*.json` is skipped; native `~/.grok/hooks/` still loads.
- Project sources are omitted entirely when the workspace is untrusted (`include_project: false`).

### Folder trust

`/hooks-trust` or `--trust` writes `~/.grok/trusted_folders.toml` and unlocks **hooks, MCP, and LSP** for that folder (and subdirectories). `/hooks-untrust` revokes. Disabling folder-trust globally (`GROK_FOLDER_TRUST=0` or `[folder_trust] enabled = false`) also ungates project hooks. Legacy `trusted-hook-projects` files are migration-only.

## Events

Config keys accept PascalCase (`PreToolUse`), snake_case (`pre_tool_use`), and Cursor-style camelCase / operation names. Wire JSON on stdin uses **snake_case** event names (e.g. `pre_tool_use`).

| Event | When | Blocking? |
|-------|------|-----------|
| `SessionStart` | Session starts | No |
| `UserPromptSubmit` | User submits a prompt | No |
| `PreToolUse` | Tool about to run | **Yes** — can deny |
| `PostToolUse` | Tool completed successfully | No |
| `PostToolUseFailure` | Tool threw / failed | No |
| `PermissionDenied` | Permission system denied the call | No |
| `Stop` | Agent turn ends (ok / cancel / error) | No |
| `StopFailure` | Turn ends on API error | No |
| `Notification` | Agent notification (permission prompt, idle, …) | No |
| `SubagentStart` / `SubagentStop` | Subagent spawn / finish | No |
| `SubagentEnd` | Alias of `SubagentStop` | No |
| `PreCompact` / `PostCompact` | Before / after compaction | No |
| `SessionEnd` | Session ends | No |

Unrecognized top-level event keys in shared Claude/Cursor files are skipped so the rest of the file still loads.

### Cursor event name map

| Cursor name | Maps to |
|-------------|---------|
| `sessionStart`, `sessionEnd` | `SessionStart`, `SessionEnd` |
| `preToolUse`, `postToolUse`, `postToolUseFailure` | `PreToolUse`, `PostToolUse`, `PostToolUseFailure` |
| `beforeShellExecution`, `beforeMCPExecution`, `beforeReadFile` | `PreToolUse` |
| `afterShellExecution`, `afterMCPExecution`, `afterFileEdit`, `afterAgentResponse`, `afterAgentThought` | `PostToolUse` |
| `beforeSubmitPrompt` | `UserPromptSubmit` |
| `subagentStart`, `subagentStop` | `SubagentStart`, `SubagentStop` |
| `preCompact`, `stop` | `PreCompact`, `Stop` |

Per-operation Cursor hooks become generic tool events; filter with `matcher` or the envelope’s `toolName`.

## JSON format

```json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          { "type": "command", "command": "bin/safety-check.sh", "timeout": 10 }
        ]
      }
    ],
    "PostToolUse": [
      {
        "hooks": [
          { "type": "command", "command": "bin/log-activity.sh" }
        ]
      }
    ]
  }
}
```

| Field | Role |
|-------|------|
| Event key | One of the events above |
| `matcher` | Optional filter (see below). Empty / omitted / `"*"` → match all |
| `type` | `"command"` or `"http"` |
| `command` | Executable path (relative to the JSON file) or shell one-liner |
| `url` | HTTPS endpoint for HTTP handlers |
| `timeout` | Seconds (default **5** → `timeout_ms` 5000) |
| `env` | Extra string env vars for the child (values must be JSON strings) |

Unsupported handler types fail at parse/run with a clear error; they do not block the session.

## Matchers

Matchers apply to:

- Tool events: `PreToolUse`, `PostToolUse`, `PostToolUseFailure`, `PermissionDenied` — tested against **resolved tool name**
- `Notification` — tested against `notificationType`

**Lifecycle events** that reject a matcher at load: `SessionStart`, `SessionEnd`, `Stop`, `UserPromptSubmit`. Other non-tool events ignore matchers.

### Matching rules

1. Empty pattern or `"*"` → match everything.
2. **Simple form** (only `[A-Za-z0-9_|]`): exact match per `|`-term, with Claude→Grok alias expansion (e.g. `Bash` also matches `run_terminal_command`).
3. Otherwise: **unanchored regex**, also tried against external alias names (so `^Bash$` still hits Grok’s shell tool).

MCP tools dispatched through the internal meta-tool appear as the qualified `server__tool` name (e.g. `linear__save_issue`), not the dispatcher name — match the qualified name.

Common aliases in matchers: `Bash` → `run_terminal_command`; `Read` → `read_file`; `Edit` / `Write` / `MultiEdit` → `search_replace`; `Grep` → `grep`; `Glob` / `ListDir` → `list_dir`; `WebSearch` → `web_search`; `Task` → `spawn_subagent`.

**Not env-expanded:** `matcher` is never substituted (`$` is a regex anchor). Generate dynamic matchers at write time.

## Command runner

For `type: "command"`:

1. Envelope JSON is written to **stdin**.
2. If `command` contains shell metacharacters (space, `|`, `&`, `;`, redirects, `$`) or starts with `~`, the runner uses `sh -c`; otherwise it direct-execs, resolving relative paths against the hook JSON’s directory.
3. Unresolved bare `$VAR` / `${VAR}` (no parameter-expansion modifier) refuse to spawn with a clear “required env var(s) not set” error (fail-open for the tool).
4. Timeout kills the process; stdout/stderr capture is capped (~64 KB).
5. `GROK_HOOK_DEBUG=1` enables trace logging of stdin payload sizes.

Default timeout: **5 seconds**.

### Blocking output (`PreToolUse`)

Stdout JSON:

```json
{"decision": "allow"}
```

```json
{"decision": "deny", "reason": "Unsafe command detected"}
```

| Exit code | Meaning |
|-----------|---------|
| `0` | Success / allow (if no deny JSON) |
| `2` | Explicit deny (blocking hooks) |
| Other | Fail-open for tool policy (logged in scrollback) |

JSON `{"decision":"deny",...}` **wins over exit code** (including exit 0). Passive-open applies to timeouts, crashes, missing commands, malformed output, and spawn refusals. **Only an explicit deny decision blocks the tool.**

Passive events ignore decision JSON; prefer exit 0.

### Example: safe shell guard

```json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          { "type": "command", "command": "bin/safe-shell-guard.sh", "timeout": 5 }
        ]
      }
    ]
  }
}
```

The example script reads stdin, pattern-matches destructive shell lines (`rm -rf /`, `mkfs`, …), prints deny JSON + exit `2` on hit, otherwise allow + exit `0`.

## HTTP runner

```json
{ "type": "http", "url": "https://hooks.example.com/grok-event", "timeout": 15 }
```

- POSTs the full event envelope as JSON.
- Same allow/deny JSON body for blocking hooks.
- **SSRF controls (CWE-918):** only **`https://`**; DNS-resolved addresses must not be private RFC1918, link-local/metadata (`169.254/16`), CGNAT (`100.64/10`), unspecified, or IPv6 ULA/link-local. **Loopback is allowed** for local dev (`127.0.0.0/8`, `::1`).
- URL is env-expanded at request time (after plugin env injection), then validated — SSRF checks the expanded URL.
- Display surfaces prefer pre-expansion `url_raw` so secrets in `${TOKEN}` do not leak into UI logs.

## Event envelope (stdin / POST body)

Common fields (camelCase wire format):

| Field | Description |
|-------|-------------|
| `hookEventName` | Snake_case event id |
| `sessionId` | Current session id |
| `cwd` | Working directory |
| `workspaceRoot` | Workspace root |
| `timestamp` | ISO-style timestamp |
| `transcriptPath`, `clientIdentifier`, `promptId` | Optional context |

Event-specific fields are flattened into the same object. Tool events include:

- `toolName`, `toolUseId`, `toolInput`, `toolInputTruncated`
- `PostToolUse` also: `toolResult`, `toolResultTruncated`, `durationMs`, `isBackgrounded`, optional `subagentType`
- Large `toolInput` / `toolResult` payloads are truncated at **128 KB** serialized size (`MAX_PAYLOAD_SIZE`) with `*Truncated: true`

`PreToolUse` example:

```json
{
  "hookEventName": "pre_tool_use",
  "sessionId": "abc-123",
  "cwd": "/Users/you/project",
  "workspaceRoot": "/Users/you/project",
  "toolName": "run_terminal_command",
  "toolUseId": "...",
  "toolInput": { "command": "npm test" },
  "toolInputTruncated": false,
  "timestamp": "2026-04-14T12:00:00Z"
}
```

## Environment variables

### Runner-injected (always; reserved)

| Variable | Meaning |
|----------|---------|
| `GROK_HOOK_EVENT` | Snake_case event name |
| `GROK_HOOK_NAME` | Configured hook name (includes source/plugin prefix) |
| `GROK_SESSION_ID` | Session id |
| `GROK_WORKSPACE_ROOT` | Workspace root |
| `CLAUDE_PROJECT_DIR` | Same as workspace root (Claude Code compat) |

User `env` entries for these keys are **stripped at load** (warning logged). The runner re-injects real values **after** `extra_env` so they always win.

### Plugin hooks

| Variable | Meaning |
|----------|---------|
| `GROK_PLUGIN_ROOT` / `CLAUDE_PLUGIN_ROOT` | Plugin install directory |
| `GROK_PLUGIN_DATA` / `CLAUDE_PLUGIN_DATA` | Writable plugin data directory |

Plugin adapter values override user `env` for those keys. See [Plugins](/plugins).

### Handler `env` map

```json
{
  "type": "command",
  "command": "bin/check.sh",
  "env": {
    "MY_API_TOKEN": "secret-here",
    "LOG_LEVEL": "debug"
  }
}
```

Values must be strings. **`env` map values are not expanded** (`"BAR": "${HOME}/x"` injects the literal). Use expansion on `command` / `url` instead.

### `$VAR` / `${VAR}` in `command` and `url`

Load-time expansion order: handler `env` map, then process environment. Unset plain references stay literal for the shell path. POSIX modifiers (`${VAR:-default}`, `${VAR%pat}`, …) are **not** expanded at load time — left for `sh -c`. HTTP `url` is re-expanded at request time; command paths that direct-exec are **not** re-expanded mid-session.

## Dispatch semantics

- Hooks for an event run **sequentially** in registry order.
- Disabled hooks (`spec.enabled == false` or listed in `~/.grok/disabled-hooks`) are skipped.
- `PreToolUse`: first **explicit deny** short-circuits; later hooks do not run. Any earlier allow + later deny → **deny** (stricter filter wins).
- Non-blocking dispatch never denies; failures are recorded for scrollback only.
- Fail-open is intentional: timeout/crash of a security hook does **not** block the tool — write robust scripts that always emit an explicit deny when policy requires it.

## Manage hooks in the TUI

| Action | How |
|--------|-----|
| Open Hooks tab | `/hooks` (any terminal); `Ctrl+L` opens Extensions on non–VS Code family |
| Reload from disk | `r` in Hooks tab |
| Add custom path | `a` or `/hooks-add <path>` |
| Remove | `x` or `/hooks-remove <path>` |
| Enable / disable | `Space` (persists via `~/.grok/disabled-hooks`) |
| Filter | `f` — All / Enabled / Disabled |
| List (headless-ish) | `/hooks-list` |
| Trust / untrust project | `/hooks-trust`, `/hooks-untrust` |

Groups: **Global**, **Project**, **Plugin**, **Custom**. Hook run outcomes appear as scrollback annotations when the plugins UI is enabled (default).

Related slash surfaces: [Slash commands](/slash-commands).

## Safety patterns

1. **Fail-open** — never rely on exit-non-zero alone; emit `{"decision":"deny","reason":"..."}` to block.
2. **Keep hooks short** — they run on the tool path; long work should background or use HTTP async consumers.
3. **Trust gates** — do not commit secrets; project hooks need explicit folder trust.
4. **HTTP** — only HTTPS public (or loopback) endpoints; envelope includes session and tool data.
5. **Relative scripts** — `bin/…` next to the JSON file is portable across clones.
6. **Matchers** — prefer simple names or careful regex; invalid regex falls back to match-all at recompile (fail-open).
7. **Debug** — `RUST_LOG=debug GROK_LOG_FILE=/tmp/grok.log grok` and/or `GROK_HOOK_DEBUG=1`.

### Interaction with other safety layers

| Layer | Role vs hooks |
|-------|----------------|
| Permission rules / modes | Separate allow/deny/ask pipeline; hooks can deny earlier on `PreToolUse` |
| Safe-bash / remembered grants | Still apply after hooks allow |
| Sandbox profiles | OS isolation of the tool process; hooks themselves run as the user outside that sandbox |

## Troubleshooting

| Symptom | Check |
|---------|--------|
| Hook never runs | `/hooks` loaded? Matcher vs real `toolName`? Disabled list? |
| Project hooks missing | Folder trust (`/hooks-trust` / `--trust`) |
| Script not found | Path relative to JSON; `chmod +x`; metacharacters force `sh -c` |
| Deny not blocking | Must be `PreToolUse` + stdout JSON deny (or exit 2 with deny path); failures fail-open |
| HTTP hook fails | HTTPS only; not private IP; DNS resolution; timeout |
| Compat file ignored | `[compat.*.hooks]`, Claude import mark, or trust |

## Implementation map

| Area | Location |
|------|----------|
| Events, envelope, truncation | `crates/codegen/xai-grok-hooks/src/event.rs` |
| Discovery / registry | `…/discovery.rs` |
| Parse / HookSpec / defaults | `…/config.rs` |
| Dispatch allow/deny | `…/dispatcher.rs` |
| Matchers + aliases | `…/matcher.rs` |
| Command / HTTP runners | `…/runner/command.rs`, `…/runner/http.rs` |
| Decisions / run results | `…/result.rs` |
| Disable list / legacy trust | `…/trust.rs` |
| Path discovery + trust gate | `crates/codegen/xai-grok-shell/src/util/hooks.rs` |
| User-facing guides | `crates/codegen/xai-grok-pager/docs/user-guide/10-hooks.md`, `custom-hooks.md` |

## Next

<CardGroup cols={2}>
  <Card title="Permissions and safety" href="/permissions-and-safety">
    Tool authorization pipeline, permission modes, and how PreToolUse denials interact with rules.
  </Card>
  <Card title="Plugins" href="/plugins">
    Bundle hooks with skills and MCP; GROK_PLUGIN_ROOT and GROK_PLUGIN_DATA.
  </Card>
  <Card title="Configure Grok" href="/configure-grok">
    config.toml precedence, compat vendor toggles, and inspect resolved settings.
  </Card>
  <Card title="Slash commands" href="/slash-commands">
    /hooks modal, trust, and related interactive commands.
  </Card>
</CardGroup>

## Related pages

- [Sandbox profiles](/sandbox) — OS isolation after hooks allow a tool
- [Subagents and personas](/subagents) — SubagentStart / SubagentStop payloads
- [MCP servers](/mcp-servers) — Match qualified `server__tool` names
- [Headless mode](/headless-mode) — CI runs still load hooks under the same discovery rules
- [Troubleshooting](/troubleshooting) — Logs, terminal, and diagnostics

---

## 14. 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.

- Page Markdown: https://grok-wiki.com/public/docs/xai-org-grok-build-90205de50458/pages/14-headless-mode.md
- Generated: 2026-07-16T01:24:36.780Z

### 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>

---

## 15. Agent mode (ACP)

> Run grok agent stdio as an Agent Client Protocol server for IDEs and custom clients; JSON-RPC session lifecycle, streaming, permissions, and options shared with agent subcommands.

- Page Markdown: https://grok-wiki.com/public/docs/xai-org-grok-build-90205de50458/pages/15-agent-mode-acp.md
- Generated: 2026-07-16T01:24:04.625Z

### Source Files

- `crates/codegen/xai-grok-pager/docs/user-guide/15-agent-mode.md`
- `crates/codegen/xai-acp-lib/src/lib.rs`
- `crates/codegen/xai-acp-lib/src/gateway.rs`
- `crates/codegen/xai-grok-shell/src/session/acp_session.rs`
- `crates/codegen/xai-grok-shell/src/session/acp_types.rs`
- `crates/codegen/xai-grok-pager/src/app/cli.rs`

---
title: "Agent mode (ACP)"
description: "Run grok agent stdio as an Agent Client Protocol server for IDEs and custom clients; JSON-RPC session lifecycle, streaming, permissions, and options shared with agent subcommands."
---

`grok agent stdio` runs the Grok agent as a long-lived **Agent Client Protocol (ACP)** server. Clients exchange **newline-delimited JSON-RPC 2.0** messages over the process **stdin** (requests/notifications) and **stdout** (responses/notifications). Unlike `grok -p` / `--single` (one prompt, print, exit), agent mode keeps the process up for many sessions and turns until the client closes the transport.

```mermaid
sequenceDiagram
  participant Client as ACP client<br/>(IDE / SDK / custom)
  participant Stdio as grok agent stdio
  participant Agent as Agent + SessionActor
  participant Tools as Tools / MCP / FS

  Client->>Stdio: initialize
  Stdio-->>Client: InitializeResponse (capabilities, models, authMethods)
  Client->>Stdio: authenticate (optional / as required)
  Client->>Stdio: session/new { cwd, mcpServers, _meta }
  Stdio->>Agent: create SessionActor
  Agent-->>Client: sessionId + modes/models
  Client->>Stdio: session/prompt
  loop Streamed turn
    Agent-->>Client: session/update (message / thought / tool_call)
    Agent->>Client: session/request_permission (when needed)
    Client-->>Agent: allow / deny / cancel
    Agent->>Tools: execute approved tools
  end
  Agent-->>Client: PromptResponse (stopReason)
  Client->>Stdio: session/cancel (optional, mid-turn)
```

## When to use agent mode

| Surface | Command | Lifetime | Best for |
| --- | --- | --- | --- |
| Interactive TUI | `grok` | User-driven terminal UI | Day-to-day coding in a terminal |
| Headless single-shot | `grok -p` / `--single` | One prompt → exit | Scripts, CI, eval harnesses |
| **ACP agent (stdio)** | `grok agent stdio` | Persistent process | IDEs, editors, ACP SDKs, custom clients |
| WebSocket server | `grok agent serve` | Persistent listener | Remote clients over `ws://` |
| Relay headless | `grok agent headless` | Persistent outbound WS | Web UIs via a shared relay |
| Shared leader | `grok agent leader` | Multi-client backend | Multiple followers sharing one agent |

Provider and endpoint settings stay portable: model selection, BYOK / OpenAI-compatible bases, and MCP registration use the same config layers as other surfaces (`config.toml`, env, CLI flags). Agent mode does not require a specific third-party host; any ACP client that speaks the protocol can attach.

## Start the stdio server

```bash
grok agent stdio
```

Options for `grok agent` go **before** the mode name. The `stdio` mode itself takes no extra flags:

```bash
grok agent --model grok-build --always-approve stdio
grok agent --agent-profile ./my-agent.md --plugin-dir /path/to/plugin stdio
```

<Check>
The agent writes JSON-RPC only on **stdout**. Diagnostics and human messages use **stderr** (for example `--plugin-dir` skip warnings, serve startup banner). Do not mix client parsers with stderr.
</Check>

### Prerequisites

- Installed `grok` on `PATH` (see [Installation](/installation)).
- Auth already present (`grok login`, device flow, or `XAI_API_KEY` / stored credentials in `~/.grok/auth.json`) unless the client drives `authenticate` after `initialize`.
- Working directory meaningful for the workspace the client will pass as `session/new` `cwd`.

### Verification

1. Spawn `grok agent stdio`.
2. Send an `initialize` request with `protocolVersion` and client capabilities.
3. Expect an `InitializeResponse` whose `_meta` includes `"grokShell": true` when talking to a grok-shell agent, plus `modelState`, `availableCommands`, and `authMethods`.
4. Create a session with `session/new`, then `session/prompt`; observe `session/update` notifications before the final prompt result.

## Agent CLI surface

### Modes (`AgentCmd`)

| Mode | Command | Role |
| --- | --- | --- |
| **stdio** | `grok agent stdio` | NDJSON ACP on stdin/stdout (primary IDE integration) |
| **headless** | `grok agent headless` | Outbound Grok WebSocket relay (`--grok-ws-url`, `--grok-ws-origin`) |
| **serve** | `grok agent serve` | Local WebSocket server (default bind `127.0.0.1:2419`) |
| **leader** | `grok agent leader` | Shared leader process for multi-client attach |
| *(omit mode)* | `grok agent` | Same path as **headless** (defaults into the headless runner) |

### Flags on `grok agent` (before the mode)

| Flag | Effect |
| --- | --- |
| `-m, --model <MODEL>` | Default model override for sessions this process creates |
| `--reasoning-effort` / `--effort <EFFORT>` | Reasoning effort override |
| `--always-approve` (`--yolo`) | Seed sessions with always-approve (YOLO) tool permissions |
| `--agent-profile <PATH>` | Load an agent definition file for this process |
| `--plugin-dir <DIR>` (repeatable) | Process-local plugins; always trusted (hooks/MCP activate without prompt). Canonicalized; non-directories skipped with a stderr warning |
| `--reauth` / `--reauthenticate` | Run authentication before start (used by headless path) |
| `--leader` / `--no-leader` | Prefer or force non-shared agent (conflicts with each other). Defaults also come from `[cli] use_leader` |
| `--cli-chat-proxy-base-url` | Override CLI chat proxy base URL |
| `--xai-api-base-url` | Override public xAI API base URL |
| Nested WS URL args | `--grok-ws-url`, `--grok-ws-origin` (headless / serve / leader) |

### `serve` options

| Flag / env | Default | Notes |
| --- | --- | --- |
| `--bind <ADDR>` | `127.0.0.1:2419` | Listen address |
| `--secret <TOKEN>` or `GROK_AGENT_SECRET` | Auto-generated (12 chars) if omitted | Client auth; printed on stderr at startup as `ws://{bind}/ws?server-key=...` |
| `--remote <URL>` | — | Proxy mode remote agent URL |

### Top-level flags that still apply to `grok agent`

When dispatching `Command::Agent`, the binary also threads these **top-level** `PagerArgs` into the agent process:

| Flag | Effect on agent |
| --- | --- |
| `--permission-mode <MODE>` | Launch permission mode; `bypassPermissions` / `always-approve` map to YOLO |
| `--trust` | Grant folder trust for current cwd |
| `--disable-web-search` | Disable web search / fetch tools in resolved agent config |
| `--no-auto-update` | Skip auto-update checks for this run |

Top-level `--leader` / `--no-leader` (pager TUI) **do not** apply to the agent subcommand; use `grok agent --leader` / `--no-leader` instead.

### Permission mode values

`--permission-mode` accepts:

`default` · `acceptEdits` · `auto` · `dontAsk` · `bypassPermissions` · `plan`

Launch resolution:

- Explicit `--permission-mode` wins for the launch.
- Otherwise `--always-approve` / `--yolo` enables always-approve.
- Managed policy can **block** always-approve (`disable_bypass_permissions_mode` / requirements); the CLI prints a warning and does not enable YOLO.

Full tool-authorization pipeline (hooks, allow/deny, remembered grants, sandbox) is documented under [Permissions and safety](/permissions-and-safety).

### Leader-backed stdio

When leader mode is active (`--leader` or `[cli] use_leader = true` and policy allows it), `grok agent stdio` becomes a **thin bridge**: stdin lines go to the leader over IPC; stdout mirrors leader messages. On reconnect it may re-play initialize/session state and emit `x.ai/leader_reconnected`.

`--plugin-dir` is **ignored** in leader mode (plugins must load on the leader process). Use `--no-leader` for per-process plugins.

## Transport and I/O contract

- **Framing:** one JSON-RPC message per line (`\n`-delimited NDJSON).
- **Max line size:** 64 MiB (hard cap in the line-buffered reader).
- **Stdin ownership:** a dedicated OS thread reads stdin so persistent clients (stdin stays open) work reliably; Windows additionally detaches the process stdin handle to avoid lock deadlocks with stray `stdin()` reads.
- **Internal inject path:** stdio mode multiplexes client lines with internal reloads (for example skills watcher) through a simplex buffer so injected control messages do not split mid-line.
- **Do not** write logs to stdout from wrappers; that corrupts the protocol.

Environment hooks useful to integrators:

| Variable | Role |
| --- | --- |
| `GROK_CLIENT_VERSION` | Logged early for auth/diagnostics when a desktop host spawns the agent |
| `GROK_AGENT_METADATA` | Optional JSON object merged into `InitializeResponse.meta.metadata` |
| `GROK_AGENT_SECRET` | Default secret for `agent serve` |
| `GROK_AGENT` | Named agent selection (below CLI `--agent-profile` / session `_meta.agentProfile`) |
| `XAI_API_KEY` | API-key auth path (same as other surfaces) |

## JSON-RPC session lifecycle

### Agent methods (client → agent)

Core ACP methods handled by the agent side:

| Method | Kind | Purpose |
| --- | --- | --- |
| `initialize` | request | Protocol version, client capabilities → agent capabilities + meta |
| `authenticate` | request | Auth method id (OIDC / API key / provider methods as advertised) |
| `session/new` | request | Create session (`cwd`, `mcpServers`, optional `_meta`) |
| `session/load` | request | Load / resume a persisted session |
| `session/prompt` | request | Submit user content blocks; streams updates until final result |
| `session/cancel` | notification | Cancel the in-flight turn for a session |
| `session/set_mode` | request | Change session mode |
| `session/set_model` | request | Change session model |
| `ext_method` / `ext_notification` | request / notification | Vendor extensions (Grok uses `x.ai/…`) |

### Client reverse methods (agent → client)

While a turn runs, the agent may call **into** the client:

| Method | Purpose |
| --- | --- |
| `session/request_permission` | Approve or deny a tool invocation |
| `fs/read_text_file` / `fs/write_text_file` | Client-side file ops when FS capabilities are enabled |
| Terminal methods | `create` / `output` / `release` / `wait_for_exit` / `kill` (when terminal capability is on) |
| `session/update` (notification) | Streamed turn progress |
| Client `ext_method` / `ext_notification` | e.g. client hook gate `x.ai/hooks/run` |

Clients that cannot implement a reverse method must advertise limited capabilities on `initialize` and handle `MethodNotFound` where applicable (for example some hosts reject `WaitForTerminalExit` and poll instead).

### `initialize` response (agent capabilities)

Successful initialize advertises roughly:

- **Protocol:** ACP v1
- **Capabilities:** `loadSession: true`, prompt `embeddedContext: true`, MCP over HTTP and SSE
- **Capability meta:** `x.ai/fs_notify`, blocking client hooks for `PreToolUse` with `deny` decisions
- **Response `_meta` (selected fields):** `grokShell`, `defaultAuthMethodId`, `agentVersion`, `agentId`, `agentInstanceId`, `hostname`, `currentWorkingDirectory`, `modelState`, `mcpServers`, `availableCommands`, `cancelRewind`, `sessionRecap`, optional `metadata` from `GROK_AGENT_METADATA`

Auth methods in the response drive whether the client must call `authenticate` or can proceed with cached credentials / API key.

### `session/new` and `session/load`

**Request (shape):**

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "session/new",
  "params": {
    "cwd": "/path/to/project",
    "mcpServers": [],
    "_meta": {
      "rules": "Extra rules appended to the system prompt",
      "systemPromptOverride": "Replace the system prompt entirely",
      "agentProfile": "grok-build",
      "yoloMode": false,
      "autoMode": false,
      "x.ai/hooks": []
    }
  }
}
```

| `_meta` field | Meaning |
| --- | --- |
| `rules` | Extra rules appended to the system prompt |
| `systemPromptOverride` | Full system prompt replacement (also re-applied on cold load when present) |
| `agentProfile` | String name **or** JSON object definition for the agent persona/toolset |
| `yoloMode` | Seed always-approve for this session (leader can inject this for capable followers) |
| `autoMode` | Seed classifier auto-permission mode (ignored when yolo is on) |
| `x.ai/hooks` | Client-registered hooks (see below) |

**Agent profile resolution order** (highest wins):

1. Session `_meta.agentProfile`
2. CLI `--agent-profile`
3. `[agent]` in `config.toml`
4. `GROK_AGENT` env
5. Built-in default agent

`mcpServers` from the client are merged with managed/project MCP config for that `cwd`.

### Prompt turn and streaming

`session/prompt` accepts a `sessionId` and `prompt` content blocks (text and other ACP block types the client supports). During the turn the agent emits **`session/update`** notifications. Standard `sessionUpdate` values clients should handle:

| `sessionUpdate` | Content |
| --- | --- |
| `agent_message_chunk` | Incremental assistant text |
| `agent_thought_chunk` | Incremental reasoning / thought text |
| `tool_call` | New tool invocation (title, kind, status, input; may include `_meta` such as `x.ai/tool`) |
| `tool_call_update` | Status/result updates for an in-flight tool |
| `plan` | Structured plan payload when plan mode is active |

The prompt **response** carries a `stopReason` such as `end_turn` or `cancelled` (cancel path). After cancel, clients should treat the turn as finished and may start a new `session/prompt`.

Grok also streams **vendor** updates on `_x.ai/session/update` / related methods for subagents, goals, hook annotations, queue changes, and similar. Treat unknown `sessionUpdate` values as forward-compatible (ignore or log).

### Permissions during a turn

Without always-approve, tool use goes through:

1. PreToolUse hooks (file hooks + client `x.ai/hooks/run` when registered)
2. Allow / deny rules and remembered grants
3. `session/request_permission` to the client when the policy is **ask**
4. Sandbox profile constraints for the child process

With `--always-approve` / `yoloMode` / `bypassPermissions` (and no managed block), the agent auto-approves tool executions that policy still allows.

Client hooks at `session/new` (`_meta["x.ai/hooks"]`):

- **PreToolUse:** reverse **request** `x.ai/hooks/run` — `deny` blocks the tool; default timeout **30s** (fails open on timeout/malformed reply)
- **Other events:** fire-and-forget notification `x.ai/hooks/event`

## Extension methods (`x.ai/…`)

Beyond base ACP, the agent implements many **extension** methods under the `x.ai/` prefix. Categories (non-exhaustive; discover what the running agent implements via initialize meta and method errors):

| Category | Examples |
| --- | --- |
| Filesystem | `x.ai/fs/*` list/exists/read/write; notifications `x.ai/fs/index`, `x.ai/fs/index/delta`, `x.ai/fs_notify` |
| Git / worktree | `x.ai/git/*`, `x.ai/git/worktree/*` (create, remove, apply, list, gc, resume helpers) |
| Search | `x.ai/search/*` (fuzzy/content); status notifications |
| Terminal | `x.ai/terminal/*` |
| Session admin | `x.ai/session/info`, `close`, `list`, `fork`, `repair`, `update_mcp_servers`, `search` |
| Conversation | compact, rewind / rewind points, prompt history (see types under shell session ACP types) |
| Hooks / plugins / MCP | `x.ai/hooks/*`, `x.ai/plugins/*`, `x.ai/mcp/list`, marketplace actions |
| Memory | `x.ai/memory/flush`, `x.ai/memory/rewrite` |
| Auth helpers | `x.ai/auth/*` (URL / device code flows for interactive clients) |
| Telemetry / feedback | `x.ai/log`, `x.ai/feedback`, `x.ai/recap` (when advertised) |
| Leader | `x.ai/leader_reconnected`, version mismatch notifications |

Extension methods are SpaceXAI/Grok-specific and evolve across releases. Prefer capability meta and error handling over hard-coding a frozen list.

## Architecture (stdio process)

```text
┌──────────────────────────┐     NDJSON JSON-RPC      ┌──────────────────────────────┐
│ ACP client               │ ───────────────────────► │ grok agent stdio             │
│ (IDE, SDK, custom host)  │ ◄─────────────────────── │                              │
└──────────────────────────┘     stdout / reverse RPC │  xai-acp-lib gateway         │
                                                      │    ↕                         │
                                                      │  Agent (mvp_agent)           │
                                                      │    ├─ session registry       │
                                                      │    ├─ SessionActor per id    │
                                                      │    ├─ tools + permissions    │
                                                      │    ├─ MCP servers            │
                                                      │    └─ auth manager           │
                                                      └──────────────────────────────┘
```

Internal crates of note:

| Crate / module | Responsibility |
| --- | --- |
| `xai-acp-lib` | Channels, gateway sender/receiver, stdin line reader, message enums |
| `xai-grok-shell` `agent::app::run_stdio_agent` | Stdio entrypoint, LocalSet, auth refresh, I/O bind |
| `mvp_agent::acp_agent` | ACP `Agent` trait: initialize, session, prompt, cancel, extensions |
| `session::acp_session` | Per-session actor: turns, tools, streaming, permissions |
| `xai-grok-pager-bin` `run_agent_command` | CLI dispatch for all agent modes + leader bridge |

## Minimal client flow

<Steps>
  <Step title="Spawn the agent">
    Start `grok agent stdio` as a child process. Keep stdin open for the whole session. Read stdout line-by-line as JSON-RPC.
  </Step>
  <Step title="Initialize and authenticate">
    Call `initialize` with your `protocolVersion` and capabilities (`fs`, `terminal` as supported). If `authMethods` require it, call `authenticate` with the chosen method id (or rely on pre-seeded credentials / API key).
  </Step>
  <Step title="Create a session">
    Call `session/new` with absolute `cwd`, any client MCP servers, and optional `_meta` (`rules`, `agentProfile`, hooks). Store `sessionId` from the result.
  </Step>
  <Step title="Prompt and stream">
    Call `session/prompt` with content blocks. Handle `session/update` until a response with `result` / `stopReason` arrives. Implement `session/request_permission` so tool calls are not stuck in ask mode.
  </Step>
  <Step title="Cancel or resume">
    Send `session/cancel` to abort a turn. Use `session/load` (or session extension helpers) to resume persisted work; use `session/set_model` / `session/set_model` as needed mid-session.
  </Step>
</Steps>

<RequestExample>
```bash
# Shell: start ACP stdio agent (IDE/SDK spawns this)
grok agent --model grok-build stdio
```
</RequestExample>

<RequestExample>
```json
{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":1,"clientCapabilities":{"fs":{"readTextFile":true,"writeTextFile":true},"terminal":true}}}
```
</RequestExample>

<ResponseExample>
```json
{"jsonrpc":"2.0","id":1,"result":{"protocolVersion":1,"agentCapabilities":{"loadSession":true},"authMethods":[],"_meta":{"grokShell":true,"agentVersion":"…","modelState":{}}}}
```
</ResponseExample>

Official ACP SDKs (language-neutral protocol) are available for TypeScript (`@agentclientprotocol/sdk`), Rust (`agent-client-protocol`), Python, Go, and Kotlin. Point them at the `grok agent stdio` binary rather than re-implementing framing.

## Comparison with headless `-p`

| Concern | `grok agent stdio` | `grok -p` / headless |
| --- | --- | --- |
| Protocol | ACP JSON-RPC | Plain / JSON / streaming-json CLI output |
| Lifetime | Persistent | Single prompt (optional resume flags) |
| Permissions UX | Client implements `request_permission` | CLI allow/deny, YOLO, or non-interactive policy |
| Sessions | `session/new` + `session/load` | `--resume` / `--continue` / `--session-id` |
| Integrators | IDEs, multi-turn UIs | Scripts and CI |

Many runtime concerns (models, MCP, skills, sandbox, permissions, memory) are shared. Prefer agent mode when the host needs multi-turn streaming and interactive tool approval; prefer `-p` for fire-and-forget automation (see [Headless mode](/headless-mode)).

## Operational notes

- **Auth refresh:** stdio starts proactive token refresh and a system-power listener so refresh does not straddle suspend on interactive hosts.
- **Shutdown:** closing stdin ends the reader; the agent flushes pending handlers, closes PTYs, and allows a short grace period for upload queue drain.
- **Updates:** direct stdio (non-leader) may run non-blocking update checks in the background; stderr stays free of NDJSON.
- **Config:** same effective config as other entrypoints (`CLI > env > user config > managed/requirements > defaults`). Dump with `grok inspect` when diagnosing model or permission seeds.
- **Folder trust:** `--trust` persists trust for the current directory before the agent runs.

## Troubleshooting

| Symptom | Likely cause | What to check |
| --- | --- | --- |
| Client hangs on first request | Stdin not line-flushed or stdout mixed with logs | Ensure NDJSON lines end with `\n`; redirect logs to stderr/files |
| `initialize` never completes (Windows) | Competing stdin readers | Only the agent owns stdin; avoid spawning interactive sub-prompts on the same process stdin |
| Tools never run / hang | Unhandled `request_permission` | Implement permission responses or pass `--always-approve` / `yoloMode` (policy permitting) |
| Auth errors on first turn | No credentials / wrong method | `grok login`, `XAI_API_KEY`, or client `authenticate`; see [Authentication](/authentication) |
| Plugins missing in leader mode | `--plugin-dir` ignored | Load plugins on the leader or use `--no-leader` |
| YOLO ignored with warning | Managed policy pin | Requirements/managed config disables bypass; remove pin or use allow rules instead |
| Protocol parse errors | Multi-line or oversized payload | One JSON object per line; keep under 64 MiB |

For terminal/SSH/color issues outside ACP, see [Troubleshooting](/troubleshooting).

## Related pages

<CardGroup>
  <Card title="Overview" href="/overview">
    How TUI, headless `-p`, and ACP agent surfaces fit together.
  </Card>
  <Card title="Headless mode" href="/headless-mode">
    Single-shot `-p` runs, output formats, and CI patterns.
  </Card>
  <Card title="Permissions and safety" href="/permissions-and-safety">
    Tool authorization, permission modes, hooks, and sandbox interaction.
  </Card>
  <Card title="Sessions" href="/sessions">
    Persistence, resume/fork/compact, and session ID constraints.
  </Card>
  <Card title="MCP servers" href="/mcp-servers">
    Register stdio/HTTP MCP servers used from `session/new` and config.
  </Card>
  <Card title="CLI reference" href="/cli-reference">
    Full `grok` command and shared runtime flag inventory.
  </Card>
  <Card title="Authentication" href="/authentication">
    OAuth, device code, API keys, and credential storage.
  </Card>
  <Card title="Configure Grok" href="/configure-grok">
    config.toml precedence, feature flags, and `grok inspect`.
  </Card>
</CardGroup>

---

## 16. Sandbox profiles

> OS-level isolation profiles (workspace, read-only, strict, devbox, off), --sandbox flag, custom deny globs, Linux vs macOS enforcement differences for child network.

- Page Markdown: https://grok-wiki.com/public/docs/xai-org-grok-build-90205de50458/pages/16-sandbox-profiles.md
- Generated: 2026-07-16T02:37:47.850Z

### Source Files

- `crates/codegen/xai-grok-pager/docs/user-guide/18-sandbox.md`
- `crates/codegen/xai-grok-sandbox/src/profiles.rs`
- `crates/codegen/xai-grok-sandbox/src/lib.rs`
- `crates/codegen/xai-grok-sandbox/src/types.rs`
- `crates/codegen/xai-grok-sandbox/src/deny`
- `crates/codegen/xai-grok-pager/src/app/cli.rs`

---
title: "Sandbox profiles"
description: "OS-level isolation profiles (workspace, read-only, strict, devbox, off), --sandbox flag, custom deny globs, Linux vs macOS enforcement differences for child network."
---

Sandbox mode applies OS-level filesystem and network limits to the entire `grok` process at startup via the `xai-grok-sandbox` crate and the [nono](https://crates.io/crates/nono) kernel backend (Landlock on Linux, Seatbelt on macOS). Limits cover in-process tools (`read_file`, `search_replace`, `list_dir`) and child processes (`bash`, `rg`); they are irreversible once applied. Sandbox is **off** by default.

```bash
grok --sandbox workspace
grok --sandbox read-only
grok --sandbox strict
```

## Built-in profiles

| Profile | FS read | FS write | Child network | Typical use |
| --- | --- | --- | --- | --- |
| `off` (default) | Unrestricted | Unrestricted | Unrestricted | No sandbox |
| `workspace` | Everywhere | CWD + `~/.grok/` + `/tmp` + `/var/tmp` (+ macOS temp dirs / `$TMPDIR`) | Allowed | Everyday development |
| `devbox` | Everywhere | All top-level dirs except `/data` (skips `/proc`, `/sys`, `/dev`) | Allowed | Disposable dev VMs |
| `read-only` | Everywhere | `~/.grok/` + temp dirs only | Blocked on Linux¹ | Exploration, review |
| `strict` | CWD + essential system paths | CWD + `~/.grok/` + temp dirs | Blocked on Linux¹ | Untrusted code |

¹ Child-network blocking uses seccomp on **Linux only**. On macOS it is a no-op for these profiles.

Aliases accepted by the profile parser: `readonly` → `read-only`; `none` → `off`.

### Profile notes

- **workspace** — Recommended default when enabling the sandbox. Full filesystem read; writes limited to the workspace, Grok home (`$GROK_HOME` or `~/.grok`), and temp paths. Network allowed for in-process tools and child commands.
- **devbox** — Reserved built-in for disposable VMs. `--sandbox devbox` always selects the built-in profile and **shadows** any `[profiles.devbox]` in `sandbox.toml`. On Linux, `/data` is write-denied via a bubblewrap remount (still readable). `/data` is not put into the kernel read-deny set, so customs that `extends = "devbox"` do not inherit a read-deny on `/data`.
- **read-only** — Read everything; write only Grok home + temp (session persistence still works). Child network restricted on Linux.
- **strict** — No default full-FS read. Read allowlist includes CWD plus present system paths such as `/usr`, `/lib`, `/bin`, `/etc`, `/tmp`, `/run`, `/var`, and macOS paths like `/System`, `/Library`, `/private`, and `~/Library` when they exist. Child network restricted on Linux.

Device files needed for normal tools (`/dev/null`, `/dev/urandom`, `/dev/tty`, PTY paths, etc.) are granted separately by the capability set builder.

## Select a profile

### CLI and environment

| Input | Identifier | Notes |
| --- | --- | --- |
| Flag | `--sandbox <PROFILE>` | Profile name for this process |
| Env | `GROK_SANDBOX` | Same values as the flag |
| Config | `[sandbox] profile` in effective `config.toml` | User/project config merge |
| Requirements | `requirements.toml` → `[sandbox] profile` | Highest precedence pin |
| Default | `off` | No enforcement |

Profile resolution order for a **new** session:

1. Managed/requirements pin (`[sandbox] profile` in requirements)
2. Explicit `--sandbox` / `GROK_SANDBOX` (including the profile restored for resume)
3. `[sandbox] profile` in config
4. `off`

Related permission UX: `[sandbox] auto_allow_bash` and `GROK_SANDBOX_AUTO_ALLOW_BASH` can auto-approve bash when the sandbox is active (default false). That is orthogonal to OS enforcement; see [Permissions and safety](/permissions-and-safety).

### Config example

```toml
# ~/.grok/config.toml (or project .grok/config.toml via effective merge)
[sandbox]
profile = "workspace"
auto_allow_bash = false
```

## Custom profiles

Define named profiles in:

- Global: `~/.grok/sandbox.toml`
- Project: `<workspace>/.grok/sandbox.toml`

```toml
[profiles.project]
extends = "workspace"
restrict_network = true
read_only = ["/data"]
read_write = ["/tmp/scratch"]
deny = ["/data/shared-secrets", "**/.env", "**/*.pem"]
```

```bash
grok --sandbox project
```

### Fields

| Field | Type | Description |
| --- | --- | --- |
| `extends` | string | Base built-in: `workspace`, `devbox`, `read-only`, `strict`. Defaults to `workspace`. Cannot extend `off`/`none` or another custom profile |
| `restrict_network` | bool | Block network for child processes (Linux seccomp; no-op on macOS) |
| `read_only` | string[] | Extra read-only paths |
| `read_write` | string[] | Extra read-write paths |
| `deny` | string[] | Kernel-enforced read + write/rename deny paths or globs |

### Naming and merge rules

- A custom name cannot replace a built-in. `--sandbox devbox` always runs the built-in `devbox`.
- Global and project files: project config may **add** new profile names only. If the same custom name exists globally, the **user/global definition wins**; the project entry is ignored (prevents a workspace from emptying a trusted `deny` list).
- When both define the same name with **different** content, Grok warns at startup (TUI welcome screen, or stderr in headless). Identical duplicates do not warn.

## Deny paths and globs

A non-empty custom `deny` list is **kernel-enforced**: denied paths cannot be read (via tools, `bash`, `grep`, or subagents) and cannot be renamed/relocated out of the deny set (`mv secret x && cat x` is closed).

- Entry is a **glob** if it contains `*`, `?`, or `[`. Those characters always mean glob.
- Supported gitignore-style subset: `*`, `?`, `**` as a whole segment, `[abc]` / `[a-z]`, negation with `!` or `^`.
- **Not** supported: brace alternation `{a,b}`, backslash escapes, unusual class forms. Unsupported or malformed globs cause Grok to **refuse to start** on both platforms.
- Relative globs are anchored at the workspace; absolute globs use their literal prefix. Non-glob entries use exact-path matching.

### Platform enforcement

| Platform | Mechanism | Glob behavior |
| --- | --- | --- |
| **macOS** | Seatbelt rules (read-deny + write sub-actions; `/private` firmlink aliases covered) | Globs become runtime regexes — files created **after** start still match |
| **Linux** | bubblewrap bind-over with mode-000 placeholders for read-deny; Landlock for allow grants | Globs expanded to files that **exist at launch** only; later matches are **not** covered |

On Linux, if bubblewrap is missing (or a deny path cannot be bound, or glob expansion exceeds caps — depth 64, 4096 matches, 200k entries walked), a profile that **requires** read-deny **refuses to start**. Built-in `devbox` (write-deny `/data` only) may fall back to Landlock with a warning when bwrap is unavailable.

## How enforcement works

```text
  Startup
     │
     ├─ resolve profile (requirements > CLI/env/resume > config > off)
     ├─ Linux: optional bwrap re-exec for deny /devbox /data
     └─ SandboxManager::apply → install (OnceLock, irreversible)
              │
              ├─ Landlock / Seatbelt CapabilitySet (FS)
              └─ RESTRICT_CHILD_NETWORK flag → seccomp in child pre_exec (Linux)
```

- Applied to the **whole process**, not per-command wrappers.
- Child FS restrictions inherit from the parent kernel policy.
- In-process HTTP (`web_search`, LLM API) is never blocked by child-network rules; the agent process needs network to function.
- On Linux with `restrict_network`: child commands such as `curl`, `wget`, and `npm install` fail with EPERM via seccomp (blocked syscalls include `connect`, `bind`, `sendto`, `sendmsg`, `listen`, `accept`, `accept4`).

If the platform cannot apply a **built-in** profile, Grok logs a warning and continues without enforcement. An explicitly requested **custom** profile that cannot be applied is fail-closed: process exit rather than run unsandboxed.

## Sessions and resume

The profile is stored with the session and is fixed for that session’s lifetime.

| Resume action | Result |
| --- | --- |
| Omit `--sandbox` | Restore saved profile |
| `--sandbox` matches saved | Allowed |
| `--sandbox` differs from saved | Error exit — start a new session to change profile |

Error shape (conceptually): cannot resume under profile `X` when the session was created with `Y`.

## Event logging

Events append to `~/.grok/sandbox-events.jsonl` (under Grok home), including:

- Profile applied (profile, workspace, platform, path sets, `restrict_network`)
- Apply failed
- Filesystem / network violations (when logged by tools)

## When to use which profile

| Situation | Profile / approach |
| --- | --- |
| Own project, basic write protection | `workspace` |
| Block `.env` / credentials on top of a base | Custom profile + `deny` |
| Review without writing project files | `read-only` |
| Untrusted third-party tree | `strict` |
| Disposable VM with broad writes but not `/data` | `devbox` |
| Need `npm install` / writes outside CWD / max flexibility | `off` |

Sandbox is one layer beside permission rules, hooks, and modes. OS isolation does not replace deny rules or `PreToolUse` hooks; combine them for defense in depth.

## Platform support

| Platform | Mechanism | Notes |
| --- | --- | --- |
| Linux | Landlock (+ bwrap for read-deny / devbox `/data`) | Kernel 5.13+ for Landlock; bubblewrap required for custom `deny` read-deny |
| macOS | Seatbelt | All versions; child network restrict is no-op |
| Windows / non-enforce builds | Not applied | Built without `enforce` or non-Unix: helpers compile; no kernel isolation |

## Related pages

<CardGroup>
  <Card title="Permissions and safety" href="/permissions-and-safety">
    Tool authorization pipeline, permission modes, and how sandbox interacts with allow/deny/ask rules.
  </Card>
  <Card title="Configure Grok" href="/configure-grok">
    Config precedence, feature flags, and inspecting resolved settings.
  </Card>
  <Card title="Configuration reference" href="/configuration-reference">
    Schema for `[sandbox]` and related keys in config.toml.
  </Card>
  <Card title="Headless mode" href="/headless-mode">
    Non-interactive runs where sandbox and tool allow/deny lists matter for CI.
  </Card>
  <Card title="CLI reference" href="/cli-reference">
    Shared runtime flags including `--sandbox` / `GROK_SANDBOX`.
  </Card>
</CardGroup>

---

## 17. Subagents and personas

> Spawn and constrain child agents: agent types, personas, capability modes, isolation/worktree behavior, and headless Agent(...) denylist entries.

- Page Markdown: https://grok-wiki.com/public/docs/xai-org-grok-build-90205de50458/pages/17-subagents-and-personas.md
- Generated: 2026-07-16T01:25:07.842Z

### Source Files

- `crates/codegen/xai-grok-pager/docs/user-guide/16-subagents.md`
- `crates/codegen/xai-grok-subagent-resolution/src/lib.rs`
- `crates/codegen/xai-grok-subagent-resolution/src/types.rs`
- `crates/codegen/xai-agent-lifecycle/src/lib.rs`
- `crates/codegen/xai-grok-agent/src`
- `crates/codegen/xai-grok-pager/docs/user-guide/14-headless-mode.md`

---
title: "Subagents and personas"
description: "Spawn and constrain child agents: agent types, personas, capability modes, isolation/worktree behavior, and headless Agent(...) denylist entries."
---

Grok Build spawns **subagents** as independent child sessions through the `task` / `spawn_subagent` tool (`TaskTool` in `xai-grok-tools`). Each child gets its own context window, a toolset from its agent type plus optional capability filtering, and an optional persona/role layer resolved by `xai-grok-subagent-resolution`. Nesting is hard-capped at depth 1 (`MAX_SUBAGENT_DEPTH = 1`): only the top-level session may spawn children. Subagents are enabled by default; disable with `GROK_SUBAGENTS=0` or `[subagents] enabled = false` in `config.toml`.

## Agents vs personas

| | **Agents** | **Personas** |
|---|---|---|
| **What they configure** | Whole session: model, tools, prompt mode, system prompt | Behavioral overlay on a subagent's prompt |
| **Scope** | Primary session or subagent | Subagents (roles/personas in config) |
| **How you set them** | Startup agent, or `.md` files under `.grok/agents/` (and compat paths) | `[subagents.personas]` in `config.toml` or `.toml` under `.grok/personas/` |
| **What they control** | Model, tool availability, prompt body, skills | Tone, output format, task focus, IO contracts |
| **Examples** | `general-purpose`, `explore`, `plan` | `researcher`, `concise` |

An agent defines the session itself. A persona shapes behavior within a child session. The spawn path always selects an agent type (`subagent_type`); resolution may layer a role and persona on top.

Manage both in the agents modal: `/config-agents` (alias `/agents`), or open Personas with `/personas`.

## Spawn surface: `task` / `spawn_subagent`

The model-facing tool is registered as `spawn_subagent` (Claude alias: `Agent` / `Task`). Input schema is `TaskToolInput` in `xai-tool-types`.

| Field | Type | Default | Notes |
| --- | --- | --- | --- |
| `prompt` | string | required | Full task for the child |
| `description` | string | required | Short label (3–5 words) |
| `subagent_type` | string | `general-purpose` | Built-ins: `general-purpose`, `explore`, `plan`; project agents may add names |
| `run_in_background` | bool | `true` | Return immediately with subagent ID; poll with `get_task_output` / `get_command_or_subagent_output` |
| `capability_mode` | enum | role default | `read-only` \| `read-write` \| `execute` \| `all` |
| `isolation` | enum | `none` | `none` \| `worktree` |
| `resume_from` | string | — | Continue a **completed** peer subagent by ID |
| `cwd` | string | parent cwd | Mutually exclusive with `isolation=worktree` when both would set a real directory |
| `model` | string | parent model | Soft-ignored when `resume_from` is set (source model is pinned) |

There is **no** model-facing `persona` parameter on `TaskToolInput`. Personas/roles apply through config resolution and harness overrides (`SubagentRuntimeOverrides.persona`), not as a free-form spawn argument from the parent model.

Background children survive parent-turn cancellation so results can be polled later. Blocking children report a summary back on the parent turn.

## Built-in agent types

`BuiltinAgentName::subagent_variants()` advertises three types to the Task tool:

| Type | Role |
| --- | --- |
| `general-purpose` | Default full-capability agent |
| `explore` | Codebase investigation; reads/searches/shell; no file edits by default toolset |
| `plan` | Architecture/implementation planning; same non-edit bias as explore |

Other built-ins (`grok-build`, `codex`, `opencode`, …) are top-level profiles resolvable by name but not listed as default subagent types.

### Discovery and shadowing

Agent definitions load from:

1. Project: `.grok/agents/`, `.claude/agents/` (walk cwd → repo root)
2. User: `~/.grok/agents/`, `~/.claude/agents/`
3. Bundled agents (lowest priority for non-built-in names)

Merge rules for the **callable subagent list**:

- Start with built-in `general-purpose` / `explore` / `plan`
- **Only project-level** agents may shadow a built-in name (e.g. project `.grok/agents/explore.md`)
- User-level or bundled files that reuse a built-in name are skipped so the visible list matches spawn resolution
- Spawn name lookup priority: **project > built-in > user > bundled**
- `[subagents.toggle]` can disable individual types

## Personas and roles

### Personas

A persona is a named behavioral overlay. Instructions are injected into the child conversation (persona/system-reminder layer) without changing the agent type or base tool catalog.

Define inline:

```toml
[subagents.personas.researcher]
instructions = "You are a thorough researcher. Always cite specific file paths."
description = "Deep investigator."
```

Or as one `.toml` file per persona. Discovery priority:

1. `.grok/personas/*.toml` (project)
2. `~/.grok/personas/*.toml` (user)
3. Bundled personas (lowest)

File stem = persona name. Inline `config.toml` personas win over files. Only `.toml` is discovered. Bundled personas are read-only in the UI; user/project personas are editable.

| Field | Purpose |
| --- | --- |
| `instructions` | Inline instruction text |
| `instructions_file` | Path loaded at spawn; merged after inline `instructions` |
| `description` | Catalog summary (else first paragraph of instructions) |
| `inputs` / `outputs` | Declared IO contract (`name`, `io_type` default `file`, `required`, `description`) |
| `model` | Model override when persona applies |
| `reasoning_effort` | Reasoning effort override |
| `default_isolation` | `none` or `worktree` |

Persona **file I/O** failures are fatal (spawn aborts). Missing persona name / empty instructions set `persona_error` without always short-circuiting other fields the same way. Role `prompt_file` load failures soft-degrade with a warning.

### Roles

Roles are named presets referenced as `subagent_type` (or config role keys):

```toml
[subagents.roles.researcher]
description = "Deep research agent"
default_capability_mode = "read-only"
model = "grok-build"
prompt_file = ".grok/prompts/researcher.md"
default_isolation = "none"
```

File discovery: `.grok/roles/*.toml` (and user-level counterparts). Inline config wins over files. Role prompts become a `<role-instructions>` block when `prompt_file` loads successfully.

### Effective override precedence

`resolve_effective_overrides` in `xai-grok-subagent-resolution` merges fields as:

1. Explicit spawn-time override (`SubagentRuntimeOverrides`)
2. Role default
3. Persona default
4. Parent inheritance / defaults downstream

Isolation uses the same cascade for the first three steps, then defaults to `SubagentIsolationMode::None` (shared workspace)—it does **not** inherit the parent's isolation mode.

## Capability modes

Optional coarse filter over tool **kinds** (`SubagentCapabilityMode::filter_tool_config`). Tools without a `kind` (for example many MCP tools built via bare IDs) are preserved. Mode `all` is a no-op filter.

| Mode | Read / search / web / LSP | File write/edit/delete/move | Shell execute | Notes |
| --- | --- | --- | --- | --- |
| `read-only` | Yes | No | No | Plan/enter-exit plan, memory get/search, skills, ask-user kept |
| `read-write` | Yes | Yes | No | Plus image/video gen kinds |
| `execute` | Yes | No | Yes | Shell/background bash kinds |
| `all` | Yes | Yes | Yes | Full set from agent definition |

Wire strings are kebab-case (`read-only`, `read-write`, `execute`, `all`) with common aliases accepted at parse time. If `capability_mode` is omitted, the child uses its agent type's toolset (and any role default).

After filtering, orphaned background-task tools (`get_task_output` / `kill_task`) are pruned when no Task tool or background-capable bash remains.

## Isolation: shared workspace vs worktree

| Mode | Behavior |
| --- | --- |
| `none` (default) | Child shares the parent workspace cwd |
| `worktree` | Child runs in an isolated git worktree; edits stay out of the parent tree until merged |

`cwd` and `isolation=worktree` are mutually exclusive when both would set a real directory. On `resume_from`, the child reuses the source's cwd/worktree (and may rehydrate from a completion snapshot ref if the worktree directory was removed). Completion output can include `worktree_path`. Worktree apply/merge is exposed through `x.ai/git/worktree/*` extension methods.

```text
Parent session (depth 0)
├── spawn_subagent ──► Child (depth 1)
│     tools: type + capability_mode
│     isolation: none | worktree
│     resume_from: optional peer ID
└── child cannot spawn further subagents
```

## Resume (`resume_from`)

Resume continues a **completed** peer in the same parent session:

- Inherits raw transcript, tool state, and model
- Re-renders system prompt / tools from the current agent definition
- Appends the new `prompt` as the next user message
- Requires matching `subagent_type`; persona must match if a persona is requested
- Model overrides on resume are soft-ignored (source model pinned)

Validation lives in `validate_resume_identity`. Invalid/sentinel IDs (`""`, `"null"`, `"none"`, `"undefined"`) are treated as absent.

## Depth limit

`SubagentDepthCounter` tracks nesting. When `depth >= MAX_SUBAGENT_DEPTH` (1), `task` fails with an invalid-arguments error: nested subagents cannot spawn further children. That keeps the tree flat (parent → children only).

## Configuration

```toml
[subagents]
enabled = true

[subagents.toggle]
explore = true
plan = false

[subagents.models]
explore = "grok-build"

[subagents.roles.researcher]
description = "Deep research agent"
default_capability_mode = "read-only"
model = "grok-build"
prompt_file = ".grok/prompts/researcher.md"

[subagents.personas.concise]
instructions = "Be concise. No filler words."
```

| Control | Effect |
| --- | --- |
| `GROK_SUBAGENTS=0` / `enabled = false` | Removes Task tool from the parent agent toolset |
| `[subagents.toggle]` | Disable specific types |
| `[subagents.models]` | Per-type model override (else inherit parent) |
| Roles / personas | Defaults for capability, model, isolation, prompts |

## Headless `Agent(...)` denylist

In headless mode (`-p` / `--single`, `--prompt-file`, `--prompt-json`), `--disallowed-tools` and `--tools` accept special **Agent** tokens (case-insensitive; `Task(...)` is an alias form in the same classifier):

| Entry | Effect |
| --- | --- |
| `Agent` | Block all subagent spawning (empty allowed type list; Task deps removed) |
| `Agent(explore)` | Deny the `explore` type |
| `Agent(explore, plan)` | Deny multiple types |

```bash
# No subagents at all
grok -p "Fix this bug" --disallowed-tools "Agent"

# Block only explore
grok -p "Refactor this module" --disallowed-tools "Agent(explore)"
```

Tokenizer keeps `Agent(a, b)` as a single token so commas inside parentheses do not split types. On agent definitions, the same grammar appears in frontmatter `tools` / `disallowedTools` and becomes `allowed_subagent_types`. Bare `Agent` deny forces an empty allow list and strips `task`, `get_task_output`, `kill_task`, and `wait_tasks`. When both allowlist and denylist apply, denylist wins for tool IDs; Agent type filtering intersects allow and deny type sets.

`--tools` / `--disallowed-tools` are headless-only flags (ignored with a warning in the interactive TUI).

## TUI surfaces

| Surface | Binding / action |
| --- | --- |
| Tasks pane | `Ctrl+B` — active/completed subagents and background commands |
| Todo pane | `Ctrl+T` |
| Agents modal | `Ctrl+P` → Manage Agents, or `/config-agents` / `/personas` |
| Parent scrollback | Lifecycle blocks: running / started / completed / failed / cancelled |
| Open child | Enter / Ctrl-F on a block, or from tasks pane — framed observational transcript |

Child views are largely observational (no normal top-level prompt like the parent). Close with `q`, Esc, or the frame close control.

## Operational constraints

- **Depth:** max 1; recursive spawn errors out
- **Background default:** `run_in_background` defaults to `true` on the tool schema
- **Cancellation:** background children outlive parent-turn cancel; foreground children tied to `parent_prompt_id` are cancelled with that turn
- **Provider neutrality:** agent definitions, personas, and roles are file/config portable; model IDs are free-form slugs (parent, role, persona, or explicit `model`), not tied to a single host provider
- **Good uses:** parallel research, tests, review of independent work
- **Poor uses:** trivial one-shot work, tight interactive user dialog, tasks where setup cost exceeds parallelism benefit

## Related pages

<CardGroup>
  <Card title="Headless mode" href="/headless-mode">
    `-p` runs, tool allow/deny lists, and `Agent(...)` denylist entries.
  </Card>
  <Card title="Permissions and safety" href="/permissions-and-safety">
    PreToolUse, allow/deny rules, and permission modes that still gate child tools.
  </Card>
  <Card title="Sandbox profiles" href="/sandbox">
    OS-level isolation for filesystem/network, orthogonal to subagent worktrees.
  </Card>
  <Card title="Plan mode" href="/plan-mode">
    Structured planning before edits; plan subagent type and enter/exit plan tools.
  </Card>
  <Card title="Configuration reference" href="/configuration-reference">
    `[subagents]` keys and related config.toml schema.
  </Card>
  <Card title="Sessions" href="/sessions">
    Session lifecycle, resume/continue, and persistence for parent and child IDs.
  </Card>
</CardGroup>

---

## 18. Plan mode

> Structured planning before edits: plan files, approval gates, mode entry/exit from the TUI, and how plan mode interacts with tools and permissions.

- Page Markdown: https://grok-wiki.com/public/docs/xai-org-grok-build-90205de50458/pages/18-plan-mode.md
- Generated: 2026-07-16T01:25:59.634Z

### Source Files

- `crates/codegen/xai-grok-pager/docs/user-guide/19-plan-mode.md`
- `crates/codegen/xai-grok-shell/src/session/goal_planner.rs`
- `crates/codegen/xai-grok-shell/src/session/goal_orchestrator.rs`
- `crates/codegen/xai-grok-shell/src/session/goal_strategist.rs`
- `crates/codegen/xai-grok-pager/docs/user-guide/22-permissions-and-safety.md`
- `crates/codegen/xai-grok-pager/src/slash/commands/mod.rs`

---
title: "Plan mode"
description: "Structured planning before edits: plan files, approval gates, mode entry/exit from the TUI, and how plan mode interacts with tools and permissions."
---

Plan mode is a session-scoped lifecycle enforced by the shell’s `PlanModeTracker` and surfaced in the TUI as an ACP session mode (`plan`). While **Active**, the agent explores and writes only the session plan file (`plan.md`); edit tools targeting any other path are rejected before execution—even under always-approve. Entry is either user-driven (`/plan`, Shift+Tab) or agent-driven (`enter_plan_mode`); exit for implementation goes through `exit_plan_mode` and a scrollable approval surface (`a` / `s` / `c` / `q`).

## What plan mode does

When plan mode is **Active**, the agent:

1. Reads and searches the codebase (and may use other non-edit tools subject to normal permission rules).
2. Writes and revises an implementation plan in the session plan file.
3. May call `ask_user_question` to clarify requirements.
4. Calls `exit_plan_mode` so you can approve, request changes, or abandon the plan.

Separating planning from implementation lets you correct the approach before any product code is written. The model is steered with injected system reminders (full / sparse alternation, reentry, and exit variants) that name the plan path and the only allowed turn-ending tools for planning.

## Enter plan mode

### User-initiated

| Surface | Behavior |
| ------- | -------- |
| **`/plan`** | Turns plan mode on (Pending until the next prompt). |
| **`/plan <description>`** | Turns plan mode on and starts a turn with that description after the mode switch completes (`SetModeThenPrompt`). |
| **Shift+Tab** | Cycles session mode: Normal → Plan → Always-approve → Normal. From Normal, one press lands on Plan. |
| **Dashboard** | `/plan` is also offered without a session so the *next* spawned agent can start in plan mode. |

If you are already in plan mode, `/plan` is idempotent (toast: use `/view-plan` for the saved plan). After a plan exists, **`/view-plan`** (aliases `/show-plan`, `/plan-view`) reopens the saved preview or parked approval UI.

### Agent-initiated

When the agent judges the task ambiguous enough to warrant planning, it calls **`enter_plan_mode`** (empty input). That tool:

- Requires **user approval** before it runs. Decline → result `"User declined to enter plan mode."` and normal mode continues.
- Emits a `PlanModeEntered` notification and activates plan mode **directly** (skips `Pending`).
- Seeds an empty session `plan.md` if missing; **never truncates** existing non-empty content.
- Returns path + tool-name hints (`ask_user_question`, `exit_plan_mode`, optional task/delegate name).

`enter_plan_mode` and `exit_plan_mode` are registered as a pair; the tool registry refuses to load one without the other.

### When plan mode fits

**Use plan mode for:** architectural ambiguity (multiple reasonable approaches), unclear requirements that need exploration first, high-impact restructuring where the wrong approach is expensive.

**Skip plan mode for:** clear one-path changes, obvious bug fixes once understood, features that follow existing conventions, trivial renames/formatting/tests, and pure research (prefer read-only or explore subagents instead).

## The plan file

Canonical location:

```text
~/.grok/sessions/<encoded-cwd>/<session-id>/plan.md
```

`<encoded-cwd>` is a directory-name encoding of the workspace path, not the literal path. The tracker always recomputes this path from session metadata; it is not stored inside the plan-mode snapshot.

Expected plan content (model guidance):

- **Context** — why the change is needed
- Recommended approach (not an exhaustive alternative dump)
- Critical file paths to modify
- Existing functions/utilities to reuse (with paths)
- Verification — how to test end to end

Path resolution for the tools prefers an explicit session `PlanFilePath` resource; without it, fallbacks include `<cwd>/.grok/plan.md` (and related display defaults). Runtime sessions use the session-directory `plan.md` above.

## Approval gate (`exit_plan_mode`)

When planning is done, the agent calls **`exit_plan_mode`** (empty input). The tool **reads the plan from disk** (it does not accept plan body as arguments), so the UI shows the same bytes on disk as the model wrote. It emits `PlanModeExited` with plan content (or empty) and parks an ACP reverse-request until you decide.

### Approval UI

The TUI opens a scrollable plan preview with an action bar:

| Key | Action |
| --- | ------ |
| `a` | **Approve** and start building. With pending line comments, the label becomes approve-with-comments and those comments ride with the approval. |
| `s` | **Request changes** — focus the prompt; type notes and press Enter. Plan mode stays active for revision. |
| `c` | **Comment** on the selected line or line range (or Enter on a line). |
| `q` | **Quit plan** — abandon without approving; turn plan mode off. |
| `Tab` | Toggle focus between preview and freeform prompt. |
| `Esc` | From the prompt, return focus to the preview. |

Focus states: **Preview**, **Commenting**, **Prompt**.

### Empty or missing plan

If `plan.md` is missing, empty, or whitespace-only, approval still opens with a clear empty-state body (placeholder starts with “No plan written yet”). You can still approve (leave plan mode and implement), request changes (back to planning), or quit. Status line distinguishes “Waiting on plan approval” vs “No plan written — approve or request changes”. In minimal mode the empty notice is committed into scrollback and the controls strip header reads **No plan written yet**.

### Wire outcomes

Shell-side outcomes map from the ext response string:

| Outcome | Effect |
| ------- | ------ |
| `approved` | Leave plan mode; implement. |
| `abandoned` | Quit path; plan mode off. |
| anything else (incl. cancel / disconnect) | Fail closed: stay in plan mode (no silent auto-approve). |

If the reverse-request cannot be delivered at all (no interactive client — e.g. some headless/SDK setups), behavior follows the ACP channel failure path; delivery-then-drop keeps approval pending rather than auto-approving.

### Resume

`awaiting_plan_approval` is persisted. On session resume, the shell can re-park `exit_plan_mode` so approval chrome returns instead of treating every Active + `plan.md` session as “still planning.”

## Lifecycle state machine

`PlanModeTracker` owns four states:

| State | Meaning |
| ----- | ------- |
| `Inactive` | Normal operation; no plan-mode edit constraints. |
| `Pending` | Client toggled plan mode on; model not yet instructed (no prompt / no tool entry yet). |
| `Active` | Planning; plan-file edits allowed, other edits rejected. |
| `ExitPending` | User toggled plan mode off while a turn is in flight; exit completes when the turn ends. |

```text
Inactive    --> Active      (enter_plan_mode approved — skips Pending)
Inactive    --> Pending     (/plan or Shift+Tab on)
Pending     --> Active      (first user prompt, or mid-turn activation)
Active      --> Inactive    (exit approved, or toggle off while idle)
Active      --> ExitPending (toggle off mid-turn)
ExitPending --> Inactive    (turn completes)
ExitPending --> Active      (toggle plan mode back on before turn ends)
```

**Persistence:** snapshot is written as `plan_mode.json` under the session directory. On restore, transient states collapse: `Pending` → `Inactive`, `ExitPending` → `Inactive` with a pending exit reminder. `awaiting_plan_approval` is retained so parked approval survives restart.

**Mid-turn toggle on:** activation can buffer a system-reminder until a safe drain point; toggle off before delivery rolls activation back so the model is not told it exited a mode it never saw.

**Status flag:** TUI shows `plan` while plan mode is active. Always-approve (if armed underneath) reappears in the status chrome after plan mode exits.

## Edits, tools, and permissions

Plan mode’s edit restriction is **not** the same as the permission-mode enum value `plan` (that value is accepted for Claude-compat config but does not itself implement this lifecycle). Enforcement is a shell **`plan_mode_edit_gate`** that runs for edit-class tool calls while the tracker is Active.

### Rules

- **Grok edit tools** (`AccessKind::Edit`): only the exact plan file path auto-approves and is allowed. Any other path is rejected with a short model-facing message naming the plan file as the only editable path.
- **Compat Write/StrReplace:** any **markdown** path (`.md`, `.markdown`, `.mdown`, `.mkd`, `.mkdn`, `.mdx`) is allowed for plan-doc style writing; non-markdown is rejected. **Delete** is not on that carve-out—it stays plan-file-only.
- **`apply_patch`:** always rejected in plan mode (targets are not known until after parse).
- **Non-edit tools** (bash, read, grep, MCP, web, `enter_plan_mode` / `exit_plan_mode` as read-scoped): not blocked by the plan gate; they use the normal permission pipeline.
- **Bash is not scanned for redirects:** plan mode blocks edit tools, not shell side-effects.

### Always-approve / YOLO

Always-approve remains “armed” underneath plan mode. YOLO does not know about plan mode; the plan edit gate still rejects non-plan-file edits. Non-edit tools may still auto-run under always-approve. After you approve exiting plan mode, always-approve resumes for implementation if it was enabled.

### Hooks and deny rules

`PreToolUse` hooks and configured deny/ask/allow rules still apply in the usual order for tools that reach the permission path. Plan-mode rejections for non-plan edits happen as a dedicated gate before the agent can mutate the tree through edit tools.

### Subagents

Subagents are **not** covered by the parent’s plan-mode edit gate. Each child starts with a fresh tracker (`Inactive`), so a write-capable type such as `general-purpose` can edit files while the parent is still planning, and it inherits the parent’s permission mode (including always-approve). Read-only types (e.g. `explore`) stay limited by their own toolset.

## Compaction and reminders

- **`/compact` during Active plan mode:** plan-mode state is preserved. Compaction context includes a reminder that plan mode is still active so the agent continues planning after compact. The reminder alternation counter resets so the next injection is the full template.
- **Reminders:** full template includes plan path and tool names; sparse template is a short “still in plan mode” line; reentry and exit templates cover re-toggle and leave-plan paths.

## Tools and notifications (integration)

| Piece | Role |
| ----- | ---- |
| `enter_plan_mode` | Agent entry; seeds plan file; `PlanModeEntered`. |
| `exit_plan_mode` | Present plan; `PlanModeExited`; parks approval. |
| `ask_user_question` | Clarifications while planning. |
| ACP session mode `plan` | Client ↔ shell mode sync (`SetSessionMode` / `CurrentModeUpdate`). |
| `plan_mode.json` | Lifecycle snapshot + `awaiting_plan_approval`. |
| `plan.md` | Authoritative plan body for approval UI. |

Claude-compat aliases `EnterPlanMode` / `ExitPlanMode` match the same tools for allowlist/compat surfaces.

## Related pages

<CardGroup cols={2}>
  <Card title="Permissions and safety" href="/permissions-and-safety">
    Allow/deny/ask rules, always-approve, hooks, and how they layer with plan-mode edit gates.
  </Card>
  <Card title="Slash commands" href="/slash-commands">
    `/plan`, `/view-plan`, and other interactive session commands.
  </Card>
  <Card title="Keyboard shortcuts" href="/keyboard-shortcuts">
    Shift+Tab mode cycle and TUI focus behavior.
  </Card>
  <Card title="Sessions" href="/sessions">
    Session directories, resume, compact, and persistence layout that host `plan.md`.
  </Card>
  <Card title="Subagents and personas" href="/subagents">
    Child agents and why they do not inherit the parent plan-mode edit gate.
  </Card>
  <Card title="Agent mode (ACP)" href="/agent-mode">
    Session modes, reverse-requests, and client surfaces that host approval.
  </Card>
</CardGroup>

## Next

<CardGroup cols={2}>
  <Card title="Permissions and safety" href="/permissions-and-safety">
    Tune deny/ask rules and always-approve for post-approval implementation.
  </Card>
  <Card title="Headless mode" href="/headless-mode">
    Non-interactive runs: plan approval needs a client; headless paths fail closed when no interactive approval channel exists.
  </Card>
</CardGroup>

---

## 19. Theming

> TUI appearance: /theme, pager.toml theme keys, color-support detection, screen modes (fullscreen/minimal), and terminal capability constraints.

- Page Markdown: https://grok-wiki.com/public/docs/xai-org-grok-build-90205de50458/pages/19-theming.md
- Generated: 2026-07-16T01:27:29.484Z

### Source Files

- `crates/codegen/xai-grok-pager/docs/user-guide/06-theming.md`
- `crates/codegen/xai-grok-pager/src/settings`
- `crates/codegen/xai-grok-pager-render/src`
- `crates/codegen/xai-grok-pager/src/diagnostics.rs`
- `crates/codegen/xai-grok-pager/docs/user-guide/21-terminal-support.md`
- `crates/codegen/xai-grok-pager/docs/user-guide/05-configuration.md`

---
title: "Theming"
description: "TUI appearance: /theme, pager.toml theme keys, color-support detection, screen modes (fullscreen/minimal), and terminal capability constraints."
---

Grok Build paints the interactive TUI from a central `Theme` palette in the pager render crate. Theme selection is stored under `[ui]` in `~/.grok/config.toml`, layout and chrome knobs live in `~/.grok/pager.toml`, and at startup every RGB slot is quantized to the terminal’s detected color level (`truecolor` / `256` / `basic` / `none`). Minimal mode bypasses that palette entirely and locks a terminal-native ANSI set so chrome stays readable on any light or dark profile.

## Built-in themes

| Theme | Canonical name | Aliases | Truecolor required |
|-------|----------------|---------|--------------------|
| Grok Night | `groknight` | `grok-night`, `dark` | No |
| Grok Day | `grokday` | `grok-day`, `light`, `day` | No |
| Tokyo Night | `tokyonight` | `tokyo-night`, `tokyo` | Yes |
| Rose Pine Moon | `rosepine-moon` | `rosepine`, `rose-pine`, `rose-pine-moon` | Yes |
| Oscura Midnight | `oscura-midnight` | `oscura` | Yes |
| Auto (meta) | `auto` | `system` | No (resolved to a concrete theme) |

Names are case-insensitive. Default when `[ui].theme` is unset is **Grok Night** (`groknight`).

Truecolor-only themes are hidden from the `/theme` picker on non-truecolor terminals. Applying one by name still works: the live visual is clamped (typically to Grok Night) so the UI does not render a broken palette.

## Switch themes

### `/theme` (alias `/t`)

| Input | Behavior |
|-------|----------|
| `/theme` | Cycle to the next theme in `ThemeKind::available()` for this terminal |
| `/theme tokyonight` | Set a named theme (aliases accepted; stored as the canonical name) |
| `/theme auto` | Enable system-appearance mode |

Arrowing through the picker previews colors in real time without writing disk; Enter commits (`Action::SetTheme`), Escape restores the previous kind. `/theme` is **not available in minimal mode**.

### `config.toml`

```toml
[ui]
theme = "tokyonight"
auto_dark_theme = "tokyonight"   # used only when theme = "auto"
auto_light_theme = "grokday"
compact_mode = false
screen_mode = "fullscreen"       # sticky: "minimal" | "fullscreen"
```

### Settings pane

`/settings` (alias `/config`) → **Appearance** exposes Theme, Auto dark theme, Auto light theme, and Compact mode. Theme rows are hidden in minimal mode (`hidden_in_minimal: true`); compact mode remains available.

## Auto theme (system appearance)

With `theme = "auto"` (or `system`):

| Appearance | Default concrete theme | Override key |
|------------|------------------------|--------------|
| Dark | `groknight` | `[ui].auto_dark_theme` |
| Light | `grokday` | `[ui].auto_light_theme` |

Detection order:

1. Desktop APIs (`dark-light`): macOS `AppleInterfaceStyle`, Linux XDG portal `org.freedesktop.appearance.color-scheme`, Windows personalization registry  
2. **Startup only:** OSC 11 terminal background query (SSH / headless when desktop APIs return unspecified)  
3. No change if both fail  

While running, appearance is polled every **5 seconds** (desktop APIs only — not OSC 11). Live switches re-resolve the concrete theme and recolor the TUI without a restart. `auto_dark_theme` / `auto_light_theme` reject the value `auto` (circular) and fall back to their defaults.

## Color support detection

Color level is detected once and cached:

| Level (`as_str`) | Meaning |
|------------------|---------|
| `truecolor` | 24-bit RGB (SGR 38;2) |
| `256` | Indexed palette (SGR 38;5) |
| `basic` | 16-color ANSI |
| `none` | Monochrome (`NO_COLOR` set, or no color capability) |

**Detection inputs:**

- `NO_COLOR` → force `none`  
- `supports-color` on stdout (`COLORTERM`, `TERM`, terminal-specific env)  
- Non-TTY / piped stdout without `NO_COLOR` → assume `truecolor` (safe for tests)  
- If the crate reports only 256-color but the brand is known truecolor (iTerm2, Ghostty, Kitty, WezTerm, Alacritty, Rio, Warp, VS Code, Windows Terminal, foot) or native Windows ConHost → upgrade to `truecolor` (covers tmux/SSH stripping `COLORTERM`)

Every theme is defined in full RGB. `Theme::current()` quantizes all slots to the detected level; runtime colors (syntax highlight, blends) go through the same `quantize` path. On `basic` (and legacy Windows ConHost below truecolor), chrome gets extra ANSI-16 overrides so dark RGB does not collapse onto a single black slot.

**Enable truecolor (common case):**

```bash
export COLORTERM=truecolor
```

```tmux
# ~/.tmux.conf
set -g default-terminal "tmux-256color"
set -as terminal-features ",*:RGB"
```

Verify inside Grok with `/terminal-setup` (aliases `/terminal-check`, `/terminal-info`): rows `color` and `themes` mirror the detector and `ThemeKind::available()`. Limited color produces an issues note there (and is not spammed on the welcome banner). Apple Terminal is inherently 256-color — truecolor themes stay unavailable.

## Screen modes

Two orthogonal controls: **sticky render mode** (fullscreen TUI vs minimal) and **alt-screen policy** (fullscreen buffer vs inline in the main scrollback).

### Sticky mode: fullscreen vs minimal

| Surface | Fullscreen TUI | Minimal (scrollback-native) |
|---------|----------------|-----------------------------|
| CLI | `grok` / `grok --fullscreen` | `grok --minimal` |
| Slash | `/fullscreen` (alias `/full`) | `/minimal` |
| Config | `[ui] screen_mode = "fullscreen"` | `[ui] screen_mode = "minimal"` |

`/minimal` and `/fullscreen` re-exec the same session with `--resume <id>` and the chosen flag, and write the sticky `[ui].screen_mode`. A CLI flag wins for that invocation and updates the sticky value. Unset `screen_mode` keeps legacy resolution: pager.toml `[terminal] minimal`, then alt-screen policy. `screen_mode` takes precedence over the legacy `[terminal] minimal` key.

**Minimal mode and theming:**

- Locks a fixed terminal-native palette (`Theme::terminal_default()`): body uses `Color::Reset` (terminal default fg/bg); sparse named ANSI-16 accents  
- Ignores `[ui].theme` / `/theme` / theme rows in `/settings` for live paint  
- Avoids dark-on-dark / light-on-light when OS appearance disagrees with the terminal profile  

### Alt-screen policy (fullscreen vs inline)

In `~/.grok/pager.toml`:

```toml
[terminal]
alt_screen = "auto"    # "auto" | "always" | "never"
```

| Value | Behavior |
|-------|----------|
| `auto` (default) | Alt-screen in plain terminals and normal tmux; **inline** in Zellij and tmux control mode |
| `always` | Always enter the alternate screen |
| `never` | Always inline in the main scrollback |

CLI override: `--no-alt-screen` always forces inline (highest precedence). Sticky `screen_mode = "fullscreen"` still respects this policy — Zellij/control-mode keep their automatic inline fallback unless you set `always`.

```text
                    ┌─────────────────────────┐
  --minimal / --fullscreen / [ui].screen_mode │
                    └───────────┬─────────────┘
                                │
              ┌─────────────────┴─────────────────┐
              ▼                                   ▼
        Minimal mode                      Fullscreen TUI path
   terminal_default palette          ThemeKind + color quantize
   /theme unavailable                /theme + auto appearance
                                              │
                                   [terminal].alt_screen
                                   / --no-alt-screen
                                              │
                              ┌───────────────┼───────────────┐
                              ▼               ▼               ▼
                           alt-screen      inline         auto:
                           buffer       main scrollback   Zellij / tmux-CC
                                                          → inline
```

## Compact mode

`/compact-mode` toggles denser layout (less outer and prompt padding). Persisted as `[ui].compact_mode` in `config.toml` (not in `pager.toml`). Auto-enabled while the terminal is **20 rows or shorter**.

## Cursor color

On theme apply, the pager sets the terminal cursor to the theme’s `accent_user` via **OSC 12** (`\x1b]12;rgb:RR/GG/BB\x07`). On exit it restores the profile default with **OSC 112**. Under `NO_COLOR`, OSC 12 is skipped. Minimal’s terminal-native palette does not drive a custom cursor color the same way.

## Syntax highlighting

Bundled TextMate themes (embedded in the binary; not user-replaceable):

| Active theme kind | `.tmTheme` asset |
|-------------------|------------------|
| GrokNight, RosePineMoon, OscuraMidnight, Auto (fallback path) | `grok-night.tmTheme` |
| TokyoNight | `tokyo-night.tmTheme` |
| GrokDay | `grok-day.tmTheme` |

Highlight colors are quantized through the same color-support pipeline as UI chrome.

## `pager.toml` appearance keys

Path: `~/.grok/pager.toml` (under `$GROK_HOME` when set). Overrides layout, scroll, block chrome, animation, and terminal policy. Theme **name** is not set here — use `[ui].theme` in `config.toml`. Specify only overrides; defaults apply for omitted keys.

### Terminal

```toml
[terminal]
alt_screen = "auto"
# minimal = false              # legacy; prefer [ui].screen_mode
# minimal_live_rows = 10
# minimal_max_commit_rows = 2000
```

### Layout, scrollbar, scroll, display

```toml
[scrollback.layout]
outer_vpad = 1
outer_hpad_left = 2
outer_hpad_right = 2
block_pad_left = 2
block_pad_right = 2

[scrollback.scrollbar]
enabled = true
gap_left = 0
gap_right = 0
# scrollbar_bg = "none"
# scrollbar_fg = "none"

[scrollback.scroll]
margin = 0
min_page_fraction = 0
follow_indicator = "center"   # "center" | "none"
follow_auto_select = true
follow_by_overscroll = true
anchor_on_fold = true
respect_manual_folds = false  # opt-in pin for manual folds while streaming

[scrollback.display]
sticky_headers = true
tab_width = 4
expandable_indicator = true
expandable_indicator_char = "›"
collapsed_accent_char = "❙"
dim_accent = 0.5
line_under_last_entry = false
selection_buttons = false
```

### Animation, prompt, todos

```toml
[animation]
fps = 30          # 1–60
wave_rows = 32

[prompt]
collapse_unfocused = true
mouse_hover = true
show_prefix = true

[todo]
badge_format = "default"   # "default" | "colon" | "comma"
```

### Block styling (representative)

```toml
[scrollback.blocks.edit]
indent = true
vpad = false
# expanded_by_default = true   # unset → follows [ui].collapsed_edit_blocks
hunk_separator = "…"
dual_line_numbers = false

[scrollback.blocks.thinking]
accent_enabled = true
animate = true
truncate_lines = 3
bg_blend = 70
header = true
header_bright = false

[scrollback.blocks.tool]
muted_collapsed = true
dim_details = true
bullet = "diamond"   # none | dot | small-circle | circle | small-triangle | triangle | diamond

[scrollback.blocks.execute]
first_lines = 2
last_lines = 3
accent_enabled = true
header_style = "label"   # "shell" | "label"
muted_command_collapsed = true

[scrollback.blocks.prompt]
vpad = true
bg = "light"             # "none" | "light" | "dark"
show_prefix = true
min_lines = 2
```

Optional top-level `disable_plugins = true` hides plugin/hooks UI chrome.

## Theme color slots

Each concrete theme fills a fixed set of roles consumed by scrollback blocks, chrome, markdown, and diffs. The theme system owns quantization; callers should not hard-code RGB outside `Theme`.

| Group | Slots |
|-------|--------|
| Backgrounds | `bg_base`, `bg_light`, `bg_dark`, `bg_highlight`, `bg_hover`, `bg_terminal`, `bg_visual` |
| Accents | `accent_user`, `accent_assistant`, `accent_thinking`, `accent_tool`, `accent_system`, `accent_error`, `accent_success`, `accent_running`, `accent_skill`, `accent_plan`, `accent_verify`, `accent_feedback`, `accent_remember`, `accent_model` |
| Text / gray | `text_primary`, `text_secondary`, `gray_dim`, `gray`, `gray_bright` |
| Semantic | `command`, `path`, `running`, `warning`, `fuzzy_accent` |
| Borders / scrollbar | `selection_border`, `hover_border`, `prompt_border`, `prompt_border_active`, `scrollbar_bg`, `scrollbar_fg` |
| Paste | `paste_bg`, `paste_fg`, `paste_dim` |
| Diff | `diff_delete_bg`, `diff_delete_fg`, `diff_insert_bg`, `diff_insert_fg`, `diff_equal_fg`, `diff_gutter_fg` |
| Markdown | `md_heading_h1`–`md_heading_h6`, `md_code`, `md_code_bg`, `md_text`, `md_muted`, `md_task_checked`, `md_task_unchecked`, `link_fg` |

## Theme resolution flow

```mermaid
flowchart TD
  subgraph config [Config surfaces]
    CT["~/.grok/config.toml [ui].theme"]
    AD["auto_dark_theme / auto_light_theme"]
    PT["~/.grok/pager.toml layout / alt_screen"]
  end

  subgraph runtime [Runtime]
    TK["ThemeKind parse + aliases"]
    SA["System appearance poll 5s"]
    AV["ThemeKind::available()"]
    CL["color_support detect OnceLock"]
    Q["Theme::quantized + runtime quantize"]
    TN["terminal_native lock → terminal_default"]
  end

  CT --> TK
  AD --> SA
  TK -->|auto| SA
  SA --> TK
  TK --> AV
  AV -->|truecolor filter| Q
  CL --> Q
  TN -->|minimal| Q
  PT --> UI[Scrollback / animation / alt-screen paint]
  Q --> UI
```

## Diagnostics and constraints

| Signal | What to check |
|--------|----------------|
| `/terminal-setup` `color` row | `truecolor` / `256` / `basic` / `none` |
| `/terminal-setup` `themes` row | `all` or `2/5: groknight, grokday` (example when truecolor missing) |
| Washed-out / wrong colors | `COLORTERM`, tmux `RGB` / `default-terminal` |
| Truecolor themes missing from picker | Expected below truecolor; Apple Terminal cannot unlock them |
| Fullscreen not entering | Zellij, tmux control mode, `alt_screen = "never"`, or `--no-alt-screen` |
| Theme slash ignored | Minimal mode — switch with `/fullscreen` first |
| Monochrome UI | `NO_COLOR` set |

Broader terminal capability issues (clipboard OSC 52, mouse, Kitty keyboard) are covered on the troubleshooting page; theming-specific color notes live in `/terminal-setup`’s limited-color warnings.

## Config key summary

| Key | File | Default / notes |
|-----|------|-----------------|
| `[ui].theme` | `config.toml` | unset → `groknight`; `auto` / concrete name |
| `[ui].auto_dark_theme` | `config.toml` | unset → `groknight` |
| `[ui].auto_light_theme` | `config.toml` | unset → `grokday` |
| `[ui].compact_mode` | `config.toml` | `false`; auto on ≤20 rows |
| `[ui].screen_mode` | `config.toml` | unset = legacy; sticky `"minimal"` \| `"fullscreen"` |
| `[terminal].alt_screen` | `pager.toml` | `"auto"` |
| `[terminal].minimal` | `pager.toml` | legacy; prefer `screen_mode` |
| `NO_COLOR` | env | disables color |
| `COLORTERM=truecolor` | env | advertises 24-bit RGB |
| `--minimal` / `--fullscreen` | CLI | sticky mode + relaunch |
| `--no-alt-screen` | CLI | force inline |

## Related pages

<CardGroup>
  <Card title="Configure Grok" href="/configure-grok">
    config.toml / pager.toml precedence, feature flags, and `grok inspect`.
  </Card>
  <Card title="Configuration reference" href="/configuration-reference">
    Schema-oriented keys for `[ui]`, pager sections, and env counterparts.
  </Card>
  <Card title="Slash commands" href="/slash-commands">
    `/theme`, `/settings`, `/compact-mode`, `/minimal`, `/fullscreen`, `/terminal-setup`.
  </Card>
  <Card title="Troubleshooting" href="/troubleshooting">
    Truecolor, tmux, SSH, OSC 52, and other terminal capability failures.
  </Card>
  <Card title="Keyboard shortcuts" href="/keyboard-shortcuts">
    Focus, cancel, and navigation that interact with the themed TUI chrome.
  </Card>
  <Card title="Quickstart" href="/quickstart">
    First interactive session and reading tool output in scrollback.
  </Card>
</CardGroup>

---

## 20. CLI reference

> Top-level grok commands and flags: default TUI, agent, login/logout, mcp, plugin, memory, models, sessions, inspect, setup, update, wrap, export, trace; shared runtime flags.

- Page Markdown: https://grok-wiki.com/public/docs/xai-org-grok-build-90205de50458/pages/20-cli-reference.md
- Generated: 2026-07-16T01:28:31.538Z

### Source Files

- `crates/codegen/xai-grok-pager/src/app/cli.rs`
- `crates/codegen/xai-grok-pager/src/headless.rs`
- `crates/codegen/xai-grok-pager/src/mcp_cmd.rs`
- `crates/codegen/xai-grok-pager/src/plugin_cmd.rs`
- `crates/codegen/xai-grok-pager/src/sessions_cmd.rs`
- `crates/codegen/xai-grok-pager/src/memory_cmd.rs`
- `crates/codegen/xai-grok-pager/docs/user-guide/14-headless-mode.md`
- `crates/codegen/xai-grok-pager-bin/src/main.rs`

---
title: "CLI reference"
description: "Top-level grok commands and flags: default TUI, agent, login/logout, mcp, plugin, memory, models, sessions, inspect, setup, update, wrap, export, trace; shared runtime flags."
---

The shipped `grok` binary is the `xai-grok-pager-bin` entrypoint. Clap parses a single top-level `PagerArgs` tree: no subcommand opens the interactive TUI (optional positional prompt), `-p` / `--prompt-json` / `--prompt-file` enter headless single-turn mode, and named subcommands (`agent`, `login`, `mcp`, `plugin`, `memory`, `models`, `sessions`, `inspect`, `setup`, `update`, `wrap`, `export`, `trace`, and peers) dispatch before the TUI. Shared session, model, permission, sandbox, and debug flags live on `PagerArgs` and apply to interactive and headless paths where noted.

```bash
grok [OPTIONS] [PROMPT]
grok [OPTIONS] <COMMAND>
grok -p "prompt" [OPTIONS]
```

## Invocation modes

| Mode | How you enter it | Behavior |
| --- | --- | --- |
| Interactive TUI | `grok` or `grok "initial prompt"` | Fullscreen (or minimal) pager; tools, slash commands, scrollback |
| Headless | `-p` / `--single`, `--prompt-json`, or `--prompt-file` | One prompt path; stdout formats `plain` / `json` / `streaming-json`; process exits |
| Subcommand | `grok <command> ...` | Auth, config, MCP, plugins, sessions, updates, export, etc. |
| ACP agent | `grok agent stdio` (and related modes) | Agent Client Protocol over stdio / WS / leader |

Interactive is defined as: no subcommand and none of the three headless prompt inputs. The binary name accepted by the parser is `grok` (or `agent` for argv0 compatibility).

## Shared runtime flags

These flags hang off the top-level `PagerArgs` parser. Many are available for both TUI and headless; a few are headless-only and are ignored with a warning in the TUI.

### Core session and prompt

| Flag | Description |
| --- | --- |
| `-v`, `-V`, `--version` | Print version (channel-aware) and exit |
| `--cwd <DIR>` | Change working directory before run |
| `-p`, `--single`, `--print <PROMPT>` | Headless prompt text |
| `--prompt-json <JSON>` | Headless ACP content blocks (array or `{"type":"acp","content":[...]}`) |
| `--prompt-file <PATH>` | Headless prompt from file (`.json` → blocks, else text) |
| `--verbatim` | Send the prompt exactly as given |
| `[PROMPT]` | Positional initial prompt for the interactive TUI only |
| `-m`, `--model <MODEL>` | Model ID |
| `--reasoning-effort`, `--effort <LEVEL>` | Reasoning effort for supported models |
| `--rules`, `--append-system-prompt <TEXT>` | Extra rules appended to the system prompt |
| `--system-prompt-override`, `--system-prompt <PROMPT>` | Replace the agent system prompt |
| `-r`, `--resume [SESSION_ID]` | Resume by ID, or most recent when ID omitted |
| `--load <SESSION_ID>` | Hidden alias for `--resume` with an ID |
| `-c`, `--continue` | Continue most recent session for the current directory |
| `-s`, `--session-id <UUID>` | Create a **new** session with this UUID (must not exist). With `-r`/`-c`, only valid with `--fork-session` |
| `--fork-session` | On resume/continue, fork into a new session ID |
| `-w`, `--worktree [NAME]` | Start in a new git worktree |
| `--worktree-ref`, `--ref <REF>` | Base ref for `--worktree` |
| `--restore-code` | With `--resume`, check out the original session commit |

### Permissions, tools, and safety

| Flag | Description |
| --- | --- |
| `--always-approve`, `--yolo`, `--dangerously-skip-permissions` | Auto-approve tool executions (same effective path as `bypassPermissions` when allowed by policy) |
| `--permission-mode <MODE>` | One of: `default`, `acceptEdits`, `auto`, `dontAsk`, `bypassPermissions`, `plan` |
| `--allow`, `--allowedTools <RULE>` | Permission allow rule (comma-delimited, repeatable) |
| `--deny`, `--disallowedTools <RULE>` | Permission deny rule (comma-delimited, repeatable) |
| `--tools <TOOLS>` | Built-in tool allowlist (comma-separated; **headless only**) |
| `--disallowed-tools <TOOLS>` | Built-in tool denylist; supports `Agent` / `Agent(type)` (**headless only**) |
| `--max-turns <N>` | Cap agent turns, `N ≥ 1` (**headless only**) |
| `--disable-web-search` | Disable web search and web fetch tools |
| `--sandbox <PROFILE>` | Sandbox profile; also `GROK_SANDBOX`. Built-ins: `workspace`, `devbox`, `read-only`, `strict`, `off` |
| `--trust` | Trust current folder and persist (hidden) |

Deny rules take precedence over allow rules. `--tools` / `--disallowed-tools` / `--max-turns` / `--agents` are headless-only; interactive use prints a warning and ignores them. Resuming a session with a different `--sandbox` than the profile stored at creation is refused.

### Output, UI, and process

| Flag | Description |
| --- | --- |
| `--output-format <FMT>` | Headless output: `plain` (default), `json`, `streaming-json` |
| `--json-schema <SCHEMA>` | Constrain model JSON; implies `--output-format json` when plain was selected |
| `--no-plan` | Disable plan mode |
| `--no-subagents` | Disable subagent spawning |
| `--agent <NAME>` | Agent name or definition path |
| `--agents <JSON>` | Inline subagent definitions |
| `--experimental-memory` / `--no-memory` | Enable or disable cross-session memory for this session |
| `--check`, `--self-verify` | Append self-verification loop (**headless**; conflicts with `--no-subagents`) |
| `--best-of-n <N>` | Run N parallel candidates and pick best (**headless**) |
| `--minimal` / `--fullscreen` | Screen mode; sticky writes `[ui] screen_mode` in `~/.grok/config.toml` (mutually exclusive) |
| `--no-alt-screen` | Inline rendering without alternate screen |
| `--oauth` | Prefer OAuth when the welcome screen authenticates |
| `--debug` / `--debug-file <FILE>` | Debug logging (global); may set `GROK_DEBUG_LOG` / `GROK_HOOKS_LOG` |
| `--leader-socket <PATH>` | Custom leader socket (global; default `~/.grok/leader.sock`) |

Headless background wait defaults to wait after the first turn (up to `--background-wait-timeout`, default 600s). Hidden `--no-wait-for-background` exits when the first turn ends.

```bash
# Interactive with model + initial prompt
grok -m grok-build "summarize this repo"

# Headless with JSON and auto-approve tools
grok -p "list open TODOs" --output-format json --yolo

# Resume last session for this directory, headless
grok -p "continue the refactor" -c

# Fork resume into a new session UUID
grok -p "try another approach" -r <session-id> --fork-session -s "$(uuidgen | tr '[:upper:]' '[:lower:]')"
```

## Default interactive TUI

With no subcommand and no headless prompt flags, the binary starts the pager TUI after config load, auth, sandbox resolution, and optional auto-update checks. A positional prompt seeds the first turn. Screen mode follows sticky config plus `--minimal` / `--fullscreen` / `--no-alt-screen`. Dashboard-at-startup is available via `grok dashboard` when the dashboard feature is enabled.

Related workflows: [Quickstart](/quickstart), [Sessions](/sessions), [Slash commands](/slash-commands), [Keyboard shortcuts](/keyboard-shortcuts).

## Headless mode (`-p`)

Headless runs the agent in-process (ACP lifecycle), streams results to stdout, and exits. Stdin is **not** the prompt; use `-p`, `--prompt-file`, or command substitution.

| Format | Shape |
| --- | --- |
| `plain` | Final assistant text |
| `json` | Single object: `text`, `stopReason`, `sessionId`, optional spend (`usage`, `modelUsage`, costs) |
| `streaming-json` | NDJSON events: `text`, `thought`, `end`, `error` (non-exhaustive) |

```bash
grok -p "Review staged changes" --yolo --output-format json
grok --prompt-file ./prompt.txt --output-format streaming-json
grok -p "only read tools" --tools "read_file,grep,list_dir"
```

Full CI patterns, spend field policy, and tool filtering details: [Headless mode](/headless-mode).

## `grok agent`

Runs Grok without the interactive UI. Nested modes:

| Mode | Command | Role |
| --- | --- | --- |
| stdio | `grok agent stdio` | ACP server on stdio (IDEs / custom clients) |
| headless | `grok agent headless` | Headless over Grok WebSocket relay |
| serve | `grok agent serve` | WebSocket agent server (`--bind`, default `127.0.0.1:2419`; secret via `--secret` or `GROK_AGENT_SECRET`) |
| leader | `grok agent leader` | Shared leader process for multiple clients |

Shared agent flags include `-m` / `--model`, `--reasoning-effort`, `--always-approve` / `--yolo`, `--agent-profile`, `--plugin-dir` (repeatable, process-local trusted plugins), `--leader` / `--no-leader`, `--reauth`, and endpoint overrides (`--cli-chat-proxy-base-url`, `--xai-api-base-url`, `--grok-ws-origin`, `--grok-ws-url`). Top-level `--leader` / `--no-leader` do **not** apply to `agent`; pass them on the agent subcommand.

See [Agent mode (ACP)](/agent-mode).

## Auth: `login` / `logout`

```bash
grok login
grok login --oauth          # browser OAuth via auth.x.ai
grok login --device-auth    # device-code flow (alias: --device-code)
grok logout                 # clear cached credentials
```

`--legacy` is accepted but ignored. Credentials live under `~/.grok` (for example `auth.json`). CI can use `XAI_API_KEY` instead of interactive login. Details: [Authentication](/authentication).

## `grok mcp`

Manage MCP server entries in user (`~/.grok/config.toml`) or project (`./.grok/config.toml`) scope.

| Subcommand | Purpose |
| --- | --- |
| `list [--json]` | List servers (project-aware merge) |
| `add` | Add/update stdio, HTTP, or SSE server |
| `remove <name> [-s user\|project]` | Remove; all scopes searched if scope omitted |
| `doctor [--json] [name]` | Diagnose config and connectivity |

```bash
grok mcp add xcode -- xcrun mcpbridge
grok mcp add --transport http sentry https://mcp.sentry.dev/mcp
grok mcp add --scope project github -- npx -y @modelcontextprotocol/server-github
grok mcp add postgres -e DATABASE_URL=postgres://localhost/db -- npx -y @modelcontextprotocol/server-postgres
```

Transports: `stdio` (default), `http`, `sse`. Flags: `-t/--transport`, `-s/--scope`, `-e/--env KEY=value`, `-H/--header "Name: value"`. See [MCP servers](/mcp-servers).

## `grok plugin`

Install and manage plugins and marketplaces.

| Subcommand | Purpose |
| --- | --- |
| `list [--json] [--available]` | Installed plugins; `--available` needs `--json` |
| `install <source> [--trust]` | Git URL, `user/repo`, local path; optional `@ref`, `#subdir` |
| `uninstall` / `rm` / `remove <name>` | Uninstall (`--confirm`, `--keep-data`) |
| `update [name]` | Update one or all |
| `enable` / `disable <name>` | Toggle without uninstall |
| `details <name>` | Component inventory |
| `validate [path]` | Validate `plugin.json` (default `.`) |
| `tag [path] [--push] [-f] [--dry-run]` | Git tag from manifest version |
| `marketplace list\|add\|remove\|update` | Marketplace sources |

See [Plugins](/plugins).

## `grok memory`

Cross-session memory maintenance from the CLI.

```bash
grok memory clear              # workspace MEMORY.md, sessions/, index (default)
grok memory clear --global     # global MEMORY.md
grok memory clear --all -y     # both scopes, skip confirm
```

See [Project rules and memory](/project-rules-and-memory).

## `grok models`

Lists auth status (API key, OAuth host, deployment key, model-scoped key, or unauthenticated), the default model, and available model IDs by spawning a short-lived agent shell.

```bash
grok models
```

Model selection at runtime: `-m` / `--model` or interactive `/model`. See [Custom models](/custom-models).

## `grok sessions`

| Subcommand | Purpose |
| --- | --- |
| `list [-n N]` | Recent sessions (default limit 20) |
| `search <query> [-n N]` | Keyword search (local + remote when available) |
| `delete <id>` | Permanently delete session history |

Resume in a run with `-r` / `-c` rather than a dedicated resume subcommand. See [Sessions](/sessions).

## `grok inspect`

Dumps configuration discovered for the current directory: project instructions, permissions, login policy, hooks, skills, agents, plugins, marketplaces, MCP/LSP, config sources, and external-compat notes. Folder trust gates repo-local hooks/plugins/MCP listings.

```bash
grok inspect
grok inspect --json
```

See [Configure Grok](/configure-grok).

## `grok setup`

Fetches and installs managed team configuration. Requires a deployment key (`GROK_DEPLOYMENT_KEY` or `[endpoints] deployment_key`) or team sign-in. `--json` prints the served report without writing `~/.grok`.

```bash
grok setup
grok setup --json
```

## `grok update`

| Flag | Purpose |
| --- | --- |
| `--check` | Check without installing |
| `--json` | Machine-readable output (with `--check`) |
| `--force-reinstall` | Re-download even if current |
| `--version <VER>` | Install specific version |
| `--alpha` / `--stable` | Channel switch (mutually exclusive; enterprise channel exists but is hidden) |

```bash
grok update --check
grok update --version 0.1.150
grok update --alpha
```

Install layout and PATH: [Installation](/installation).

## `grok wrap`

Runs an arbitrary command in a local PTY, intercepts OSC 52 clipboard sequences, and writes them to the local system clipboard (useful inside containers/SSH when the outer terminal does not handle OSC 52). Terminal size tracks the outer window.

```bash
grok wrap docker exec -it my-container bash
grok wrap kubectl exec -it my-pod -- bash
```

On Unix, a single quoted string or shell alias can run via `$SHELL -i -c`. Hidden on platforms without Unix/Windows PTY support.

## `grok export` / `grok trace`

```bash
# Markdown transcript of a session (stdout, file, or clipboard)
grok export <session-id>
grok export <session-id> ./out.md
grok export <session-id> --clipboard

# Trace archive (local and/or upload when [telemetry] trace_upload is enabled)
grok trace <session-id>
grok trace <session-id> --local -o /tmp/trace.tar.gz
grok trace <session-id> --json
```

Default local trace path: `$GROK_HOME/trace-exports/<session-id>.tar.gz`.

## Version, completions, worktree, dashboard

| Command | Purpose |
| --- | --- |
| `grok version` / `grok v` | Version string; `--json` → `currentVersion` + `channel` |
| `grok completions <shell>` | Emit completion script (`bash`, `zsh`, `fish`, `powershell`, …) |
| `grok worktree` | List (`ls`), show, rm, gc/prune, db rebuild/stats/path for tracked worktrees |
| `grok dashboard` | Open Agent Dashboard at TUI startup (disabled by `[dashboard].enabled = false` or `GROK_AGENT_DASHBOARD=0`) |

Hidden/gated: `grok share`, `grok workspace` (hub exposure; requires server flag or `GROK_WORKSPACE_COMMAND=1`).

## Environment counterparts (selected)

| Variable | Role |
| --- | --- |
| `XAI_API_KEY` | Non-interactive API auth |
| `GROK_DEPLOYMENT_KEY` | Managed config / enterprise auth |
| `GROK_SANDBOX` | Default sandbox profile |
| `GROK_AGENT_SECRET` | Secret for `agent serve` |
| `GROK_DEBUG_LOG` / `GROK_HOOKS_LOG` | Debug / hooks logging |
| `GROK_LOG_SAMPLING` | Sampling log file |
| `GROK_DISABLE_AUTOUPDATER` | Suppress auto-update |
| `GROK_WORKSPACE_COMMAND` | Enable/disable `workspace` CLI gate |
| `GROK_AGENT_DASHBOARD` | Disable dashboard with `0` |

Config precedence and full key schema: [Configuration reference](/configuration-reference), [Configure Grok](/configure-grok).

## Dispatch map

```text
grok argv
├── completions | wrap          → early exit (no full agent config path)
├── version | login | logout | update | setup | inspect
├── mcp | plugin | memory | models | sessions | worktree | export | trace
├── agent {stdio|headless|serve|leader}
├── -p | --prompt-json | --prompt-file  → headless single-turn
└── else → interactive TUI (+ optional positional prompt / dashboard)
```

Implementation anchors: clap command tree in `crates/codegen/xai-grok-pager/src/app/cli.rs`; binary dispatch in `crates/codegen/xai-grok-pager-bin/src/main.rs`; headless runner in `crates/codegen/xai-grok-pager/src/headless.rs`; management commands in `mcp_cmd.rs`, `plugin_cmd.rs`, `sessions_cmd.rs`, `memory_cmd.rs`, `export_cmd.rs`, `trace_cmd.rs`.

## Related pages

<CardGroup cols={2}>
  <Card title="Headless mode" href="/headless-mode">
    `-p`, output formats, tool allow/deny, max-turns, CI patterns
  </Card>
  <Card title="Agent mode (ACP)" href="/agent-mode">
    `grok agent stdio` and JSON-RPC session lifecycle
  </Card>
  <Card title="Authentication" href="/authentication">
    OAuth, device-code, API key, login/logout storage
  </Card>
  <Card title="Configuration reference" href="/configuration-reference">
    `config.toml` / `pager.toml` keys and env counterparts
  </Card>
  <Card title="Permissions and safety" href="/permissions-and-safety">
    Allow/deny rules, permission modes, safe-bash defaults
  </Card>
  <Card title="Sessions" href="/sessions">
    Resume, continue, fork, persistence, `grok sessions`
  </Card>
</CardGroup>

---

## 21. Configuration reference

> Schema-oriented keys for ~/.grok/config.toml and pager.toml: [cli], [models], [ui], [features], [session], [tools], [mcp_servers], [skills], [compat], permissions, sandbox, and env var counterparts.

- Page Markdown: https://grok-wiki.com/public/docs/xai-org-grok-build-90205de50458/pages/21-configuration-reference.md
- Generated: 2026-07-16T01:28:40.915Z

### Source Files

- `crates/codegen/xai-grok-pager/docs/user-guide/05-configuration.md`
- `crates/codegen/xai-grok-config-types/src/lib.rs`
- `crates/codegen/xai-grok-config-types/src/mcp.rs`
- `crates/codegen/xai-grok-config-types/src/permission.rs`
- `crates/codegen/xai-grok-config/src/validation.rs`
- `crates/codegen/xai-grok-shell/src/util/config/resolve/features.rs`
- `crates/codegen/xai-grok-env/src`
- `crates/codegen/xai-grok-paths/src/lib.rs`

---
title: "Configuration reference"
description: "Schema-oriented keys for ~/.grok/config.toml and pager.toml: [cli], [models], [ui], [features], [session], [tools], [mcp_servers], [skills], [compat], permissions, sandbox, and env var counterparts."
---

Grok Build resolves settings from CLI flags, environment variables, TOML layers under `$GROK_HOME` (default `~/.grok`), managed/requirements policy files, and built-in defaults. The primary user schema is `config.toml`; TUI layout and appearance live in `pager.toml`; OS sandbox profiles live in `sandbox.toml`. Typed sections are deserialized in the shell/agent config types (`CliConfig`, `ModelsConfig`, `UiConfig`, `Features`, `SessionConfig`, `PermissionConfig`, `McpServerConfig`, `SkillsConfig`, `CompatConfigToml`, `SandboxSettingsConfig`).

## Config surfaces

| Path | Role |
|------|------|
| `~/.grok/config.toml` | Main user schema (`[cli]`, `[models]`, `[ui]`, `[features]`, …) |
| `~/.grok/pager.toml` | TUI terminal, animation, prompt, scrollback, blocks |
| `~/.grok/sandbox.toml` | Custom OS sandbox profiles |
| `~/.grok/managed_config.toml` | Org-managed user layer (often synced) |
| `~/.grok/requirements.toml` | User requirements layer (policy pins) |
| `/etc/grok/managed_config.toml` | System managed layer (Unix) |
| `/etc/grok/requirements.toml` | System requirements layer (Unix) |
| `.grok/config.toml` | Project-scoped `[mcp_servers]`, `[plugins]`, `[permission]`, and `[mcp] max_output_bytes` |
| `.grok/sandbox.toml` | Project custom profiles (additive names only) |

`GROK_HOME` replaces `~/.grok` for all user-global paths. `grok_home()` creates the directory if needed; loaders that must not mistake a project `.grok` for the user home use `user_grok_home()` and return `None` when no home resolves.

:::files
~/.grok/
├── config.toml
├── pager.toml
├── sandbox.toml
├── managed_config.toml
├── requirements.toml
├── auth.json
├── sessions/
├── memory/
├── skills/
├── plugins/
├── agents/
├── lsp.json
└── logs/
::

## Precedence

Highest wins unless a resolver documents a special walk:

1. **CLI flags** — e.g. `--model`, `--sandbox`, `--yolo`
2. **Environment variables** — e.g. `XAI_API_KEY`, `GROK_MEMORY`, `GROK_SANDBOX`
3. **User `config.toml`** — `$GROK_HOME/config.toml`
4. **Managed / requirements** — `managed_config.toml`, `requirements.toml` (user, system, macOS MDM)
5. **Built-in defaults**

Boolean feature flags commonly use: **requirement → CLI → env → user config → managed → remote settings → default** (`BoolFlag` / `ConfigSource`).

Exceptions:

| Knob | Walk |
|------|------|
| `[features] remote_fetch` | Requirements (MDM → system → user) → managed → system managed → user config → default `true`. No env/remote tier (air-gapped safety). |
| `[permission]` rules | Merged across scopes; severity `deny` > `ask` > `allow` (not last-write-wins). |
| Project MCP/plugins | `.grok/config.toml` (cwd) > `<repo-root>/.grok/config.toml` > `~/.grok/config.toml` (same name replaces). |
| `[toolset.ask_user_question]` | requirements → env → user → managed → defaults. |

Dump the resolved view with `grok inspect`. How-to editing and workflows: [Configure Grok](/configure-grok).

## Project-scoped sections

Project `.grok/config.toml` contributes only:

- `[mcp_servers]`
- `[plugins]`
- `[permission]`
- `[mcp] max_output_bytes`

Other tables load from user/global layers only.

---

## `config.toml` schema

Omit keys you do not need; absent keys use defaults or remote/managed pins.

### `[cli]`

| Key | Type | Notes |
|-----|------|--------|
| `auto_update` | bool | Check for updates on launch |
| `channel` | string | Release channel (e.g. stable/alpha); remote may supply fallback |
| `use_leader` | bool | Leader-mode preference |
| `show_tips` | bool | Startup tips |
| `worktree_type` | string | Worktree backend preference |
| `session_registry` | bool | Session registry hooks |
| `minimum_version` | string | User floor; managed can raise via semver-max |
| `session_picker_grouped` | bool | Group sessions by repo in pickers |
| `dismissed_version` | string | Persisted dismiss state |
| `installer` / `npm_registry` | string | Installer metadata |

```toml
[cli]
auto_update = true
channel = "stable"
```

### `[models]` and `[model.<id>]`

**Global `[models]`** (applies when per-model fields are unset):

| Key | Type | Notes |
|-----|------|--------|
| `default` | string | Model for new sessions |
| `web_search` | string | Model for web_search tool |
| `session_summary` | string | Session summary model |
| `image_description` | string | Vision/transcription model |
| `prompt_suggestion` | string | Ghost-text suggestion model |
| `default_reasoning_effort` | string | Effort pin for default model |
| `allowed_models` | string[] | Glob allowlist for picker/`/model`/`-m` |
| `hidden_models` | string[] | Hide from picker (still usable via `-m`) |
| `disabled_models` | string[] | Remove from catalog (wins over hidden) |
| `agent_type` | string | Fallback agent type |
| `extra_headers` | map | Default request headers |
| `temperature` | float | Global sampling default |
| `top_p` | float | Global nucleus default |
| `max_completion_tokens` | u32 | Global completion cap |
| `max_retries` | u32 | Global retries (default often 8) |
| `inference_idle_timeout_secs` | u64 | Stream idle timeout |
| `stream_tool_calls` | bool | Stream tool-call deltas |

**Per-model `[model.<id>]`** (OpenAI-compatible / BYOK; override built-ins by id):

| Key | Type | Notes |
|-----|------|--------|
| `model` | string | Wire model id |
| `base_url` | string | OpenAI-compatible endpoint |
| `name` / `description` | string | Display metadata |
| `api_key` | string | Inline key |
| `env_key` | string or string[] | Env var(s); first set non-empty wins |
| `temperature` / `top_p` / `max_completion_tokens` | number | Sampling |
| `context_window` | number | Auto-compact sizing |
| `extra_headers` | map | Per-key override of global headers |

Credential order for a model: `api_key` → `env_key` → signed-in session → `XAI_API_KEY`.

```toml
[models]
default = "grok-build"
web_search = "grok-4.20-multi-agent"
temperature = 0.7
extra_headers = { "X-Request-Tags" = "team=example" }

[model.my-ollama]
model = "llama3.2"
base_url = "http://127.0.0.1:11434/v1"
name = "Local Llama"
env_key = "OLLAMA_API_KEY"
context_window = 128000
```

See [Custom models](/custom-models).

### `[ui]`

| Key | Type | Default / values | Notes |
|-----|------|------------------|--------|
| `simple_mode` | bool | `true` | Prompt editor: readline vs vim modal |
| `vim_mode` | bool | `false` | Scrollback vim keys (independent of `simple_mode`) |
| `screen_mode` | string | unset / `"minimal"` / `"fullscreen"` | Sticky TUI mode; CLI `/minimal` `/fullscreen` write it |
| `theme` | string | e.g. `groknight`, `auto` | Built-in theme or system auto |
| `auto_dark_theme` / `auto_light_theme` | string | — | Themes when `theme = "auto"` |
| `max_thoughts_width` | u16 | `120` | Reasoning column width |
| `default_selected_permission` | string | `always_allow_all_sessions` | First permission-menu cursor; also `allow_command_always`, `allow_once`, `reject` |
| `remember_tool_approvals` | bool | `false` | Show per-command “Always allow” rows |
| `show_thinking_blocks` | bool | `true` | Show thinking in scrollback |
| `group_tool_verbs` | bool | `true` | Fold consecutive read/search/list rows |
| `collapsed_edit_blocks` | bool | `false` | Edit as `+N/-M` until expanded |
| `compact_mode` | bool | — | Compact UI; also `/compact-mode` |
| `scroll_speed` | u8 | `50` (1–100) | Wheel/trackpad multiplier |
| `scroll_mode` | string | `auto` \| `wheel` \| `trackpad` | Input classification |
| `scroll_lines` | u8 | unset (1–10) | Explicit lines/tick for both devices |
| `invert_scroll` | bool | `false` | Natural scrolling |
| `prompt_suggestions` | bool | on when unset | Ghost-text after turns |
| `permission_mode` / `approval_mode` | string | — | Permission mode preference |
| `hunk_tracker_mode` | string | `agent_only` \| `all_dirty` \| `off` | Diff hunk tracking |
| `render_mermaid` | string | `auto` \| `on` \| `off` | Mermaid in scrollback |
| `cursor_blink` | bool | inherit terminal | Force block cursor blink |
| `mouse_reporting_toggle` | bool | `false` | Opt-in Ctrl+R mouse toggle (scrollback focus) |
| `cancel_subagents_on_turn_cancel` | string | `ask` \| `always_stop` \| `always_continue` | Cancel-turn subagent policy |

Env overrides (first load; do not write `config.toml`): `GROK_DEFAULT_SELECTED_PERMISSION`, `GROK_SCROLL_SPEED`, `GROK_SCROLL_MODE`, `GROK_SCROLL_LINES`, `GROK_INVERT_SCROLL`, `GROK_PROMPT_SUGGESTIONS`, `GROK_HUNK_TRACKER`.

#### `[ui.notifications]`

| Key | Type | Default | Notes |
|-----|------|---------|--------|
| `method` | string | `auto` | `auto` \| `osc9` \| `osc99` \| `osc777` \| `bel` \| `none` |
| `condition` | string | `unfocused` | `unfocused` \| `always` \| `never` |
| `idle_threshold_secs` | int | `3` | Unfocused delay before fire |
| `events` | string[] | turn_complete, approval_required | Plus session_ready, task_complete, agent_error |
| `sleep_prevention` | bool | `true` | Keep display awake on turns |
| `progress_bar` | bool | `true` | OSC 9;4 tab progress |
| `title.enabled` | bool | `true` | Terminal title updates |
| `title.items` | string[] | action-required, spinner, … | Title segments |

`[[ui.notifications.hooks]]`: `command`, `events`, `only_unfocused` (default true), `timeout_secs` (default 10). Hook env: `$GROK_EVENT`, `$GROK_MESSAGE`, `$GROK_SESSION_ID`.

#### `[ui.display_refresh]`

| Key | Type | Default | Notes |
|-----|------|---------|--------|
| `probe_enabled` | bool | on | Primary-display Hz probe |
| `auto_cadence_enabled` | bool | off | Derive paint cadence from probe |
| `floor_ms` / `ceiling_ms` | u32 | 8 / 16 | Auto-cadence clamps |
| `min_hz` / `max_hz` | u32 | 55 / 165 | Accepted probe band |

### `[features]`

| Key | Type | Typical default | Notes |
|-----|------|-----------------|--------|
| `telemetry` | bool / mode | off | Master anonymous telemetry; modes may include session-metrics/full/off |
| `feedback` | bool | true (product docs) | Feedback UX; remote may pin |
| `lsp_tools` | bool | false | Expose `lsp` tool |
| `codebase_indexing` | bool or glob list | true | Code graph; globs can include non-git dirs |
| `two_pass_compaction` | bool | false | Prefire compaction |
| `remote_fetch` | bool | true | Online model-catalog + settings fetches |
| `managed_config` | bool | true | Background managed-config sync |
| `web_fetch` | bool | false (resolver) | Web fetch tool |
| `tool_search` | bool | true | MCP tool search |
| `ask_user_question` | bool | true | Ask-user-question tool |
| `session_recap` | bool | true | `/recap` + return-from-away |
| `write_file` | bool | true | Write-file tool |
| `cancel_rewind` | bool | true | Ctrl+C restore prompt before activity |
| `auto_wake` | bool | true | Wake on background/subagent completion |
| `backend_tools` | bool | true | Server-side web_search / x_search |
| `video_gen` | bool | false | Video generation tool |
| `image_gen_model_override` | string | — | Imagine model pin |
| `non_git_warning` | bool | false | Warn outside git repos |
| `support_permission` | bool | — | Agent may request tool permission |
| `zdr_access_enabled` | bool | false | ZDR access gate |

```toml
[features]
telemetry = false
feedback = true
lsp_tools = false
codebase_indexing = true
two_pass_compaction = false
remote_fetch = true
```

### `[session]`

| Key | Type | Default | Notes |
|-----|------|---------|--------|
| `auto_compact_threshold_percent` | u8 | 85 | % of context window before auto-compact |
| `load_envrc` | bool | true | Inject `.envrc` into bash tool env |

Env: resolver chain for auto-compact (env / per-model / remote / default).

### `[tools]` and `[toolset.*]`

```toml
[tools]
respect_gitignore = false   # GROK_RESPECT_GITIGNORE overrides

[toolset.bash]
timeout_secs = 120.0
output_byte_limit = 20000

[toolset.ask_user_question]
timeout_enabled = true
timeout_secs = 1800

[toolset.web_fetch]
proxy_endpoint = "https://proxy.example.com"
allowed_domains = ["docs.rs", "x.ai"]
```

Env: `GROK_ASK_USER_QUESTION_TIMEOUT_ENABLED`, `GROK_ASK_USER_QUESTION_TIMEOUT_SECS`, `GROK_RESPECT_GITIGNORE`, `GROK_WEB_FETCH`.

`[toolset] file_toolset` may be `"standard"` or `"hashline"` (remote can set default; local wins when set).

### `[mcp_servers.<name>]`

Transport is untagged: **stdio** (`command` + `args`) or **HTTP/SSE** (`url`).

| Key | Type | Default | Notes |
|-----|------|---------|--------|
| `command` / `args` / `env` / `cwd` | stdio | — | Process transport; `cwd` not exposed to ACP yet |
| `url` | string | — | Streamable HTTP |
| `type` | string | — | `"sse"` or URL ending `/sse` selects SSE |
| `headers` | map | — | HTTP headers; may use `{{session_id}}` |
| `bearer_token_env_var` | string | — | Sets `Authorization: Bearer …` |
| `oauth_client_id` / `oauth_client_secret_env_var` / `oauth_scopes` | HTTP OAuth fields | — | Flat on transport |
| `oauth` | block | — | `{ client_id, client_secret_env_var, scopes, callback_port }` |
| `enabled` | bool | `true` | Disabled servers skipped |
| `startup_timeout_sec` | u64 | 30 | Init handshake |
| `tool_timeout_sec` | u64 | 6000 | Default tool call timeout |
| `tool_timeouts` | map | — | Per-tool seconds |
| `expose_image_base64` | bool | — | Keep base64 in tool text (~2× tokens) |

```toml
[mcp_servers.github]
command = "npx"
args = ["-y", "@modelcontextprotocol/server-github"]
env = { GITHUB_PERSONAL_ACCESS_TOKEN = "ghp_xxx" }
enabled = true
startup_timeout_sec = 30
tool_timeout_sec = 6000

[mcp_servers.remote]
url = "https://mcp.example.com/api/mcp"
headers = { "x-mcp-session-id" = "{{session_id}}" }
bearer_token_env_var = "MCP_TOKEN"
```

Global MCP defaults may also live under `[mcp]` (e.g. `max_output_bytes`, default 20_000). Management CLI: [MCP servers](/mcp-servers).

### `[permission]`

Two equivalent shapes.

**Structured rules:**

```toml
[permission]
rules = [
  { action = "allow", tool = "bash", pattern = "git *" },
  { action = "deny",  tool = "bash", pattern = "rm -rf *" },
  { action = "ask",   tool = "edit" },
]
```

| Field | Values | Notes |
|-------|--------|--------|
| `action` | `allow` \| `deny` \| `ask` | Default if omitted: **deny** (fail closed) |
| `tool` | `any` \| `bash` \| `edit` \| `read` \| `grep` \| `mcp` \| `webfetch` | Case-insensitive enum |
| `pattern` | string | Optional match string |
| `pattern_mode` | `glob` (default) \| `domain` | Domain mode for WebFetch hosts |

**Compact arrays** (same strings as `--allow` / `--deny`):

```toml
[permission]
allow = ["Bash(git *)", "Read", "Grep"]
deny  = ["Bash(rm -rf *)", "Read(/Users/you/private/**)"]
ask   = ["Edit"]
```

Rules load once per session from user, every project `.grok/config.toml` on the path to cwd, managed layers, and Claude-compat settings. Severity is always `deny` > `ask` > `allow`. Details: [Permissions and safety](/permissions-and-safety).

### `[sandbox]` (in `config.toml`)

| Key | Type | Notes |
|-----|------|--------|
| `profile` | string | `off`, `workspace`, `devbox`, `read-only`, `strict`, or custom name |
| `auto_allow_bash` | bool | Skip bash permission prompts while sandbox is active |

CLI `--sandbox` and env `GROK_SANDBOX` override. Full profile matrix: [Sandbox profiles](/sandbox).

### `[skills]`

| Key | Type | Notes |
|-----|------|--------|
| `paths` | string[] | Extra skill dirs or `SKILL.md` files (`~` expanded) |
| `ignore` | string[] | Path prefixes excluded after discovery |
| `disabled` | string[] | Names remain listed but inactive |

Discovery still scans `.grok` / `.agents` / vendor trees gated by `[compat]`. See [Skills](/skills).

### `[compat]`

Every cell defaults **on**. Env > `config.toml` > default.

```toml
[compat.cursor]
skills = true
rules = true
agents = true
mcps = true
hooks = true
sessions = true   # staged until foreign-session scanner + resume skill

[compat.claude]
skills = true
rules = true
agents = true
mcps = true
hooks = true
sessions = true

[compat.codex]
sessions = true   # non-session Codex cells are reserved/inert
```

`grok inspect` shows unresolved cells as `?` / `compatibilityStatus: "unresolved"`.

### `[plugins]`

```toml
[plugins]
paths = ["~/my-plugins/custom-tools"]
disabled = ["user/a1b2c3d4/noisy-plugin"]
```

See [Plugins](/plugins).

### `[memory]`

Requires experimental memory (`GROK_MEMORY=1` or `--experimental-memory`). Nested tables include `session`, `watcher`, `search`, `initial_injection`, `embedding` (see user guide and [Project rules and memory](/project-rules-and-memory)).

### `[subagents]`

```toml
[subagents]
enabled = true

[subagents.toggle]
explore = true
plan = false

[subagents.models]
explore = "grok-build"
```

Env: `GROK_SUBAGENTS`. See [Subagents and personas](/subagents).

### `[auth]` and OIDC

```toml
[auth]
auth_provider_command = "/usr/local/bin/my-auth-provider"
auth_provider_label = "Acme Corp"
auth_token_ttl = 3600

[grok_com_config.oidc]
issuer = "https://acme.okta.com"
client_id = "0oa..."
# scopes / audience optional
```

Env: `XAI_API_KEY`, `GROK_AUTH_PROVIDER_COMMAND`, `GROK_AUTH_PROVIDER_LABEL`, `GROK_AUTH_TOKEN_TTL`, `GROK_AUTH_EARLY_INVALIDATION_SECS`, `GROK_OIDC_ISSUER`, `GROK_OIDC_CLIENT_ID`. See [Authentication](/authentication).

### `[hints]`

Persisted “don’t ask again” UI prefs (written by TUI; editable by hand):

| Key | Default | Notes |
|-----|---------|--------|
| `project_picker_disabled` | false | Skip non-project directory picker |
| `memory_modal_fullscreen` | false | Memory modal fullscreen state |
| `new_session_worktree_mode` | `"never"` | `ask` \| `always` \| `never` for `/new` |
| `fork_worktree_mode` | `"ask"` | Same for `/fork` |

### `[telemetry]`

| Key | Notes |
|-----|--------|
| `events_url` / `events_api_key` | Private collector for product events |
| `mixpanel_enabled` | Product analytics off-switch |
| `trace_upload` | Session/trace upload |
| `otel_enabled` | External OTLP stream (`GROK_EXTERNAL_OTEL`) |
| `otel_metrics_exporter` / `otel_logs_exporter` | `otlp` \| `console` \| `none` |
| `otel_endpoint` / `otel_protocol` | Collector base + `http/protobuf` \| `grpc` |
| `otel_log_user_prompts` / `otel_log_tool_details` | Content gates (admins may lock via requirements) |

External OTEL auth uses `OTEL_EXPORTER_OTLP_HEADERS` (not stored in TOML).

### Other tables

| Section | Purpose |
|---------|---------|
| `[relay]` | `enabled` — session sharing; env `GROK_RELAY_SYNC_ENABLED` |
| `[hub]` | `url` — default Computer Hub WS for `grok workspace` only |
| `[doom_loop_recovery]` | `enabled`, `max_threshold`, `max_retries` (env > TOML > remote > default) |
| `[folder_trust]` | `enabled` — gate repo-local MCP/LSP until trusted (`GROK_FOLDER_TRUST`) |
| `[diagnostics]` | `crash_handler` |
| `[compaction]` | Nested memory flush / pruning |

---

## `pager.toml` (TUI appearance)

Location: `~/.grok/pager.toml`. Applied on restart (except where noted). Theme primary control is often `[ui] theme` in `config.toml`; layout lives here. See [Theming](/theming).

### `[terminal]`

| Key | Values | Notes |
|-----|--------|--------|
| `alt_screen` | `auto` \| `always` \| `never` | Alternate screen vs main scrollback |
| `minimal` | bool (legacy) | Superseded by `[ui] screen_mode` when set |

### `[animation]`

| Key | Default | Notes |
|-----|---------|--------|
| `fps` | 30 | Animation tick rate |
| `wave_rows` | 32 | Accent wave period |

### `[prompt]`

| Key | Default | Notes |
|-----|---------|--------|
| `collapse_unfocused` | true | Collapse prompt when scrollback focused |
| `mouse_hover` | true | Hover highlight |
| `show_prefix` | true | Prompt prefix character |

### `[scrollback.*]`

| Table | Selected keys |
|-------|----------------|
| `scrollback.layout` | `outer_vpad`, `outer_hpad_left/right`, `block_pad_left/right` |
| `scrollback.scrollbar` | `enabled`, `gap_left`, `gap_right` |
| `scrollback.scroll` | `margin`, `min_page_fraction`, `follow_indicator`, `follow_auto_select`, `follow_by_overscroll`, `anchor_on_fold`, `respect_manual_folds` (default **false**) |
| `scrollback.display` | `sticky_headers`, `tab_width`, `expandable_indicator*`, `selection_buttons`, `dim_accent` |
| `scrollback.blocks.edit` | `indent`, `vpad`, `expanded_by_default`, `dual_line_numbers`, `line_summary`, `hunk_separator` |
| `scrollback.blocks.prompt` | `vpad`, `show_prefix`, `min_lines` |
| `scrollback.blocks.thinking` | `animate`, `truncated_lines` |

`expanded_by_default` / `line_summary` on edit blocks override `[ui] collapsed_edit_blocks` when set.

### `[todo]`

`badge_format`: `"default"` (`2/5`) \| `"colon"` \| `"comma"`.

### Top-level

`disable_plugins = false` — hide hooks/plugins UI.

Keyboard shortcuts are **not** configurable in TOML. See [Keyboard shortcuts](/keyboard-shortcuts).

---

## `sandbox.toml` profiles

Not the same as `[sandbox]` in `config.toml`. Custom profiles:

```toml
[profiles.project]
extends = "workspace"
restrict_network = true
read_only = ["/data"]
read_write = ["/tmp/scratch"]
deny = ["**/.env", "**/*.pem"]
```

| Field | Notes |
|-------|--------|
| `extends` | Built-in base: workspace, devbox, read-only, strict (default workspace) |
| `restrict_network` | Child network block (Linux-enforced for read-only/strict) |
| `read_only` / `read_write` | Extra path grants |
| `deny` | Kernel-enforced read+write/rename deny; globs use `*`, `?`, `[`, `**` |

Built-in names cannot be redefined. Project file may **add** profile names only; conflicting same-name custom profiles: global wins, startup warning if definitions differ.

Activation: `grok --sandbox project`, `[sandbox] profile = "project"`, or `GROK_SANDBOX=project`.

---

## Environment variable counterparts

| Variable | Maps to / effect |
|----------|------------------|
| `GROK_HOME` | Root for config, auth, sessions, skills, plugins |
| `XAI_API_KEY` | API auth for CI/headless |
| `GROK_SANDBOX` | Sandbox profile |
| `GROK_MEMORY` | Cross-session memory on/off |
| `GROK_SUBAGENTS` | Subagents on/off |
| `GROK_WEB_FETCH` | web_fetch tool |
| `GROK_AGENT` | Custom agent path or name |
| `GROK_RESPECT_GITIGNORE` | Overrides `[tools] respect_gitignore` |
| `GROK_TELEMETRY_ENABLED` | Telemetry master |
| `GROK_FEEDBACK_ENABLED` | Feedback system |
| `GROK_EXTERNAL_OTEL` | External OTEL stream |
| `GROK_RELAY_SYNC_ENABLED` | Relay sync |
| `GROK_FOLDER_TRUST` | Folder-trust gate |
| `GROK_CLI_CHAT_PROXY_BASE_URL` | API proxy base (also `GROK_PRODUCTION_*` endpoint family) |
| `GROK_GATEWAY_URL` | Cloud sandbox gateway opt-in |
| `GROK_LOG_FILE` / `RUST_LOG` | Logging path and filter |
| `GROK_DEPLOYMENT_KEY` | Enterprise management API key |
| `GROK_MANAGED_CONFIG_FAIL_CLOSED` | Tighten requirements fail-closed |
| `GROK_DEFAULT_SELECTED_PERMISSION` | First permission-menu row |
| `GROK_SCROLL_*` / `GROK_INVERT_SCROLL` | Scroll UI |
| `GROK_ASK_USER_QUESTION_TIMEOUT_*` | Ask-question timeouts |
| `GROK_TWO_PASS_COMPACTION` | Two-pass compaction |
| `GROK_PROMPT_SUGGESTIONS` | Ghost-text suggestions |
| `GROK_HUNK_TRACKER` | Hunk tracker mode |
| `GROK_COMPACTION_MODE` | Compaction presentation mode |
| `OTEL_EXPORTER_OTLP_HEADERS` | OTLP collector auth (never stored in TOML) |

Endpoint production defaults (override via env): cli-chat-proxy, assets, relay WS, gateway WS, ws origin — resolved in `xai-grok-env`.

---

## Verify resolved config

```bash
grok inspect
# optional: JSON / focus on compat cells and sources
```

Expect explicit sources (`requirement`, `cli`, `env`, `user_config`, `managed`, `remote`, `default`) for critical flags. Invalid requirements under fail-closed refuse startup; soft-load paths skip bad layers with logs.

## Related pages

<CardGroup>
  <Card title="Configure Grok" href="/configure-grok">
    Edit workflow, precedence narrative, and inspect usage.
  </Card>
  <Card title="Custom models" href="/custom-models">
    BYOK, OpenAI-compatible endpoints, and model selection.
  </Card>
  <Card title="MCP servers" href="/mcp-servers">
    Register, enable, and manage MCP transports.
  </Card>
  <Card title="Permissions and safety" href="/permissions-and-safety">
    Rule evaluation, modes, and hooks interaction.
  </Card>
  <Card title="Sandbox profiles" href="/sandbox">
    Built-in isolation and custom deny globs.
  </Card>
  <Card title="Theming" href="/theming">
    Themes, screen modes, and color capability.
  </Card>
  <Card title="CLI reference" href="/cli-reference">
    Flags that override TOML at runtime.
  </Card>
</CardGroup>

---

## 22. Slash commands

> Interactive / commands for sessions, models, memory, hooks, plugins, themes, and more; pager vs shell builtins; skill-provided commands in the autocomplete menu.

- Page Markdown: https://grok-wiki.com/public/docs/xai-org-grok-build-90205de50458/pages/22-slash-commands.md
- Generated: 2026-07-16T01:27:25.778Z

### Source Files

- `crates/codegen/xai-grok-pager/docs/user-guide/04-slash-commands.md`
- `crates/codegen/xai-grok-pager/src/slash/commands/mod.rs`
- `crates/codegen/xai-grok-shell/src/session/commands.rs`
- `crates/codegen/xai-grok-pager/src/app/dispatch/router.rs`
- `crates/codegen/xai-grok-pager/docs/user-guide/08-skills.md`

---
title: "Slash commands"
description: "Interactive / commands for sessions, models, memory, hooks, plugins, themes, and more; pager vs shell builtins; skill-provided commands in the autocomplete menu."
---

Typing `/` in the interactive prompt opens a fuzzy-complete command menu. Grok Build merges **pager builtins** (TUI-local actions in `xai-grok-pager`), **shell builtins** (session/agent actions advertised by `xai-grok-shell` over ACP), and **user-invocable skills** (from `SKILL.md` packages and plugins) into one registry. Execution is synchronous at the pager edge: a command returns a `CommandResult` (local action, queue entry, skill injection, or pass-through to the shell), and the dispatch layer applies the effect.

## How invocation works

1. You type `/` (optionally followed by a name and args).
2. `SlashController` parses the token, ranks triggers with fuzzy matching, and shows up to six visible suggestion rows (with scroll for longer lists).
3. `Tab` or `Enter` accepts a row. Commands that take args insert a trailing space so argument completion can open next (for example `/model` → model list → effort levels).
4. On submit, the pager looks up the command in `CommandRegistry` and runs it. Unknown or shell-owned names are sent as prompt text so the agent can resolve them.

```
/model grok-build
/compact keep the auth implementation details
/commit fix typo in README
```

### Argument completeness

| `takes_args` | `args_required` | Example | Enter with no args |
| --- | --- | --- | --- |
| `false` | `false` | `/quit` | Runs immediately |
| `true` | `false` | `/compact [context]` | Runs with empty args |
| `true` | `true` | `/model <name>` | Blocks until args are present |

Argument placeholders (for example `compaction instructions` on `/compact`) appear in the prompt and autocomplete menu.

## Two handlers: pager vs shell

| Source | Owner | Typical work | How the menu learns about it |
| --- | --- | --- | --- |
| **Pager builtins** | `xai-grok-pager` | UI, theme, screen mode, resume picker, modals, local toggles | Built into `builtin_commands()` at startup |
| **Shell builtins** | `xai-grok-shell` | Compact, memory flush/dream, goal, hooks/plugins CLI, feedback | ACP `AvailableCommandsUpdate` → `CommandRegistry::set_acp_commands` |
| **Skills** | Disk / plugins | Inject structured skill prompts into the turn | Shell advertises user-invocable enabled skills; pager can inject via `InjectSkill` |

Some names exist on **both** sides with different UX. Example: the shell advertises `/hooks-list`, `/hooks-trust`, `/hooks-add`, `/hooks-remove`, `/hooks-untrust`, and `/plugins` subcommands; the TUI consolidates those into the `/hooks`, `/plugins`, `/marketplace`, and `/skills` **extensions modal** tabs. Prefer the modal in interactive sessions; shell subcommands remain useful for headless or ACP clients.

**Precedence:** pager/shell built-in names always win over a skill with the same bare name. Use a qualified skill name when you need the skill instead (see [Skills as slash commands](#skills-as-slash-commands)).

### Execution outcomes

| Result | Meaning |
| --- | --- |
| `Action(...)` | Dispatch a pager action (`SetDefaultModel`, open modal, quit, …) |
| `QueueCommand(...)` | Enqueue work on the session command pipeline (for example `/compact`) |
| `InjectSkill { ... }` | Load skill (or skill-like) prompt blocks for the model |
| `PassThrough(...)` | Send text as a normal prompt; shell resolves ACP or unknown commands |
| `Message` / `Error` | Show user-visible text without starting a turn |
| `Handled` / `HandledNoOp` | Success or no-op with no extra scrollback |

## Autocomplete menu

The dropdown shows, per row:

- Command name (with leading `/`)
- Description
- Argument hint when applicable
- Source context for skills (scope or plugin), via ACP metadata when present

Behavior details:

- **Fuzzy filter** as you type after `/`.
- **Inline ghost completion** for a smart prefix match against the selected row.
- **MRU-aware ranking** for recently used commands.
- **Tool gating:** commands that declare `required_tools()` (for example `/loop` needing `scheduler_create`) stay hidden until the shell advertises those tools in `AvailableCommandsUpdate` meta (`{"tools": [...]}`). Before any toolset is known, tool-dependent commands are fail-closed (hidden).
- **Capability gates (shell):** memory, scheduler, hooks, plugins, feedback, and goal builtins only advertise when `CommandAvailability` allows them.
- **Restricted / tier-hidden** names can be denied even if typed; separate from menu-only hide lists.
- **Session-scoped vs dashboard:** session-only commands (`/compact`, `/fork`, …) are suppressed on the agent dashboard dispatch input unless marked `offered_when_session_less` (for example `/model`, `/plan`). `/cd` is dashboard-only.

## Session management

| Command | Aliases | Purpose |
| --- | --- | --- |
| `/new` | `/clear` | Start a fresh session |
| `/resume` | — | Open the session picker |
| `/compact [context]` | — | Compress history; optional focus text |
| `/context` | — | Context-window breakdown and costs |
| `/session-info` | shell: `/status`, `/info` | Model, turns, usage snapshot |
| `/fork` | — | Branch session history into a new agent |
| `/rewind` | — | Rewind to an earlier turn |
| `/copy [n]` | — | Copy latest (or Nth-latest) response |
| `/export` | — | Export conversation to file or clipboard |
| `/transcript` | — | Open transcript views |
| `/rename <title>` | `/title` | Rename the session |
| `/home` | `/welcome` | Return to the welcome screen |
| `/quit` | `/exit` | Quit the app |
| `/share` | — | Share-session flows |
| `/queue` | — | Inspect the prompt queue |
| `/tasks` | — | Background / scheduled tasks pane |

Auto-compaction also runs when the context window hits the threshold (default **85%**, `[session] auto_compact_threshold_percent` in `config.toml`).

## Model, permissions, and UI mode

| Command | Aliases | Purpose |
| --- | --- | --- |
| `/model <name> [effort]` | `/m` | Switch model; optional effort for reasoning models |
| `/effort <level>` | — | Set effort on the **current** model: `low`, `medium`, `high`, `xhigh` |
| `/always-approve` | shell: `/yolo` | Toggle skip-all permission prompts |
| `/auto` | — | Toggle classifier-based auto-approve (feature-gated; fail-closed until enabled) |
| `/multiline` | `/ml` | Toggle multiline composer (`Enter` = newline) |
| `/history` | — | Fuzzy search this session’s prompts |
| `/compact-mode` | — | Denser visual spacing |
| `/vim-mode` | — | Vim scrollback bindings; persists to `[ui].vim_mode` |
| `/minimal` / `/fullscreen` | `/full` | Sticky screen-mode switch (`[ui].screen_mode`) |
| `/plan [description]` | — | Enter plan mode |
| `/view-plan` | `/show-plan`, `/plan-view` | Preview the saved plan |
| `/theme` | `/t` | Theme picker |
| `/settings` | `/config`, `/preferences`, `/prefs` | Settings modal |
| `/timestamps` | — | Toggle message timestamps |
| `/terminal-setup` | `/terminal-check`, `/terminal-info` | Capability detection and fix hints |

Permission mode tips:

- `/always-approve` and `/auto` are **toggles**: run the active mode again to return to ask.
- Switching with the other command while one is on **changes** modes rather than stacking them.
- `Shift+Tab` (cycle), `Ctrl+O`, and `/settings` also change permission mode.

## Memory

Requires experimental memory (`--experimental-memory` or `GROK_MEMORY=1`) for `/flush`, `/dream`, and `/memory`. `/remember` is always available in the pager.

| Command | Aliases | Purpose |
| --- | --- | --- |
| `/memory [on\|off]` | `/mem` | Browse/manage memories; enable or disable |
| `/flush` | — | LLM summary of important session content → memory |
| `/dream` | — | Consolidate session logs into topics |
| `/remember <note>` | — | Save a note immediately |

Use `/flush` before compaction or when you want durable decisions/patterns captured for later retrieval.

## Hooks, plugins, marketplace, skills UI

| Command | Opens |
| --- | --- |
| `/hooks` | Extensions modal → Hooks |
| `/plugins` | Extensions modal → Plugins |
| `/marketplace` | Extensions modal → Marketplace |
| `/skills` | Extensions modal → Skills |
| `/mcps` | MCP servers modal |

Shell-side `/plugins list|reload|trust|add|remove|install|uninstall|update` and `/reload-plugins` still exist for non-modal clients. Project hook trust is **not** granted merely by opening the modal; see the hooks docs for the trust model.

## Media, scheduling, and goals

| Command | Purpose |
| --- | --- |
| `/imagine <description>` | Text-to-image |
| `/imagine-video <description>` | Planned video generation |
| `/loop [interval] <prompt>` | Recurring prompt (`Ns`/`Nm`/`Nh`/`Nd`, minimum 60s; auto-expires after 7 days) |
| `/goal <objective\|status\|pause\|resume\|clear>` | Autonomous goal (feature + `update_goal` tool required) |

`/loop` injects a structured prompt (and can show a provisional scheduled-task preview immediately). Cancel via `scheduler_delete` with the job id returned at creation.

## Account, agents, and utilities

| Command | Aliases | Purpose |
| --- | --- | --- |
| `/login` / `/logout` | — | Re-auth or sign out without leaving the TUI |
| `/usage` | — | Credits / billing (may be tier-restricted) |
| `/privacy` | — | Privacy / data-retention status |
| `/config-agents` | `/agents` | Agent definitions modal |
| `/personas` | — | Create/edit/delete personas |
| `/btw <text>` | — | Aside to the agent without interrupting the turn |
| `/feedback [message]` | — | Send feedback |
| `/docs [web\|title]` | `/howto`, `/guides` | In-TUI guides or open Build docs |
| `/import-claude` | — | Import `~/.claude` settings into Grok |
| `/release-notes` | `/changelog` | Current version notes |
| `/recap` | — | Session recap (visibility may be gated) |
| `/voice` | — | Voice mode (fail-closed until runtime enables it) |
| `/cd` | — | Dashboard: change dispatch working directory |
| `/help` | — | Help surface |

Debug-oriented commands (for example `/debug`, `/scroll-debug`, `/gboom`) exist in the registry; some stay menu-hidden until unlocked.

## Skills as slash commands

Any **enabled**, **user-invocable** skill appears as `/<skill-name>`:

```
/commit fix the build
/review-pr
```

Rules that matter in practice:

- Frontmatter `user-invocable: false` hides the skill from slash autocomplete (model may still invoke it unless `disable-model-invocation: true`).
- Flat `*.md` under `commands/` directories become slash commands (stem = name), matching legacy Claude Code layout.
- Name collisions advertise **qualified** forms: `/local:commit`, `/user:commit`, or `/plugin-name:commit`.
- Built-ins always take the bare name (`/compact` never becomes a skill unless you use a qualified skill path).
- Skill discovery does **not** honor `.gitignore` under skill roots; use `[skills] ignore` / `disabled` in config to hide entries.
- Sources stay **provider-neutral**: skills load from portable filesystem trees (`.grok`, `.agents`, `.claude`, `.cursor`, plugin roots, and `[skills].paths`), not from a single model vendor.

Invoking a skill loads its instructions into the turn (`InjectSkill` with skill display styling for real skills). Automatic model invocation still uses `description` / `when-to-use`; slash is the explicit path.

## Minimal mode and other constraints

In scrollback-native **minimal** mode (`grok --minimal` or `/minimal`), commands that need fullscreen-only surfaces are refused (for example `/find`, `/copy`, `/dashboard`). Mode-agnostic commands remain available by default (`available_in_minimal` denylist, not allowlist).

Other hide reasons you may see missing entries for:

- Feature flags (`/auto`, `/goal`, voice, experimental memory)
- Missing tools in the advertised toolset
- Account tier restrictions
- No active session (session-scoped commands)
- Dashboard vs agent surface filtering

## Practical recipes

**Start clean, keep history elsewhere**

```
/export
/new
```

**Switch model mid-session with effort**

```
/model Reasoning X high
# or keep model, only change effort:
/effort medium
```

**Preserve knowledge before compaction**

```
/remember staging deploy uses eu-west
/flush
/compact keep auth and deploy decisions
```

**Run a project skill without fighting a builtin name**

```
/local:compact
```

## Related pages

<CardGroup cols={2}>
  <Card title="Sessions" href="/sessions">
    Lifecycle, resume/fork/compact persistence, and CLI session tools.
  </Card>
  <Card title="Skills" href="/skills">
    SKILL.md layout, discovery paths, and user-invocable slash surface.
  </Card>
  <Card title="Custom models" href="/custom-models">
    `-m` / `/model`, BYOK endpoints, and model listing.
  </Card>
  <Card title="Permissions and safety" href="/permissions-and-safety">
    Allow/deny/ask, always-approve and auto modes, and tool authorization.
  </Card>
  <Card title="Project rules and memory" href="/project-rules-and-memory">
    AGENTS.md, `/flush` / `/dream`, and retrieval vs system-prompt injection.
  </Card>
  <Card title="Hooks" href="/hooks">
    Lifecycle hooks and how `/hooks` relates to project trust.
  </Card>
  <Card title="Plugins" href="/plugins">
    Marketplace install, trust, and plugin-bundled commands/skills.
  </Card>
  <Card title="Theming" href="/theming">
    `/theme`, `pager.toml` theme keys, and screen modes.
  </Card>
  <Card title="Keyboard shortcuts" href="/keyboard-shortcuts">
    Prompt/scrollback keys that complement slash commands.
  </Card>
  <Card title="Plan mode" href="/plan-mode">
    `/plan`, approval gates, and tool interaction.
  </Card>
</CardGroup>

## Next

<CardGroup cols={2}>
  <Card title="Configure Grok" href="/configure-grok">
    config.toml / pager.toml precedence and feature flags that gate commands.
  </Card>
  <Card title="CLI reference" href="/cli-reference">
    Top-level `grok` commands and flags outside the interactive slash menu.
  </Card>
</CardGroup>

{/* wiki-source-anchors
- crates/codegen/xai-grok-pager/docs/user-guide/04-slash-commands.md (full command catalog UX)
- crates/codegen/xai-grok-pager/src/slash/commands/mod.rs (builtin_commands registry order)
- crates/codegen/xai-grok-pager/src/slash/command.rs (CommandResult, completeness, session/minimal gates)
- crates/codegen/xai-grok-pager/src/slash/registry.rs (Builtin vs Acp, tool/tier visibility)
- crates/codegen/xai-grok-pager/src/slash/mod.rs (SlashController, fuzzy menu)
- crates/codegen/xai-grok-shell/src/session/slash_commands.rs (shell BUILTIN_COMMANDS, gates, skills)
- crates/codegen/xai-grok-pager/docs/user-guide/08-skills.md (skill slash surface and qualified names)
- crates/codegen/xai-grok-pager/src/app/dispatch/router.rs (action dispatch surface for command effects)
*/}

---

## 23. Keyboard shortcuts

> TUI key bindings for prompt and scrollback focus, cancel, rewind, vim mode, mouse actions, and multi-key Escape behavior with timing constraints.

- Page Markdown: https://grok-wiki.com/public/docs/xai-org-grok-build-90205de50458/pages/23-keyboard-shortcuts.md
- Generated: 2026-07-16T01:40:42.388Z

### Source Files

- `crates/codegen/xai-grok-pager/docs/user-guide/03-keyboard-shortcuts.md`
- `crates/codegen/xai-grok-pager/src/input`
- `crates/codegen/xai-grok-pager/src/app/mod.rs`
- `crates/codegen/xai-ratatui-textarea/src`
- `crates/codegen/xai-grok-pager/docs/user-guide/01-getting-started.md`

---
title: "Keyboard shortcuts"
description: "TUI key bindings for prompt and scrollback focus, cancel, rewind, vim mode, mouse actions, and multi-key Escape behavior with timing constraints."
---

The interactive Grok Build TUI binds keys through a single action registry (`ActionDef` / `ActionRegistry`): each action has a default key, optional alts, a `When` context (scrollback focused, prompt focused, agent screen, always), and optional double-press confirmation. Bindings are **built in and not user-remappable**. Terminal brand detection rewrites a few chords (VS Code family, Apple Terminal, Kitty keyboard protocol gaps). Scrollback **vim mode** gates bare-letter bindings; Escape is a separate policy (clear / rewind / mid-turn swallow) with an **800ms** double-press window, independent of focus and vim settings.

## Binding model

| Concept | Behavior |
|---------|----------|
| Registry | All shortcuts live in default action definitions, not scattered handlers |
| Context (`When`) | Exact match per input layer: scrollback, prompt, agent screen, global |
| Alt keys | Second (or more) bindings on the same action (e.g. `j` / `Down`) |
| Confirmation | Some actions require double-press within **1000ms** (`requires_confirmation`) |
| Esc policy | Handled outside the registry: arm clear or rewind with **800ms** TTL |
| Shortcuts bar | Bottom bar shows high-priority hints for the current focus and run state |
| Cheatsheet | `Ctrl+.` or `Ctrl+X` opens the full shortcuts help; palette lists the same actions |

Open the **command palette** with `Ctrl+P` or `?` to search actions, slash commands, and skills without memorizing chords.

## Input modes: simple vs vim scrollback

Two orthogonal UI settings control keys:

| Setting | Config | Default | Controls |
|---------|--------|---------|----------|
| Scrollback vim mode | `[ui].vim_mode` in `~/.grok/config.toml`, or `/vim-mode` | `false` | Bare-letter scrollback nav (`j`/`k`, `h`/`l`, `g`/`G`, …) |
| Prompt simple mode | `[ui].simple_mode` | `true` | Prompt editor: plain input vs experimental vim-in-prompt |

**Simple mode (default scrollback):** arrows for entry nav, `Shift+Arrow` for turn jumps, `Space` or any letter to focus the prompt (letter types into the composer).

**Vim mode (opt-in scrollback):** `j`/`k`, `H`/`L`, `J`/`K`, `h`/`l`, `e`/`E`, `g`/`G`, `y`/`Y`, `r`, `x`, `o`/`O`, and `i` for insert/focus. Arrow keys, `Tab`, `Esc`, `Space`, `PageUp`/`PageDown`, and every `Ctrl+letter` shortcut work in **both** modes.

```toml
# ~/.grok/config.toml
[ui]
vim_mode = true      # scrollback hjkl / gG (does not change the prompt editor)
simple_mode = true   # plain prompt input (default)
```

## Navigation (scrollback focused)

| Key | Alt | Action |
|-----|-----|--------|
| `j` | `Down` | Select next entry |
| `k` | `Up` | Select previous entry |
| `⇧L` | `Shift+Right` | Next turn (user prompt) |
| `⇧H` | `Shift+Left` | Previous turn |
| `⇧J` | — | Next assistant response |
| `⇧K` | — | Previous assistant response |
| `g` | — | Top of scrollback |
| `⇧G` | — | Bottom of scrollback |
| `Ctrl+K` | — | Scroll up one line (selection unchanged) |
| `Ctrl+J` | — | Scroll down one line |
| `PageUp` | — | Page up (selection → top of viewport) |
| `PageDown` | — | Page down (selection → bottom of viewport) |
| `Ctrl+U` | — | Half page up |
| `Ctrl+D` | `Shift+D` in VS Code family | Half page down |
| `o` / `⇧O` | — | Next / previous link (vim mode) |

`PageUp` / `PageDown` also scroll while the **ordinary** prompt is focused, without moving focus or the draft. An open history panel, `@` file search, slash menu, or completion dropdown keeps those keys for its own navigation.

## View and block content (scrollback focused)

| Key | Alt | Action |
|-----|-----|--------|
| `h` | `Left` | Collapse selected entry |
| `l` | `Right` | Expand selected entry |
| `e` | — | Toggle fold |
| `⇧E` | — | Expand all / collapse all |
| `Ctrl+E` | — | Expand/collapse all thinking blocks |
| `r` | — | Toggle raw markdown on selection |
| `y` | — | Copy block content |
| `⇧Y` | — | Copy block metadata (e.g. shell command / path) |
| `Enter` | `Ctrl+F` | Open block in fullscreen viewer |
| `x` | — | Kill background task on selected task block (vim mode) |

Optional `pager.toml` pin for hand folds:

```toml
[scrollback.scroll]
respect_manual_folds = true   # opt-in; default off
```

When pinned, streaming finish events leave a manual fold alone; expanding while auto-scroll follows the tail stops follow. Resume with `⇧G`, `j` at the last entry, scrolling past the bottom, or sending a prompt. `⇧E` clears all pins; `Ctrl+E` clears thinking pins.

## Focus

| Key | Context | Action |
|-----|---------|--------|
| `Tab`, `Space`, `i` (vim) | Scrollback focused | Focus prompt |
| `Tab` | Prompt focused | Focus scrollback |
| `Enter` | Prompt focused (idle) | Send prompt |
| Letter keys (simple mode) | Scrollback focused | Auto-focus prompt and type |

**Esc is not a focus key.** It never switches panes; clear / rewind / mid-turn policy owns bare Esc.

## Escape policy and timing

Bare Esc (no modifiers) is handled by a dedicated policy before generic bindings. First-press **steal-Esc** wins for overlays, modals, slash/`@`/completion dropdowns, history and scrollback search, text selection, link highlight, voice, and empty-composer exit from Bash (`!`) / Remember (`#`) / Feedback modes (even mid-turn).

| State | Gesture | Effect |
|-------|---------|--------|
| Turn running | `Esc` | Swallowed (does **not** cancel). Use `Ctrl+C` or palette cancel. |
| Turn cancelling | `Esc` | Re-sends cancel (retry if ack lost). `Ctrl+C` escalates toward quit. |
| Idle, non-empty prompt (text or image chips), **prompt focused** | **2× Esc within 800ms** | Clear prompt; text saved to prompt history. First press: “press again to clear”. |
| Idle, empty prompt, conversation has user turns, **prompt or scrollback** | **2× Esc within 800ms** | Open rewind picker (same as `/rewind`). First press silent. |
| Idle empty with no turns, or scrollback with draft / moded composer / needs-input overlay / history search open | `Esc` | Swallowed no-op |

Default clear/rewind window is **`PendingAction::ESC_DOUBLE_PRESS_TTL` = 800ms**. Tests may override via `GROK_ESC_DOUBLE_PRESS_MS` (clamped; production users should keep the default).

### Ctrl+C vs Esc

| Situation | `Ctrl+C` | `Esc` |
|-----------|----------|-------|
| Mid-turn, non-empty draft | Clear draft; turn keeps running | No-op (swallowed) |
| Mid-turn, empty prompt | Cancel turn | No-op while running; retry cancel only if already cancelling |
| Idle, non-empty prompt | Clear in **one** press | Clear only with **2× within 800ms** |
| Idle, empty + messages | — | Rewind with **2× within 800ms** |

## Agent-level and session chords

| Key | Context | Action |
|-----|---------|--------|
| `Ctrl+P` / `?` | Agent screen | Command palette |
| `Ctrl+M` | Agent (scrollback) | Model picker |
| `Ctrl+M` | Prompt focused | Toggle multiline |
| `Ctrl+C` | Agent screen | Cancel / clear draft (see above) |
| `Ctrl+O` | Agent screen | Toggle always-approve (YOLO) |
| `Ctrl+S` | Agent / welcome | Session picker (`/resume`) |
| `Ctrl+;` (`Ctrl+'` alt; **local macOS VS Code family:** primary `Ctrl+4`) | Agent | Toggle prompt queue when non-empty |
| `Shift+Tab` | Prompt | Cycle Normal → Plan → Always-approve |
| `Ctrl+G` | Agent | Send current task to background |
| `Ctrl+T` / `Ctrl+B` | Agent | Toggle todos / tasks panes |
| `Ctrl+L` | Agent | Extensions modal (**non–VS Code family**); on VS Code family this is mid-turn **send now** |
| `↑` | Empty normal prompt | Open history; `↑`/`↓` browse; `↓` at newest closes |
| `!` | Empty prompt | Enter shell mode |
| `Ctrl+.` / `Ctrl+X` | Agent | Keyboard shortcuts help |
| `F2` / `Ctrl+,` / `Cmd+,` | Agent | Settings modal |
| `Ctrl+Space` / `F8` | Always | Voice dictation (toggle or hold per config) |

**`Ctrl+.`** needs Kitty keyboard protocol (or tmux `extended-keys on`). On VS Code family, VTE, Apple Terminal, Windows Terminal, JetBrains, tmux without extended keys, screen, etc., the UI advertises **`Ctrl+X`** as primary; **`Ctrl+X` always works** as a classic control character. Use `/terminal-setup` if modified keys misbehave under tmux.

## During an active turn

| Input | Behavior |
|-------|----------|
| Plain `Enter` with text | **Queue** a follow-up (held while blocked on background tasks / subagents) |
| Plain `Enter` on empty composer | **Send now** the top queued follow-up |
| Send-now chord (below) | **Cancel current turn** and run your message next (queue/tasks/subagents keep running) |
| Blocked waiting + text + `Enter` | Cancel blocked wait and run your message next |

### Send now (interject) by terminal

| Terminal | Primary | Alternates |
|----------|---------|------------|
| Default | `Ctrl+Enter` | `Ctrl+I` |
| Apple Terminal | `Ctrl+O` | `Ctrl+Enter`, `Ctrl+I` |
| VS Code family (VS Code, Cursor, Windsurf, Zed) | **`Ctrl+L`** | *(none)* |

In multiline mode, `Shift+Enter` / `Alt+Enter` send while plain `Enter` inserts a newline — except empty composer mid-turn with a queue, where plain `Enter` still force-sends the top row. Queue follow-ups without stopping the agent; use send-now only when you want “stop and take this”.

WezTerm needs `enable_kitty_keyboard = true` for modified Enter. On Windows non–VS Code, prefer `Ctrl+I` if `Ctrl+Enter` drops the Ctrl bit. On VS Code family, unbind host **Clear** on `Ctrl+L` so the PTY receives form feed (`\x0c`).

## Global and welcome

| Key | Action | Confirmation |
|-----|--------|--------------|
| `Ctrl+N` | New session (optional git worktree) | Double-press within **1000ms** |
| `Ctrl+Q` | Quit | Double-press within **1000ms** |
| `Ctrl+D` | Quit alt (sole quit key on VS Code family) | Same |

First press arms a pending action (“press again…”); second press within 1000ms executes. Other keys or TTL expiry cancel.

**Welcome screen only:** `Ctrl+W` new worktree (in a git repo), `Ctrl+I` import Claude settings, `Ctrl+Shift+I` dismiss that row. There is **no** key for “return home” — use `/home` (alias `/welcome`).

## VS Code family remaps

In VS Code / Cursor / Windsurf / Zed integrated terminals:

| Default elsewhere | VS Code family |
|-------------------|----------------|
| Quit `Ctrl+Q` (+ `Ctrl+D` alt) | **`Ctrl+D` only** |
| Half page down `Ctrl+D` | **`Shift+D`** |
| Send now `Ctrl+Enter` / `Ctrl+I` | **`Ctrl+L` only** |
| Extensions `Ctrl+L` | Unbound → `/plugins`, `/hooks`, or palette |
| Queue toggle `Ctrl+;` | Local **macOS**: primary **`Ctrl+4`** (`;` / `'` still alts) |

## Paste, images, and mouse

### Paste and drag-and-drop

| Action | macOS | Linux | Windows |
|--------|-------|-------|---------|
| Drag image into prompt | Finder | Files / Dolphin | Explorer |
| Paste copied file path/chip | `Cmd+V` | `Ctrl+V` | `Ctrl+V` |
| Screenshot / “Copy Image” | `Cmd+V` | `Ctrl+V` | **`Alt+V`** |

Non-image files insert an absolute path as text. Windows Terminal’s default `Ctrl+V` drops image clipboards; `Alt+V` is the grok escape hatch. To force `Ctrl+V` images, null the host binding in Windows Terminal `settings.json`.

**Linux:** `Ctrl+V` reads CLIPBOARD only (no PRIMARY fallback). Unmodified **middle click** pastes PRIMARY when `DISPLAY` is set (XWayland needs `xclip` or `xsel`). `Shift+Insert` / `Shift+middle click` are terminal-native paths. Over SSH, use terminal-native paste so the local terminal feeds the PTY.

**Select all:** in-app `Cmd+A` is wired only on **Ghostty** (unbind Ghostty’s own `cmd+a` so the TUI receives it). Elsewhere the terminal keeps native Select All.

Optional: `[ui] mouse_reporting_toggle = true` enables **`Ctrl+R`** (scrollback focused) to toggle mouse capture for native terminal selection.

### Mouse

- Click a scrollback entry to select it  
- Wheel / trackpad scroll (normalized stream; tune via `scroll_mode`, `scroll_lines`, `invert_scroll`, or `GROK_SCROLL_MODE`)  
- Click the prompt to focus it  
- Hover highlight on the prompt (configurable in `pager.toml`)  
- Middle click PRIMARY paste on Linux X11/XWayland  

## Quick reference

**Scrollback — simple (default)**

```
Nav:     Up/Down   Shift+Left/Right (turns)
Scroll:  Ctrl+J/K  PgUp/PgDn  Ctrl+U/D
Focus:   Space or any letter
```

**Scrollback — vim**

```
Nav:     j/k  H/L turns  J/K responses  g/G
Scroll:  Ctrl+J/K  Ctrl+U/D (Shift+D in VS Code)  PgUp/PgDn
Fold:    h/l  e  E    Content: y Y Enter    View: r  Ctrl+E
Focus:   i  Tab  Space
```

**Prompt**

```
Send:     Enter          Newline: Shift+Enter / Alt+Enter
Multiline: Ctrl+M        Paste: Ctrl+V (Alt+V images on Windows)
Leave:    Tab            Cancel: Ctrl+C (empty) / clear draft first
Clear:    Esc Esc (800ms, non-empty idle)
Rewind:   Esc Esc (800ms, empty idle + messages)
```

**Always**

```
Palette: Ctrl+P or ?     Model: Ctrl+M (from scrollback)
YOLO:    Ctrl+O          New: Ctrl+N (×2)    Quit: Ctrl+Q or Ctrl+D
```

## Related pages

<Cards>
  <Card title="Quickstart" href="/quickstart">
    First interactive session: launch, auth, send a prompt, cancel turns, read tool output in scrollback.
  </Card>
  <Card title="Slash commands" href="/slash-commands">
    `/vim-mode`, `/rewind`, `/multiline`, `/plugins`, `/home`, and other command-palette entries.
  </Card>
  <Card title="Configure Grok" href="/configure-grok">
    `config.toml` / `pager.toml` precedence, `[ui].vim_mode`, `simple_mode`, and `grok inspect`.
  </Card>
  <Card title="Plan mode" href="/plan-mode">
    Mode cycle via Shift+Tab and how plan mode interacts with tools and permissions.
  </Card>
  <Card title="Theming" href="/theming">
    TUI appearance, screen modes, and terminal capability constraints that affect key delivery.
  </Card>
  <Card title="Troubleshooting" href="/troubleshooting">
    tmux, SSH, Kitty keyboard protocol, OSC 52 clipboard, and terminal diagnostics.
  </Card>
  <Card title="Permissions and safety" href="/permissions-and-safety">
    Always-approve (YOLO) and how tool authorization interacts with cancel and send-now.
  </Card>
  <Card title="Sessions" href="/sessions">
    New / resume / rewind lifecycle behind Ctrl+N, Ctrl+S, and Esc Esc rewind.
  </Card>
</Cards>

---

## 24. Troubleshooting

> Terminal support issues (tmux, SSH, truecolor, OSC 52 clipboard), color warnings, auth recovery, MCP startup failures, and diagnostics probes backed by the pager diagnostics module.

- Page Markdown: https://grok-wiki.com/public/docs/xai-org-grok-build-90205de50458/pages/24-troubleshooting.md
- Generated: 2026-07-16T02:33:09.513Z

### Source Files

- `crates/codegen/xai-grok-pager/docs/user-guide/21-terminal-support.md`
- `crates/codegen/xai-grok-pager/src/diagnostics.rs`
- `crates/codegen/xai-grok-pager/docs/user-guide/02-authentication.md`
- `crates/codegen/xai-grok-shell/src/auth/recovery.rs`
- `crates/codegen/xai-grok-shell/src/mcp_doctor.rs`
- `crates/codegen/xai-tty-utils/src`
- `crates/codegen/xai-grok-shell/README.md`

---
title: "Troubleshooting"
description: "Terminal support issues (tmux, SSH, truecolor, OSC 52 clipboard), color warnings, auth recovery, MCP startup failures, and diagnostics probes backed by the pager diagnostics module."
---

Grok Build surfaces failures through a route-aware pager diagnostics engine (`/terminal-setup`), automatic 401 auth recovery against `~/.grok/auth.json`, and `grok mcp doctor` probes that start configured MCP servers and report per-check health.

## First probes

| Surface | When to use | What it reports |
|---------|-------------|-----------------|
| `/terminal-setup` (aliases `/terminal-check`, `/terminal-info`) | Colors, clipboard, keyboard, tmux/SSH oddities inside the TUI | Terminal brand, multiplexer, SSH flag, color level, available themes, clipboard routes, structured issues + fixes |
| Welcome-screen startup banners | Over SSH or for high-priority local input/clipboard issues | Short summary; full detail is in `/terminal-setup` |
| `grok mcp doctor` | MCP servers fail to start or expose tools | Config sources, per-server checks, exit 1 if any fail |
| `grok --debug` / `GROK_LOG_FILE` | Auth, network, spawn failures | File logs (TUI captures stderr; bare `RUST_LOG` alone is not enough) |
| `grok login` / `grok logout` | Session rejected or wrong account | Replaces or clears `~/.grok/auth.json` |

<Tip>
Run `/terminal-setup` first for any TUI display, color, or clipboard issue. Expect `color truecolor` and `themes all` when 24-bit RGB is available.
</Tip>

---

## Terminal diagnostics

The pager diagnostics module classifies environment problems into structured `TerminalWarning` records (category, message, optional fix, config path, note). `/terminal-setup` runs the same engine used for startup warnings and prints:

1. **Environment** — terminal brand, optional XTVERSION self-report, multiplexer/Byobu, SSH, color level, themes, keyboard/newline notes  
2. **Clipboard routes** — native tool, tmux buffer, OSC 52, Wayland data-control  
3. **Issues** — each warning with fix/note when available  

### Detected terminals

Brand detection uses environment variables and (over SSH) XTVERSION. Known brands include Apple Terminal, Ghostty, iTerm2, Warp, WezTerm, Kitty, Alacritty, Rio, foot, VS Code / Cursor / Windsurf / Zed, JetBrains IDEs, Grok Desktop, VTE-based terminals, and Windows Terminal.

Detection limits:

- Inside tmux, outer terminal identity often does not reach the pager  
- Over SSH, many terminal variables are not forwarded  
- tmux’s global environment reflects the first client that attached to the server, not the current client  

### Warning categories

| Category | Typical cause | Primary fix |
|----------|---------------|-------------|
| `Clipboard` | tmux `set-clipboard` not `on`/`external` | `set -g set-clipboard on` in tmux config |
| `DcsPassthrough` | tmux `allow-passthrough` off (tmux 3.3+) | `set -g allow-passthrough on` |
| `ControlMode` | `tmux -CC` / control mode | Expect inline or unreliable fullscreen; force with `[terminal] alt_screen = "always"` if needed |
| `ByobuScreen` | Byobu on GNU screen | Prefer Byobu on tmux |
| `UnsupportedTerminal` | Apple Terminal over SSH (no OSC 52) | Use OSC 52–capable terminal or `grok wrap` |
| `TmuxExtendedKeysOff` | `extended-keys` off | `set -g extended-keys on` then reload tmux |
| `WezTermKittyKeyboardOff` | Kitty keyboard protocol off | `config.enable_kitty_keyboard = true` in `wezterm.lua` (local); `\` then Enter over SSH |
| `WaylandNoDataControl` | Compositor lacks data-control | Stay focused mid-copy; install `wl-clipboard` when suggested |
| `LimitedColorSupport` | Below truecolor / `NO_COLOR` | `COLORTERM=truecolor`, tmux RGB features, or switch terminal |
| `FocusTrackingUnavailable` | Unfocused notifications on unsupported brand | `[ui.notifications] condition = "always"` |
| `SandboxProfileConflict` | Project vs user sandbox profile clash | Prefer user `~/.grok/sandbox.toml` (banner notes the resolution) |

Query failure is silent: a missing tmux option value does **not** warn. Only an explicit non-good value (for example `set-clipboard off`) produces a clipboard/DCS warning.

Welcome-screen summarization is SSH-gated for most tmux clipboard noise. Local high-priority banners include WezTerm Shift+Enter, Wayland focus-dependent copies, and sandbox profile conflicts.

---

## Truecolor and washed-out themes

Truecolor-only themes stay hidden when color level is below truecolor. `/terminal-setup` shows the level and which themes remain.

<Steps>
  <Step title="Set COLORTERM">
    ```bash
    # ~/.zshrc or ~/.bashrc
    export COLORTERM=truecolor
    ```
  </Step>
  <Step title="Configure tmux for 24-bit RGB">
    ```tmux
    # ~/.tmux.conf
    set -g default-terminal "tmux-256color"
    set -as terminal-features ",*:RGB"
    ```
    Then `tmux source-file ~/.tmux.conf` (or detach/reattach) and restart Grok.
  </Step>
  <Step title="Verify">
    Run `/terminal-setup`. Expect `color truecolor` and `themes all`. Apple Terminal is inherently 256-color — truecolor themes stay unavailable; switch to a truecolor terminal (for example Ghostty) if needed.
  </Step>
</Steps>

If `NO_COLOR` is set, themed colors are disabled; unset it and restart Grok.

---

## Clipboard (native, tmux, OSC 52)

Grok may write through up to three routes (shown under **Clipboard routes** in `/terminal-setup`):

| Route | Behavior |
|-------|----------|
| **native** | OS clipboard first (tool name printed next to the row) |
| **tmux buffer** | Also `tmux load-buffer` when inside tmux |
| **OSC 52** | Escape sequence for the outer terminal. Always emitted inside tmux; outside tmux, on Linux, over SSH, or in a container without a display |

Recommended tmux clipboard + passthrough:

```tmux
set -g set-clipboard on
set -g allow-passthrough on
```

### Platform notes

**Linux Wayland** — With compositor data-control (GNOME 48+, KDE, Sway, Hyprland; `/terminal-setup` `data-control` row = `yes`), copies work even if the terminal loses focus. Without it, keep focus until the copy toast confirms and install `wl-clipboard` (`wl-copy`) when suggested. To force CLI tools only: `GROK_CLIPBOARD_NO_DATA_CONTROL=1`.

**Linux X11** — PRIMARY and CLIPBOARD are separate. Middle-click (unmodified) can read PRIMARY when `DISPLAY` is set; `Ctrl+V` reads CLIPBOARD only. `Shift+Insert` remains terminal-native selected-text paste.

**SSH selected text** — A remote Grok process usually cannot read the local terminal’s PRIMARY/CLIPBOARD. Use terminal-native `Shift+Insert`, or hold `Shift` while middle-clicking when the terminal uses that gesture to bypass mouse reporting.

**iTerm2** — Enable **Settings → General → Selection → “Applications in terminal may access clipboard”** (off by default). Without it, OSC 52 writes are ignored.

**Apple Terminal + SSH** — Terminal.app ignores OSC 52, so remote copies never reach the local clipboard.

### `grok wrap` (experimental)

```bash
grok wrap ssh user@host
grok wrap docker exec -it <container> bash
grok wrap kubectl exec -it <pod> -- bash
```

`grok wrap` runs the command in a local PTY, intercepts OSC 52 (including tmux-wrapped DCS forms), and writes payloads to the local clipboard. Useful when the outer terminal or remote environment cannot deliver clipboard itself.

<Warning>
`grok wrap` is experimental and may misbehave in some setups. On PTY setup failure it falls back to an unwrapped exec.
</Warning>

---

## Keyboard and mouse

### WezTerm: Shift+Enter / Ctrl+Enter

WezTerm defaults `enable_kitty_keyboard = false`. Without the Kitty keyboard protocol, Shift+Enter is byte-identical to Enter (submits instead of newline), and stock Alt+Enter toggles fullscreen.

```lua
-- ~/.config/wezterm/wezterm.lua (after config = wezterm.config_builder())
config.enable_kitty_keyboard = true
```

Reload WezTerm and restart `grok`. Until fixed, type `\` then Enter for a newline. Over SSH, the pager does not yet negotiate the protocol for WezTerm; the same `\`+Enter workaround applies.

### VS Code / Cursor / Windsurf / Zed: Shift+Enter

xterm.js mis-encodes shifted keys, so Grok does not negotiate Kitty keyboard protocol. Use **Alt+Enter** for newline. `/terminal-setup` shows `newline Alt+Enter (...)` when this applies. Same over SSH when `TERM_PROGRAM` is not forwarded (brand falls back to Unknown).

### Apple Terminal interject

Grok binds **Ctrl+O** to interject (Ctrl+Enter is unreliable).

### tmux extended keys

If `extended-keys` is off, modifier chords may not reach the pager:

```tmux
set -g extended-keys on
```

Reload with `tmux source-file <config>` or detach/reattach.

### Mouse reporting

If Grok scrolling stops and the native scrollbar takes over:

- **Apple Terminal:** View → Allow Mouse Reporting (`Cmd+R`)  
- **iTerm2:** Profiles → Terminal → Enable mouse reporting  

### Zellij key interception

Zellij intercepts many Ctrl/Alt chords. On Zellij 0.41+, use the **Unlock-First (non-colliding)** preset so keys pass through while locked; unlock with `Ctrl+g` for Zellij pane management.

### Fullscreen / alternate screen

Zellij and tmux control mode intentionally run inline (no alt screen) unless forced:

```toml
# ~/.grok/pager.toml
[terminal]
alt_screen = "always"
```

CLI: `--no-alt-screen` disables alt-screen entirely for debugging.

### Byobu

Byobu on GNU screen is best-effort only. Prefer Byobu backed by tmux.

---

## Authentication recovery

On HTTP 401, `UnauthorizedRecovery` walks:

1. **ReloadFromDisk** — re-read `~/.grok/auth.json` under a file lock; adopt the token only if it differs from the rejected one (another process may have refreshed)  
2. **RefreshFromAuthority** — OIDC refresh or external auth binary, unless the live token is inside the ±120s fresh-mint guard (avoids re-minting on stale/lagged 401s)  
3. **DevboxRecovery** — on devbox environments, purge and mint fresh OIDC when automatic OIDC is allowed  
4. **Done** — recovery exhausted  

| Outcome / error | User action |
|-----------------|-------------|
| Silent recovery success | None; request retries with new token |
| Transient refresh failure | Retries stay allowed; do not force re-login on a blip |
| `Token expired` / `RecoveryExhausted` / permanent refresh reject | `grok login` |
| API key / legacy session (no refresh authority) | Rotate key or `grok login`; 401 stands after disk reload |
| Pinned team mismatch | `grok login` with the required team |
| API-key auth disabled by admin | `grok login` (OIDC/session path) |

### Manual recovery

```bash
# Re-run sign-in (replaces cached session)
grok login
# Headless / remote: device-code flow
grok login --device-auth

# Clear session so XAI_API_KEY can take over
grok logout
# or delete ~/.grok/auth.json

export XAI_API_KEY="xai-..."
```

Stored session tokens take precedence over `XAI_API_KEY` while present. Credentials without a server-provided expiry fall back to a 30-day lifetime in the authentication docs; tokens refresh in the background when a refresh authority exists.

### Debug auth

```bash
# Custom path; filter module-level if needed
GROK_LOG_FILE=/tmp/grok-auth.log RUST_LOG="info,xai_grok_shell::auth=debug" grok -p "hello"
grep "auth:" /tmp/grok-auth.log

# Per-session debug firehose
grok --debug
tail -f ~/.grok/debug/latest.txt
```

External auth provider failures surface as spawn failures, empty/unparseable stdout, non-zero exit, or timeouts (initial ~60s, mid-session refresh ~5s). Ensure the binary prints **only** the token on stdout and status/URLs on stderr.

---

## MCP startup failures

`grok mcp doctor` discovers servers from user/project `config.toml`, plugins, Claude import paths (when not skipped), `.mcp.json`, and managed `grok.com` servers when xAI OIDC auth is eligible. It then probes (up to 8 concurrent):

| Check | Failure meaning | Hint direction |
|-------|-----------------|----------------|
| command found (stdio) | Binary not on `PATH` | Fix command / install tool |
| server started | Spawn or timeout | Permissions, args, `startup_timeout_sec` |
| handshake OK | Protocol init failed | Server logs / transport URL |
| N tools discovered | Empty or failed `tools/list` | Server config / logs |
| disabled in config | Listed in `disabled_mcp_servers` or `enabled = false` | Enable server |
| folder untrusted | Project-scoped server in untrusted clone | Re-run with trust for that folder |
| blocked by organization policy | Managed allowlist deny | Org policy / allowlist |

```bash
grok mcp doctor               # all servers
grok mcp doctor github        # one name
grok mcp doctor --json        # machine-readable DoctorReport
```

Exit status is non-zero when any probed server fails, or when a name filter matches nothing.

Common config fix for slow spawns:

```toml
[mcp_servers.filesystem]
startup_timeout_sec = 30
```

Test the stdio command outside Grok first (`npx …`, absolute path, env vars). Prefer native HTTP/SSE `url` entries over wrapping remote MCP in extra stdio proxies when the server exposes HTTP.

Managed discovery skips with reasons such as `not logged in`, `auth expired — run grok login`, non-xAI auth mode, or fetch errors. Claude-imported sources may show as skipped when `claude_compat imported = true`.

---

## Debug logging and inspect

| Mechanism | Behavior |
|-----------|----------|
| `grok --debug` | Per-session log under `~/.grok/debug/<sessionId>.txt`; fixed first-party `debug` filter (not narrowed by `RUST_LOG`) |
| `GROK_LOG_FILE=/path` | Custom log file; defaults to `debug`, honors `RUST_LOG` filters |
| `~/.grok/debug/latest.txt` | Symlink to most recent session log (Unix) |
| `grok inspect` / `grok inspect --json` | Resolved project instructions, skills, plugins, MCP, hooks, permissions for the current directory |

TTY-owned child processes are detached via `xai_tty_utils` (`setsid` / `setpgid` on Unix, `CREATE_NO_WINDOW` on Windows) so tools and diagnostic subprocesses do not steal the TUI’s controlling terminal. Unexpected mouse garbage or credential prompts in the TUI usually mean a child still opened `/dev/tty` — report via `/feedback` with environment details from `/terminal-setup`.

---

## Quick failure matrix

| Symptom | Likely cause | Action |
|---------|--------------|--------|
| Washed-out / missing truecolor themes | No RGB / missing `COLORTERM` | Export `COLORTERM=truecolor`; tmux RGB features; `/terminal-setup` |
| Copy over SSH fails (Apple Terminal) | No OSC 52 | `grok wrap ssh …` or switch terminal |
| Copy fails in tmux | `set-clipboard` / passthrough off | Enable both; reload tmux |
| Shift+Enter submits (WezTerm) | Kitty keyboard off | Enable in `wezterm.lua` or `\`+Enter |
| Shift+Enter submits (VS Code family) | xterm.js limitation | Alt+Enter |
| Keys stolen (Zellij) | Zellij bindings | Unlock-First preset |
| Inline instead of fullscreen | Control mode / Zellij | Expected, or `alt_screen = "always"` |
| Re-auth banner / 401 loop | Recovery exhausted | `grok login`; check logs for `auth:` |
| MCP tools missing | Spawn/handshake/disabled/untrusted | `grok mcp doctor --json` |
| Mouse scroll broken | Mouse reporting off | Terminal setting (Apple Terminal / iTerm2) |

Still stuck after probes: run `/feedback` with `/terminal-setup` output (and `grok mcp doctor --json` when MCP is involved).

## Related pages

<CardGroup>
  <Card title="Authentication" href="/authentication">
    Login flows, device code, API keys, and credential storage in `~/.grok/auth.json`.
  </Card>
  <Card title="MCP servers" href="/mcp-servers">
    Register and manage stdio/HTTP MCP servers, timeouts, and enable/disable flags.
  </Card>
  <Card title="Theming" href="/theming">
    Color-support detection, truecolor-only themes, and `/theme`.
  </Card>
  <Card title="Configure Grok" href="/configure-grok">
    `config.toml` / `pager.toml` precedence and `grok inspect`.
  </Card>
  <Card title="Keyboard shortcuts" href="/keyboard-shortcuts">
    Interject, newline chords, and multipress Escape behavior.
  </Card>
  <Card title="CLI reference" href="/cli-reference">
    Top-level commands including `wrap`, `mcp`, `login`, `inspect`, and `update`.
  </Card>
</CardGroup>

---

## 25. Build from source

> Workspace build of xai-grok-pager-bin: Rust toolchain pin, protoc/dotslash, cargo run/check/test per-crate, binary name xai-grok-pager vs shipped grok, and contribution policy constraints.

- Page Markdown: https://grok-wiki.com/public/docs/xai-org-grok-build-90205de50458/pages/25-build-from-source.md
- Generated: 2026-07-16T01:28:12.537Z

### Source Files

- `README.md`
- `Cargo.toml`
- `rust-toolchain.toml`
- `crates/codegen/xai-grok-pager-bin/src/main.rs`
- `crates/codegen/xai-grok-pager-bin/Cargo.toml`
- `bin/protoc`
- `CONTRIBUTING.md`
- `clippy.toml`

---
title: "Build from source"
description: "Workspace build of xai-grok-pager-bin: Rust toolchain pin, protoc/dotslash, cargo run/check/test per-crate, binary name xai-grok-pager vs shipped grok, and contribution policy constraints."
---

The public tree is a Cargo workspace that produces the Grok Build CLI/TUI from the composition-root package `xai-grok-pager-bin`. That package owns the binary target named `xai-grok-pager` (still the on-disk artifact name in `target/`); official installers ship the same program as `grok`. The root `Cargo.toml` is generated and should be treated as read-only; edit per-crate manifests instead. Builds expect the pin in `rust-toolchain.toml` (channel `1.92.0`, `rustfmt` + `clippy`) and a working Protocol Buffers compiler resolved through `xai-proto-build` — typically the DotSlash launcher at `bin/protoc`, or `$PROTOC` / a `protoc` on `PATH`.

## When to build from source

| Goal | Prefer |
|------|--------|
| Day-to-day use | Released `grok` binary via install scripts |
| Inspect or run the open source tree | `cargo run -p xai-grok-pager-bin` |
| Local development / debugging | Debug build of `xai-grok-pager-bin` |
| Closer-to-ship artifact | `cargo build -p xai-grok-pager-bin --profile release-dist` |

For install layout, PATH, and version pins of the shipped binary, see [Installation](/installation).

## Requirements

- **Rust toolchain** — pinned by `rust-toolchain.toml`. With `rustup`, the first `cargo` invocation in the repo installs channel `1.92.0` and components `rustfmt` and `clippy`. The file also lists Linux cross targets (`x86_64-unknown-linux-gnu`, `aarch64-unknown-linux-gnu`); the host target is added automatically.
- **Workspace edition** — workspace package edition is `2024` (set under `[workspace.package]`).
- **protoc** — needed when crates with protobuf build scripts compile (for example `xai-grok-tools-api` via `xai-proto-build`). Resolution order in `find_protoc()`:
  1. `$PROTOC` (absolute path override; used by hermetic/Bazel-style setups)
  2. `bin/protoc` found by walking up from the current directory (DotSlash wrapper for local dev)
  3. `protoc` on `$PATH`
- **DotSlash** — `bin/protoc` starts with `#!/usr/bin/env dotslash` and downloads pinned protobuf **v29.3** artifacts for `macos-aarch64`, `linux-x86_64`, and `linux-aarch64`. If the wrapper fails with a version check error, install DotSlash (`cargo install dotslash`) or point `$PROTOC` at a real compiler.
- **Host OS** — macOS and Linux are supported build hosts. Windows builds are best-effort and not tested from this tree.

<Warning>
On GitHub Actions, missing `protoc` is a hard error. Outside CI, `find_protoc` may return `None` and only print a warning — proto-generating crates can still fail later if they require a compiler.
</Warning>

## Quick build and run

From the repository root:

```sh
# Install pinned toolchain (rustup) and build + launch the TUI
cargo run -p xai-grok-pager-bin

# Fast typecheck of the composition root
cargo check -p xai-grok-pager-bin

# Optimized binary (default release profile)
cargo build -p xai-grok-pager-bin --release
# → target/release/xai-grok-pager

# Hardened distribution profile (thin LTO, codegen-units=1; used for shipping)
cargo build -p xai-grok-pager-bin --profile release-dist
# → target/release-dist/xai-grok-pager
```

Always pass `-p <crate>` (or an explicit package set). Full-workspace `cargo check` / `cargo test` without package filters is slow on this monorepo-sized tree.

### Binary name vs product name

| Surface | Name |
|---------|------|
| Cargo package | `xai-grok-pager-bin` |
| Binary target / `default-run` | `xai-grok-pager` |
| Debug/release artifact path | `target/{debug,release}/xai-grok-pager` |
| Official install / docs | `grok` |

You can put the built binary on your `PATH` under either name; CLI behavior matches the shipped `grok` entrypoint (TUI by default, headless `-p`, `agent`, etc.). Tests and the PTY harness look for `xai-grok-pager` (and env overrides such as `PAGER_BINARY` / `CARGO_BIN_EXE_xai-grok-pager`).

## Composition root: what `xai-grok-pager-bin` links

`xai-grok-pager-bin` exists so the binary can link both the main pager library and the optional `xai-grok-pager-minimal` render mode without a Cargo cycle (`minimal` depends on `xai-grok-pager`, so the library cannot depend back on it). At startup, `main` installs minimal-mode IoC hooks, then drives the same surfaces as the product CLI:

- Interactive TUI (`xai-grok-pager`)
- Agent runtime entry points from `xai-grok-shell` (headless, leader, stdio/ACP)
- Update / version / telemetry / workspace / crash-handler crates

Default package features include `jemalloc` (Unix global allocator) and `sandbox-enforce`. Feature `release-dist` is forwarded into the pager library for distribution builds.

```text
xai-grok-pager-bin (binary: xai-grok-pager)
├── xai-grok-pager          # TUI + app CLI parsing
├── xai-grok-pager-minimal  # optional render mode (wired at startup)
├── xai-grok-shell          # agent / headless / leader / ACP
├── xai-grok-update
├── xai-grok-version
├── xai-grok-telemetry
├── xai-grok-workspace
├── xai-crash-handler
└── xai-acp-lib
```

Related runtime crates commonly edited nearby: `xai-grok-tools`, `xai-grok-config`, `xai-grok-sandbox`, and the rest of the `crates/codegen/` closure listed in the workspace members.

## Version string baked into the binary

`crates/codegen/xai-grok-pager-bin/build.rs` emits `VERSION_WITH_COMMIT`:

1. Version from `GROK_VERSION` if set, else `CARGO_PKG_VERSION`
2. Short git commit from `git rev-parse --short HEAD`, or `unknown` if git is unavailable

That string feeds crash reporting, Sentry release tags, and other startup paths. `xai-grok-version` similarly prefers `GROK_VERSION` at compile time for the public version constant.

```sh
GROK_VERSION=0.1.220-alpha.4 cargo build -p xai-grok-pager-bin
```

## Development workflow

Prefer package-scoped commands:

```sh
cargo check -p xai-grok-config
cargo test  -p xai-grok-config
cargo clippy -p xai-grok-pager-bin
cargo fmt --all
```

| Tooling | Location / notes |
|---------|------------------|
| Toolchain pin | `rust-toolchain.toml` — bump one point version at a time; after a bump run `cargo check --all-targets --workspace` and `cargo clippy --all-targets --workspace` |
| rustfmt | `rustfmt.toml` (field-init shorthand) |
| Clippy (codegen crates) | Root `clippy.toml` — Clippy does **not** merge configs; nearest file wins. Disallows `std`/`tokio` `canonicalize` in favor of `dunce::canonicalize` (Windows verbatim-path safety). Large-error threshold `256` for tonic-related types |
| Workspace lints | `[workspace.lints.clippy]` in the generated root `Cargo.toml` |
| Lockfile | `Cargo.lock` is present; keep dependency changes intentional |

### Profiles (selection)

| Profile | Role |
|---------|------|
| `dev` | Local default; `panic = "abort"`, high `codegen-units` |
| `release` | Optimized local release; incremental, `panic = "abort"` |
| `release-dist` | Shipping hardening (thin LTO, `codegen-units = 1`, line-table debug retained for sidecar extraction) |
| `release-dist-jemalloc` | Named alias of `release-dist` for desktop/workflow references |

### protoc / DotSlash troubleshooting

| Symptom | Likely cause | Fix |
|---------|--------------|-----|
| `protoc --version failed, likely dotslash is missing` | `bin/protoc` present but DotSlash not installed | `cargo install dotslash`, or set `$PROTOC` to a system `protoc` |
| `bin/protoc found … failed to execute; trying protoc from PATH` | DotSlash unavailable (e.g. some remote/Bazel envs) | Install a hermetic `protoc` and set `$PROTOC` |
| Hard fail only on GitHub Actions | No `protoc` anywhere | Provide `$PROTOC` or install `protoc` in the job image |
| Host not in DotSlash platforms | e.g. macOS x86_64 or Windows | Install platform `protoc` and set `$PROTOC` |

`bin/protoc` currently pins protobuf **29.3** zips for three platforms only (`macos-aarch64`, `linux-x86_64`, `linux-aarch64`). Other hosts should not rely on DotSlash for this launcher.

## Repository layout (build-relevant)

| Path | Role |
|------|------|
| `crates/codegen/xai-grok-pager-bin` | Composition root; `[[bin]] name = "xai-grok-pager"` |
| `crates/codegen/xai-grok-pager` | TUI and most application logic |
| `crates/codegen/xai-grok-shell` | Agent runtime and non-TUI entry points |
| `crates/build/xai-proto-build` | Shared prost/tonic configure + `find_protoc` |
| `bin/protoc` | DotSlash launcher for pinned protoc |
| `third_party/` | Vendored Mermaid stack and related ports |
| `Cargo.toml` | Generated workspace root — do not hand-edit for routine work |

## Contribution and license constraints

External pull requests and unsolicited patches are **not** accepted. The public tree is for source transparency and local builds under the Apache License, Version 2.0 (`LICENSE`). There is no CLA for outsiders because contributions are not taken.

- Security issues: report via the process in `SECURITY.md` (HackerOne); do not open public vulnerability issues.
- Third-party / vendored notices: `THIRD-PARTY-NOTICES`, crate-local notices under tools, and `third_party/NOTICE`.

## Related product surfaces after a local build

Once `xai-grok-pager` runs, the same authentication, config, and runtime flags apply as for a released `grok`:

- First launch may open browser OAuth — see [Authentication](/authentication)
- Config and feature resolution — [Configure Grok](/configure-grok)
- Non-interactive runs — [Headless mode](/headless-mode)
- ACP / IDE embedding — [Agent mode (ACP)](/agent-mode)

## Next

<CardGroup cols={2}>
  <Card title="Installation" href="/installation">
    Install the released `grok` binary, PATH under `~/.grok/bin`, and version verification.
  </Card>
  <Card title="Overview" href="/overview">
    Product surfaces (TUI, headless, ACP) and the shortest path to a first successful turn.
  </Card>
  <Card title="CLI reference" href="/cli-reference">
    Top-level commands and shared runtime flags available on the built binary.
  </Card>
  <Card title="Troubleshooting" href="/troubleshooting">
    Terminal, auth, MCP, and diagnostics issues after launch.
  </Card>
</CardGroup>

---
