Publish harness and TUI open-source
initial sync from the monorepo
This commit is contained in:
commit
c68e39f604
2734 changed files with 1437016 additions and 0 deletions
|
|
@ -0,0 +1,255 @@
|
|||
# Getting Started
|
||||
|
||||
Grok Build is a terminal-based AI coding assistant from SpaceXAI. It runs as a TUI (Terminal User Interface) that understands your codebase, executes shell commands, edits files, searches the web, and manages tasks.
|
||||
|
||||
You can use it interactively as a full-screen TUI, run it headlessly for scripting and CI/CD, or integrate it into editors via the Agent Client Protocol (ACP).
|
||||
|
||||
---
|
||||
|
||||
## Installation
|
||||
|
||||
Install the latest stable release (macOS, Linux, or Windows via Git Bash):
|
||||
|
||||
```bash
|
||||
curl -fsSL https://x.ai/cli/install.sh | bash
|
||||
```
|
||||
|
||||
Install a specific version:
|
||||
|
||||
```bash
|
||||
curl -fsSL https://x.ai/cli/install.sh | bash -s 0.1.42
|
||||
```
|
||||
|
||||
On **Windows (PowerShell)**, use the native PowerShell installer:
|
||||
|
||||
```powershell
|
||||
irm https://x.ai/cli/install.ps1 | iex
|
||||
```
|
||||
|
||||
Install a specific version:
|
||||
|
||||
```powershell
|
||||
$env:GROK_VERSION="0.1.42"; irm https://x.ai/cli/install.ps1 | iex
|
||||
```
|
||||
|
||||
The PowerShell installer automatically adds `%USERPROFILE%\.grok\bin` to your User PATH. Alternatively, install via [Git for Windows](https://gitforwindows.org/) (Git Bash) or MSYS2 using the bash script above. WSL users get the Linux binary automatically.
|
||||
|
||||
Verify the installation:
|
||||
|
||||
```bash
|
||||
grok --version
|
||||
```
|
||||
|
||||
Update to the latest version at any time:
|
||||
|
||||
```bash
|
||||
grok update
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## First Launch
|
||||
|
||||
Start Grok by running:
|
||||
|
||||
```bash
|
||||
grok
|
||||
```
|
||||
|
||||
On first launch, Grok opens your browser to authenticate with grok.com. After you sign in, Grok stores your credentials in `~/.grok/auth.json`, where they persist across sessions. Grok refreshes your credentials automatically and prompts you to sign in again when they can no longer be renewed.
|
||||
|
||||
If you prefer API key authentication (e.g., for CI/CD or environments without a browser), set the `XAI_API_KEY` environment variable instead:
|
||||
|
||||
```bash
|
||||
export XAI_API_KEY="xai-..."
|
||||
grok
|
||||
```
|
||||
|
||||
See [Authentication](02-authentication.md) for the full set of auth options including OIDC, external auth providers, and device code flow.
|
||||
|
||||
---
|
||||
|
||||
## Basic Interaction
|
||||
|
||||
Once authenticated, Grok presents a full-screen TUI with two main areas:
|
||||
|
||||
- **Scrollback** -- the conversation history showing your prompts, Grok's responses, tool calls, file edits, and more.
|
||||
- **Prompt** -- the input area at the bottom where you type messages.
|
||||
|
||||
Type a message and press `Enter` to send it. Grok reads files, runs commands, and edits code as needed. Each tool run streams into the scrollback in real time.
|
||||
|
||||
Press `Tab` to move focus between the prompt and the scrollback. While a turn is running, `Ctrl+C` cancels it (or clears a non-empty draft first); `Esc` is a no-op mid-turn. Idle, press `Esc` twice within 800ms to clear a non-empty prompt, or (with an empty prompt and conversation messages) to open rewind — see [Keyboard Shortcuts](03-keyboard-shortcuts.md#escape). With the scrollback focused, use the arrow keys to select entries and to collapse or expand them. To navigate with `j`/`k` and fold with `h`/`l` instead, enable Vim mode.
|
||||
|
||||
### File References
|
||||
|
||||
Use `@` in your prompt to attach files:
|
||||
|
||||
```
|
||||
@src/main.rs # Attach a file
|
||||
@src/main.rs:10-50 # Attach lines 10-50
|
||||
@src/ # Browse a directory
|
||||
```
|
||||
|
||||
The `@` operator opens a fuzzy file picker. By default it respects `.gitignore` and hides dotfiles. Prefix with `!` to search hidden files:
|
||||
|
||||
```
|
||||
@!.github # Search hidden files
|
||||
@!.env # Attach a .env file
|
||||
```
|
||||
|
||||
### Permissions
|
||||
|
||||
By default, Grok asks for permission before executing shell commands or editing files. You can approve individually or toggle always-approve mode:
|
||||
|
||||
- Press `Ctrl+O` to toggle always-approve mode
|
||||
- Use the `--yolo` flag at launch: `grok --yolo`
|
||||
- Type `/always-approve` in the prompt to toggle the mode
|
||||
|
||||
---
|
||||
|
||||
## Key Concepts
|
||||
|
||||
### Sessions
|
||||
|
||||
Every conversation is a **session**. Sessions are automatically saved to `~/.grok/sessions/` and can be resumed later. Each session tracks the full conversation history, tool calls, file edits, and task state.
|
||||
|
||||
- Start a new session: `Ctrl+N` or `/new`
|
||||
- Resume a previous session: `/resume` in the TUI, or `--resume <ID>` from the CLI
|
||||
- Continue the most recent session: `grok -c`
|
||||
|
||||
### Scrollback
|
||||
|
||||
The scrollback is the main display area. It shows:
|
||||
|
||||
- **User prompts** -- your messages, rendered as sticky headers
|
||||
- **Agent messages** -- Grok's responses with full markdown rendering and syntax highlighting
|
||||
- **Thinking blocks** -- Grok's reasoning process (collapsible)
|
||||
- **Tool calls** -- file edits (with inline diffs), command executions, search results, and more
|
||||
- **Task lists** -- TODO items tracking progress
|
||||
|
||||
Collapse or expand the selected entry with the `Left`/`Right` arrow keys (or `h`/`l` and `e` in Vim mode). In Vim mode, press `y` to copy its content and `Y` to copy its metadata (for example, the command that ran). Press `Enter` to open it in the fullscreen viewer (in any mode).
|
||||
|
||||
### Tools
|
||||
|
||||
Grok has built-in tools for:
|
||||
|
||||
| Tool | Description |
|
||||
|------|-------------|
|
||||
| `read_file` / `search_replace` | Read and edit files with line-precise changes |
|
||||
| `grep` | Regex search across your codebase (powered by ripgrep) |
|
||||
| `list_dir` | List directory contents |
|
||||
| `run_terminal_command` | Execute shell commands |
|
||||
| `web_search` / `web_fetch` | Search the web and fetch URLs |
|
||||
| `todo_write` | Create and manage task lists |
|
||||
| `spawn_subagent` | Spawn parallel subagent sessions |
|
||||
| `memory_search` | Search cross-session memory |
|
||||
|
||||
Tools can be extended with [MCP servers](05-configuration.md#mcp-servers) for integrations like GitHub, databases, and more.
|
||||
|
||||
### Slash Commands
|
||||
|
||||
Type `/` in the prompt to access commands. These provide quick actions without writing a full prompt:
|
||||
|
||||
```
|
||||
/model grok-build # Switch model
|
||||
/compact # Compress conversation history
|
||||
/always-approve # Toggle always-approve mode
|
||||
/new # Start a new session
|
||||
```
|
||||
|
||||
See [Slash Commands](04-slash-commands.md) for the complete reference.
|
||||
|
||||
---
|
||||
|
||||
## Common Launch Options
|
||||
|
||||
```bash
|
||||
# Launch the interactive TUI and submit an initial prompt as the first turn
|
||||
grok "fix the failing auth test and run it"
|
||||
|
||||
# Initial prompt in a new git worktree. Use --worktree=<name> (with `=`) so the
|
||||
# prompt isn't swallowed as the worktree name — `grok -w "refactor module X"`
|
||||
# would treat "refactor module X" as the worktree label, not the prompt.
|
||||
grok --worktree=feat "refactor module X"
|
||||
|
||||
# Base the worktree on a specific branch (e.g. main) instead of the current HEAD:
|
||||
grok -w --ref main "implement feature from main"
|
||||
|
||||
|
||||
# Start in a specific project directory
|
||||
grok --cwd ~/projects/my-app
|
||||
|
||||
# Add project-specific rules
|
||||
grok --rules "Always use TypeScript. Prefer functional components."
|
||||
|
||||
# Auto-approve all tool executions
|
||||
grok --yolo
|
||||
|
||||
# Use a specific model
|
||||
grok -m grok-build
|
||||
|
||||
# Resume a previous session
|
||||
grok --resume <session-id>
|
||||
|
||||
# Continue the most recent session
|
||||
grok -c
|
||||
|
||||
# Experimental scrollback-native render mode. Sticky: plain `grok` reopens in
|
||||
# the mode last chosen via --minimal/--fullscreen (or /minimal//fullscreen).
|
||||
grok --minimal
|
||||
|
||||
# Back to the standard fullscreen TUI (and make it sticky again)
|
||||
grok --fullscreen
|
||||
|
||||
# Headless mode (for scripts)
|
||||
grok -p "Explain this codebase"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Headless Mode
|
||||
|
||||
Run Grok non-interactively for scripting, CI/CD, and automation:
|
||||
|
||||
```bash
|
||||
grok -p "Your prompt here"
|
||||
```
|
||||
|
||||
Output formats:
|
||||
|
||||
| Format | Flag | Description |
|
||||
|--------|------|-------------|
|
||||
| `plain` | (default) | Human-readable text |
|
||||
| `json` | `--output-format json` | Single JSON object with `text`, `stopReason`, `sessionId`, and `requestId` |
|
||||
| `streaming-json` | `--output-format streaming-json` | NDJSON event stream for real-time processing |
|
||||
|
||||
Example CI/CD usage:
|
||||
|
||||
```bash
|
||||
grok -p "Review changes for bugs" --output-format json --yolo | jq -r '.text'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Project Rules (AGENTS.md)
|
||||
|
||||
Add per-project instructions by creating an `AGENTS.md` file in your repository. Grok reads these files and injects their contents as a project-instructions message at the start of the conversation:
|
||||
|
||||
```
|
||||
~/.grok/AGENTS.md # Global rules (apply to all projects)
|
||||
<repo-root>/AGENTS.md # Repository-level rules
|
||||
<cwd>/AGENTS.md # Directory-level rules (highest priority)
|
||||
```
|
||||
|
||||
Deeper files take precedence. Grok also reads `CLAUDE.md` files for compatibility.
|
||||
|
||||
---
|
||||
|
||||
## Where to Go Next
|
||||
|
||||
| Document | What You Will Learn |
|
||||
|----------|-------------------|
|
||||
| [Authentication](02-authentication.md) | Browser login, API keys, OIDC, external auth, device code flow |
|
||||
| [Keyboard Shortcuts](03-keyboard-shortcuts.md) | Complete reference for all key bindings |
|
||||
| [Slash Commands](04-slash-commands.md) | All available `/` commands |
|
||||
| [Configuration](05-configuration.md) | config.toml, pager.toml, environment variables |
|
||||
|
|
@ -0,0 +1,300 @@
|
|||
# Authentication
|
||||
|
||||
Grok supports several authentication methods, including interactive browser login, enterprise single sign-on (SSO), and headless CI/CD runners.
|
||||
|
||||
---
|
||||
|
||||
## Browser Login (Default)
|
||||
|
||||
On first launch, Grok opens your browser to authenticate with grok.com:
|
||||
|
||||
```bash
|
||||
grok
|
||||
```
|
||||
|
||||
Grok stores credentials in `~/.grok/auth.json` and reuses them across sessions. Grok refreshes access tokens automatically in the background. When a token can't be refreshed, Grok prompts you to sign in again. Credentials without a server-provided expiry fall back to a 30-day lifetime.
|
||||
|
||||
### Re-authenticate
|
||||
|
||||
To switch accounts or resolve an authentication problem, run:
|
||||
|
||||
```bash
|
||||
grok login
|
||||
```
|
||||
|
||||
Running `grok login` starts the sign-in flow again, replacing your cached session. By default, it opens your browser and signs in through SpaceXAI OAuth at `auth.x.ai`. Pass a flag to select a different flow:
|
||||
|
||||
| Flag | Description |
|
||||
|------|-------------|
|
||||
| `--oauth` | Sign in through SpaceXAI OAuth at `auth.x.ai`. This is the default, so the flag is optional. |
|
||||
| `--device-auth` (alias `--device-code`) | Sign in with the device-code flow for headless or remote environments. |
|
||||
|
||||
To sign out, run `grok logout`. It takes no flags and clears your cached credentials.
|
||||
|
||||
---
|
||||
|
||||
## API Key
|
||||
|
||||
For CI/CD, automation, or environments without browser access, use an API key from [console.x.ai](https://console.x.ai):
|
||||
|
||||
```bash
|
||||
export XAI_API_KEY="xai-..."
|
||||
grok
|
||||
```
|
||||
|
||||
Grok uses the API key as a fallback when no session token is active. If you have already signed in interactively, the stored session token takes precedence. To fall back to the API key, run `grok logout` or delete `~/.grok/auth.json`.
|
||||
|
||||
---
|
||||
|
||||
## OIDC (Customer SSO)
|
||||
|
||||
Authenticate developers through your own Identity Provider (IdP) -- such as Okta, Azure AD, or Auth0 -- instead of grok.com.
|
||||
|
||||
### 1. Register a public client in your IdP
|
||||
|
||||
- Grant type: Authorization Code with PKCE (Proof Key for Code Exchange)
|
||||
- Redirect URI: `http://127.0.0.1/callback` -- a loopback address. Grok binds a random port at sign-in time, and most IdPs treat the loopback redirect as port-agnostic per [RFC 8252](https://tools.ietf.org/html/rfc8252).
|
||||
- No client secret. PKCE replaces it.
|
||||
|
||||
### 2. Configure the CLI
|
||||
|
||||
Via config file:
|
||||
|
||||
```toml
|
||||
# ~/.grok/config.toml
|
||||
[grok_com_config.oidc]
|
||||
issuer = "https://acme.okta.com"
|
||||
client_id = "0oa1b2c3d4e5f6g7h8i9"
|
||||
```
|
||||
|
||||
Or via environment variables:
|
||||
|
||||
```bash
|
||||
export GROK_OIDC_ISSUER="https://acme.okta.com"
|
||||
export GROK_OIDC_CLIENT_ID="0oa1b2c3d4e5f6g7h8i9"
|
||||
```
|
||||
|
||||
You can also override the API endpoint to point at your own proxy:
|
||||
|
||||
```bash
|
||||
export GROK_CLI_CHAT_PROXY_BASE_URL="https://grok-proxy.acme.com/v1"
|
||||
```
|
||||
|
||||
### 3. Run `grok`
|
||||
|
||||
The CLI discovers endpoints via `{issuer}/.well-known/openid-configuration`, opens the IdP login page, and stores tokens in `~/.grok/auth.json`. Tokens auto-refresh silently via the stored `refresh_token`.
|
||||
|
||||
### Optional fields
|
||||
|
||||
| Field | Default | Notes |
|
||||
|-------|---------|-------|
|
||||
| `scopes` | `["openid", "profile", "email", "offline_access", "api:access"]` | `offline_access` enables silent token refresh |
|
||||
| `audience` | None | Required by some IdPs (e.g., Auth0) |
|
||||
|
||||
---
|
||||
|
||||
## External Auth Provider
|
||||
|
||||
When browser-based login isn't possible -- for example, on sandboxed VMs, CI runners, or air-gapped networks -- delegate authentication to an external binary or script.
|
||||
|
||||
### How It Works
|
||||
|
||||
```
|
||||
+--------------+ sh -c +------------------------+
|
||||
| Grok |-------------->| your auth binary |
|
||||
| | | |
|
||||
| reads |<-- stdout ----| prints token |
|
||||
| auth.json | | |
|
||||
| | (stderr) | prints status/URLs |--> surfaced to user
|
||||
+--------------+ +------------------------+
|
||||
```
|
||||
|
||||
1. Grok runs your command via `sh -c "<command>"`
|
||||
2. Your binary runs whatever auth flow it needs (SSO, device code, certificate exchange)
|
||||
3. **stderr** carries human-readable output, such as login URLs and status messages. Grok reads stderr and surfaces it to the user; in the TUI, it turns the first `https://` URL into a clickable sign-in link.
|
||||
4. **stdout** is captured by Grok and saved as the access token
|
||||
5. Exit 0 = success; exit non-zero = Grok falls back to interactive login
|
||||
|
||||
### The stdout / stderr Contract
|
||||
|
||||
| Stream | What to print | Who sees it |
|
||||
|--------|---------------|-------------|
|
||||
| **stdout** | The token -- nothing else | Grok (parsed and stored in auth.json) |
|
||||
| **stderr** | Login URLs, status messages, errors | The user (Grok reads stderr and shows the sign-in URL as a clickable link in the TUI) |
|
||||
|
||||
**Do not print anything to stdout except the token.** No progress messages, no debug output. Grok reads stdout, trims surrounding whitespace, and parses the result as a token.
|
||||
|
||||
### stdout Token Format
|
||||
|
||||
**Bare string** -- just the raw token:
|
||||
|
||||
```
|
||||
eyJhbGciOiJSUzI1NiIs...
|
||||
```
|
||||
|
||||
**JSON** -- with optional refresh token, expiry, and issuer:
|
||||
|
||||
```json
|
||||
{"access_token": "eyJhbGciOi...", "refresh_token": "ref-tok", "expires_in": 3600, "issuer": "https://idp.example.com"}
|
||||
```
|
||||
|
||||
Use JSON if your tokens expire and you want Grok to automatically re-run the binary before expiry.
|
||||
|
||||
JSON fields:
|
||||
|
||||
| Field | Required | Meaning |
|
||||
|-------|----------|---------|
|
||||
| `access_token` | yes | Bearer token Grok sends to the xAI API |
|
||||
| `refresh_token` | no | Stored for reference. Grok refreshes by re-running your binary, not with an OAuth refresh grant |
|
||||
| `expires_in` | no | Token lifetime in seconds; enables proactive refresh before expiry |
|
||||
| `issuer` | no | Identifies the token's issuer |
|
||||
|
||||
### Configuration
|
||||
|
||||
Via config file:
|
||||
|
||||
```toml
|
||||
# ~/.grok/config.toml
|
||||
[auth]
|
||||
auth_provider_command = "/usr/local/bin/my-auth-provider"
|
||||
auth_provider_label = "Acme Corp" # optional -- customizes the TUI login button
|
||||
auth_token_ttl = 3600 # optional -- token lifetime in seconds
|
||||
```
|
||||
|
||||
Or via environment variables:
|
||||
|
||||
```bash
|
||||
export GROK_AUTH_PROVIDER_COMMAND="/usr/local/bin/my-auth-provider"
|
||||
export GROK_AUTH_PROVIDER_LABEL="Acme Corp"
|
||||
export GROK_AUTH_TOKEN_TTL=3600
|
||||
```
|
||||
|
||||
### Token Refresh
|
||||
|
||||
When Grok needs to refresh an expired token, it re-runs your binary with `GROK_AUTH_EXPIRED=1` set in the environment. Each run fully replaces the stored credential, so emit the same JSON fields (such as `issuer`) on every invocation, including refreshes. Your binary can use this to take a faster silent-refresh path:
|
||||
|
||||
```bash
|
||||
#!/bin/sh
|
||||
if [ "$GROK_AUTH_EXPIRED" = "1" ]; then
|
||||
echo "Refreshing token..." >&2
|
||||
TOKEN=$(my-company-auth --refresh --silent)
|
||||
else
|
||||
echo "Authenticating via Acme Corp SSO..." >&2
|
||||
TOKEN=$(my-company-auth --login --interactive)
|
||||
fi
|
||||
|
||||
if [ -z "$TOKEN" ]; then
|
||||
echo "Authentication failed" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "{\"access_token\": \"$TOKEN\", \"expires_in\": 3600}"
|
||||
```
|
||||
|
||||
### Environment Variables
|
||||
|
||||
| Variable | Description |
|
||||
|----------|-------------|
|
||||
| `GROK_AUTH_PROVIDER_COMMAND` | Path to your auth binary |
|
||||
| `GROK_AUTH_PROVIDER_LABEL` | Display name on the TUI login screen (e.g., "Acme Corp") |
|
||||
| `GROK_AUTH_TOKEN_TTL` | Token lifetime in seconds (for bare-string tokens without `expires_in`) |
|
||||
| `GROK_AUTH_EXPIRED` | Set to `1` by Grok when re-running the binary for token refresh |
|
||||
| `GROK_AUTH_EARLY_INVALIDATION_SECS` | Seconds before expiry to proactively refresh (default: 300) |
|
||||
|
||||
---
|
||||
|
||||
## Device Code Flow
|
||||
|
||||
For headless environments (SSH sessions, Docker containers, remote VMs) where no browser is available locally:
|
||||
|
||||
```bash
|
||||
grok login --device-auth # or: grok login --device-code
|
||||
```
|
||||
|
||||
This prints a URL and code to the terminal. Open the URL on any device, enter the code, and complete authentication. Grok polls until the login is confirmed.
|
||||
|
||||
You can also implement the device-code flow through an [External Auth Provider](#external-auth-provider) for full control.
|
||||
|
||||
---
|
||||
|
||||
## Automatic Credential Refresh
|
||||
|
||||
Grok automatically refreshes expired credentials:
|
||||
|
||||
- **Before expiry:** If your auth provider returned `expires_in` (JSON output) or you set `auth_token_ttl`, Grok re-runs the auth binary ~5 minutes before expiry.
|
||||
- **On auth error:** If the server returns 401 Unauthorized, Grok refreshes the credentials and retries the request.
|
||||
- **OIDC:** If a `refresh_token` is available, Grok silently refreshes via your IdP without re-opening the browser.
|
||||
|
||||
Tune the refresh buffer:
|
||||
|
||||
```bash
|
||||
# Refresh 5 minutes before expiry (default)
|
||||
export GROK_AUTH_EARLY_INVALIDATION_SECS=300
|
||||
|
||||
# Disable the proactive buffer: refresh at expiry or on a 401 (set to 0)
|
||||
export GROK_AUTH_EARLY_INVALIDATION_SECS=0
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Hot Reload
|
||||
|
||||
Grok picks up changes to `~/.grok/auth.json` automatically. If you update credentials externally (for example, with a script that writes new tokens), Grok uses the new credentials on the next API call without a restart.
|
||||
|
||||
---
|
||||
|
||||
## Auth Precedence
|
||||
|
||||
Grok resolves credentials for each request in this order, highest to lowest:
|
||||
|
||||
1. **Per-model `api_key` or `env_key`** -- set under `[model.<name>]` in `config.toml`. Wins whenever present.
|
||||
2. **Active session token** -- obtained through browser, OIDC/OAuth2, or external-provider login and stored in `~/.grok/auth.json`.
|
||||
3. **`XAI_API_KEY`** -- fallback when no session token is active.
|
||||
|
||||
When more than one login flow is configured, Grok populates the session token from the first available source, highest to lowest:
|
||||
|
||||
1. **External auth provider** (`auth_provider_command`)
|
||||
2. **Enterprise OIDC** -- when OIDC is configured, through `[grok_com_config.oidc]` in `config.toml` or the `GROK_OIDC_ISSUER` and `GROK_OIDC_CLIENT_ID` environment variables
|
||||
3. **SpaceXAI OAuth2 browser login** -- the default
|
||||
|
||||
During a session, the active method handles all mid-session refreshes.
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Debug logging
|
||||
|
||||
Set `RUST_LOG` to control the verbosity of the file log and headless stderr output. (The TUI's on-screen tracing pane uses a fixed filter and ignores `RUST_LOG`.) In the TUI, file logging defaults to `DEBUG`; in headless mode (`-p`), `RUST_LOG` defaults to `off` so only the answer is printed — set `RUST_LOG=error` (or broader) to see logs on stderr.
|
||||
|
||||
In the TUI, set `GROK_LOG_FILE` to an absolute path to write logs to that file:
|
||||
|
||||
```bash
|
||||
GROK_LOG_FILE=/tmp/grok.log RUST_LOG=debug grok
|
||||
tail -f /tmp/grok.log
|
||||
```
|
||||
|
||||
`GROK_LOG_FILE` is treated as a literal file path. A relative value such as `1` writes a file named `1` in the current directory.
|
||||
|
||||
In headless mode, logs go to stderr. Redirect them to a file:
|
||||
|
||||
```bash
|
||||
RUST_LOG=debug grok -p "hello" 2> /tmp/grok.log
|
||||
```
|
||||
|
||||
### Common log messages
|
||||
|
||||
| Log message | What it means |
|
||||
|-------------|---------------|
|
||||
| `auth: running external auth provider` | Grok is running your binary |
|
||||
| `auth: external auth provider returned fresh token` | Grok parsed and stored the token |
|
||||
| `auth: external auth provider failed` | Binary exited non-zero or stdout was empty |
|
||||
| `auth: external auth provider timed out (likely needs interactive auth), killing` | Binary did not exit before the timeout and was killed |
|
||||
| `auth: failed to start external auth provider` | Command could not be spawned (binary not found) |
|
||||
|
||||
### Common fixes
|
||||
|
||||
- **"Authentication failed"** -- Run `grok logout` to clear cached credentials, then `grok login` to sign in again.
|
||||
- **Token expires too quickly** -- Set `auth_token_ttl` or return `expires_in` in your auth provider's JSON output.
|
||||
- **OIDC redirect fails** -- Ensure your IdP allows loopback redirect URIs (`http://127.0.0.1/callback`).
|
||||
- **External auth provider not found** -- Check that the `auth_provider_command` path is correct and the binary is executable.
|
||||
|
|
@ -0,0 +1,336 @@
|
|||
# Keyboard Shortcuts
|
||||
|
||||
Reference for key bindings in the Grok Build TUI. Bindings are built in and cannot currently be remapped.
|
||||
|
||||
---
|
||||
|
||||
## Input Modes
|
||||
|
||||
Grok has two input modes that control how you navigate the scrollback:
|
||||
|
||||
- **Simple mode** (default): Arrow keys for navigation, `Shift+Arrow` for turn navigation, `Space` to focus the prompt, and any letter key auto-focuses the prompt.
|
||||
- **Vim mode** (opt-in): `j`/`k` for navigation, `H`/`L` for turn navigation, `J`/`K` for response navigation, `h`/`l` for fold, `e`/`E` for expand/collapse, and `i`/`Tab`/`Space` to focus the prompt.
|
||||
|
||||
Simple mode is active by default. To switch to Vim mode, set `vim_mode = true` under `[ui]` in `~/.grok/config.toml`, or toggle it at runtime with `/vim-mode`. See [Configuration](05-configuration.md) for details.
|
||||
|
||||
The tables below document bindings for both modes. The "Key" column shows the Vim-mode binding, and the "Alt Key" column shows the equivalent in simple mode (arrow keys, etc.).
|
||||
|
||||
> **Vim-mode required**: Single-letter and `Shift+letter` bindings in the
|
||||
> **Scrollback** context (`j/k`, `h/l`, `g/G`, `L/H`, `y/Y`, `o/O`, `r`,
|
||||
> `x`, `e/E`, and the `i` insert-mode alt) require `[ui].vim_mode = true`
|
||||
> in `~/.grok/config.toml` (or `/vim-mode` to toggle). Arrow keys, `Tab`,
|
||||
> `Esc`, `Space`, `PageUp/Down`, and every `Ctrl+letter` shortcut work in
|
||||
> both modes.
|
||||
|
||||
---
|
||||
|
||||
## Navigation (Scrollback Focused)
|
||||
|
||||
Move through conversation entries in the scrollback pane.
|
||||
|
||||
| Key | Alt Key | Action |
|
||||
|-----|---------|--------|
|
||||
| `j` | `Down` | Select next entry |
|
||||
| `k` | `Up` | Select previous entry |
|
||||
| `⇧L` | `Shift+Right` | Jump to next turn (user prompt) |
|
||||
| `⇧H` | `Shift+Left` | Jump to previous turn (user prompt) |
|
||||
| `⇧J` | | Jump to next assistant response |
|
||||
| `⇧K` | | Jump to previous assistant response |
|
||||
| `g` | | Go to top of scrollback |
|
||||
| `⇧G` | | Go to bottom of scrollback |
|
||||
| `Ctrl+K` | | Scroll up one line (without changing selection) |
|
||||
| `Ctrl+J` | | Scroll down one line (without changing selection) |
|
||||
| `PageUp` | | Scroll up one page (selection moves to the top of the viewport) |
|
||||
| `PageDown` | | Scroll down one page (selection moves to the bottom of the viewport) |
|
||||
| `Ctrl+U` | | Scroll up half page |
|
||||
| `Ctrl+D` (`Shift+D` in VSCode) | | Scroll down half page |
|
||||
|
||||
`PageUp` and `PageDown` also scroll the conversation while the ordinary prompt
|
||||
is focused, without moving focus or changing the draft. An active prompt
|
||||
history, `@` file search, slash menu, or completion dropdown keeps the keys for
|
||||
its own navigation.
|
||||
|
||||
---
|
||||
|
||||
## View (Scrollback Focused)
|
||||
|
||||
Control how entries are displayed in the scrollback.
|
||||
|
||||
| Key | Alt Key | Action |
|
||||
|-----|---------|--------|
|
||||
| `h` | `Left` | Collapse selected entry |
|
||||
| `l` | `Right` | Expand selected entry |
|
||||
| `e` | | Toggle fold on selected entry |
|
||||
| `⇧E` | | Expand all / collapse all entries |
|
||||
| `Ctrl+E` | | Expand/collapse all thinking blocks |
|
||||
| `r` | | Toggle raw markdown on selected entry |
|
||||
|
||||
Setting `respect_manual_folds = true` under `[scrollback.scroll]` in
|
||||
`pager.toml` (opt-in, off by default — see
|
||||
[Configuration](05-configuration.md)) makes a hand-folded block pinned:
|
||||
streaming updates and finish events (for example a thinking block ending)
|
||||
leave it alone instead of resetting it, and expanding a block while
|
||||
auto-scroll is following the tail stops following so you can read; resume
|
||||
with `⇧G`, `j` at the last entry, scrolling past the bottom, or sending a new
|
||||
prompt. `⇧E` clears all pins, and `Ctrl+E` clears pins on thinking blocks.
|
||||
|
||||
### Block Content
|
||||
|
||||
| Key | Action |
|
||||
|-----|--------|
|
||||
| `y` | Copy block content to clipboard |
|
||||
| `⇧Y` | Copy block metadata (e.g., the shell command) to clipboard |
|
||||
| `Enter` | Open block content in fullscreen viewer |
|
||||
| `Ctrl+F` | Open block content in fullscreen viewer (alt binding) |
|
||||
|
||||
---
|
||||
|
||||
## Focus
|
||||
|
||||
Switch between the prompt input and scrollback pane.
|
||||
|
||||
| Key | Alt Key | Context | Action |
|
||||
|-----|---------|---------|--------|
|
||||
| `Tab` | `Space` (and `i` in vim mode) | Scrollback focused | Focus the prompt input |
|
||||
| `Tab` | | Prompt focused | Focus the scrollback (both simple and vim scrollback modes) |
|
||||
| `Enter` | | Prompt focused | Send the current prompt |
|
||||
|
||||
**Esc is not a focus key.** It follows clear / rewind semantics below (and swallows mid-turn), independent of `[ui].simple_mode` (prompt editor) and `[ui].vim_mode` (scrollback nav). Overlays, modals, slash/file dropdowns, voice, search, and selection still steal Esc first.
|
||||
|
||||
## Escape
|
||||
|
||||
| State | Gesture | Effect |
|
||||
|--------|---------|--------|
|
||||
| Turn running | `Esc` | Swallowed no-op (does **not** cancel). Use `Ctrl+C` (or palette / other cancel entry points). |
|
||||
| Turn cancelling | `Esc` | Re-sends cancel (retry if the first ack was lost). `Ctrl+C` in this state escalates toward quit. |
|
||||
| Idle + non-empty prompt (text or image chips), **prompt focused** | **2× `Esc` within 800ms** | Clear the prompt; non-empty text is saved to prompt history. First press shows “press again to clear”. |
|
||||
| Idle + empty prompt + conversation messages, **prompt or scrollback focused** | **2× `Esc` within 800ms** | Open the rewind picker (same as `/rewind`). First press is silent (no toast). |
|
||||
| Idle + empty + no messages, **or scrollback focused with a draft / moded (`!` `#` feedback) composer / pending needs-input overlay / open history search** | `Esc` | Swallowed no-op (does not focus scrollback). Clear is prompt-pane only; rewind requires an empty Normal-mode composer, no pending overlay, and no open history search — reading the scrollback never mutates your draft, your composer mode, a question awaiting an answer, or an in-progress search. |
|
||||
|
||||
**Steal-Esc (runs before mid-turn swallow / clear / rewind):** overlays, modals, slash/file/completion dropdowns, history search, scrollback search, text selection, link highlight, voice, and **Bash / Remember / Feedback mode exit** when the prompt is empty (Esc leaves `!` / `#` / feedback mode and returns to the normal prompt — even while a turn is running).
|
||||
|
||||
**Ctrl+C vs Esc:** with a non-empty draft while a turn is running, Ctrl+C clears the draft and keeps the turn; a second Ctrl+C on an empty prompt cancels. Esc does not cancel a running turn (only retries while already cancelling). Idle non-empty Ctrl+C clears in one press; Esc requires two presses within 800ms.
|
||||
|
||||
---
|
||||
|
||||
## Agent-Level
|
||||
|
||||
Actions that affect the agent session, available from the agent screen.
|
||||
|
||||
| Key | Context | Action |
|
||||
|-----|---------|--------|
|
||||
| `Ctrl+P` | Agent screen | Open the command palette |
|
||||
| `?` (Shift+/) | Agent screen | Open the command palette (alt binding) |
|
||||
| `Ctrl+M` | Agent screen | Open the model picker / switch model |
|
||||
| `Ctrl+M` | Prompt focused | Toggle multiline input mode |
|
||||
| `Ctrl+C` | Agent screen | Cancel the current turn (or clear non-empty draft first; see Escape table) |
|
||||
| `Ctrl+O` | Agent screen | Toggle always-approve (YOLO) mode |
|
||||
| `Ctrl+S` | Agent screen | Open the session picker (resume a previous session) |
|
||||
| `Ctrl+;` (alt: `Ctrl+'`) | Agent screen | Toggle the prompt queue pane (when non-empty). **Local macOS** VS Code family only: primary **`Ctrl+4`** (`;` / `'` still alts). SSH and non-Mac keep **`Ctrl+;`** / **`Ctrl+'`**. |
|
||||
| `Shift+Tab` | Prompt focused | Cycle mode (Normal → Plan → Always-approve) |
|
||||
| `Ctrl+G` | Agent screen | Send the current task to the background |
|
||||
| `Ctrl+T` | Agent screen | Toggle the todos pane |
|
||||
| `Ctrl+B` | Agent screen | Toggle the tasks pane |
|
||||
| `Ctrl+L` | Agent screen | Open the extensions modal (**non–VS Code family only**; on VS Code / Cursor / Windsurf / Zed, `Ctrl+L` is mid-turn **interject** and extensions open via `/plugins` / `/hooks`) |
|
||||
| `↑` | Prompt focused (empty prompt, normal input mode) | Open the history panel with your last prompt filled in; `↑`/`↓` step through entries (each lands in the input), `↓` at the newest closes the panel, and typing edits the recalled prompt in place. Recalled `!` shell commands re-enter shell mode. `↓` never opens history. |
|
||||
| `!` | Prompt focused | Enter shell mode (type `!` on an empty prompt) |
|
||||
| `Ctrl+.` (alt: `Ctrl+X`) | Agent screen | Open the keyboard shortcuts help |
|
||||
| `F2` (alt: `Ctrl+,` / `Cmd+,`) | Agent screen | Open the settings modal |
|
||||
|
||||
**Note:** `Ctrl+M` is context-dependent. When the prompt is focused, it toggles multiline input mode. Otherwise, it opens the model picker.
|
||||
|
||||
**Note:** `Ctrl+'` is a Windows alt for `Ctrl+;` — some Windows consoles drop the `Ctrl` modifier on punctuation keys.
|
||||
|
||||
**Note:** `Ctrl+.` needs the Kitty keyboard protocol (or tmux `extended-keys on` so that protocol can pass through). On VS Code / Cursor / Windsurf / Zed integrated terminals, VTE, Apple Terminal, Windows Terminal, JetBrains, tmux with `extended-keys off`, screen, and similar no-KKP setups, Grok advertises **`Ctrl+X`** as the primary shortcuts-cheatsheet key instead. **`Ctrl+X` always works** as a classic control character even when `Ctrl+.` does not. Run `/terminal-setup` if modified keys misbehave in tmux.
|
||||
|
||||
---
|
||||
|
||||
## Image Paste & Drag-and-Drop
|
||||
|
||||
| Action | macOS | Linux | Windows |
|
||||
|---|---|---|---|
|
||||
| Drag image from file manager into the prompt | Finder ✓ | Files / Dolphin ✓ | Explorer ✓ |
|
||||
| Copy a file in the file manager, then paste | `Cmd+V` | `Ctrl+V` | `Ctrl+V` |
|
||||
| Screenshot or "Copy Image" in clipboard, then paste | `Cmd+V` | `Ctrl+V` | **`Alt+V`** |
|
||||
|
||||
Non-image files insert their absolute path as text instead of a chip.
|
||||
|
||||
> **`Alt+V` on Windows** is grok-specific. Windows Terminal's default `Ctrl+V` only pastes plain text and silently drops image clipboards; `Alt+V` bypasses the interceptor. To use `Ctrl+V` for images too, add `{ "command": null, "keys": "ctrl+v" }` to `actions` in your Windows Terminal `settings.json`.
|
||||
|
||||
### Linux PRIMARY and CLIPBOARD
|
||||
|
||||
Linux X11 has two independent text selections:
|
||||
|
||||
- `Ctrl+V` reads **CLIPBOARD**, the explicit copy/cut selection. It never falls back to PRIMARY. To put text there with `xclip`, use `printf %s "text" | xclip -selection clipboard`.
|
||||
- An unmodified middle click in Grok reads **PRIMARY**, the current mouse selection, only when `DISPLAY` is non-empty. Pure X11 can use its native reader fallback; XWayland requires `xclip` or `xsel` on `PATH` so Grok reads the X11 selection rather than Wayland PRIMARY. The press is handled once; the release does not paste again.
|
||||
- `Shift+Insert` is the terminal-native way to paste selected text. Many terminals also use `Shift+middle click` to bypass application mouse reporting.
|
||||
|
||||
Over SSH, the remote Grok process usually cannot access the terminal's local X11 selection. Use terminal-native `Shift+Insert` or `Shift+middle click` so the local terminal sends the selected text through the PTY.
|
||||
|
||||
---
|
||||
|
||||
## During an active turn (agent running)
|
||||
|
||||
While the agent is generating:
|
||||
|
||||
- **Plain `Enter`** (with text in the composer) **queues** a follow-up for later. Queued follow-ups run after the current turn ends — and they deliberately **hold** while the agent is blocked waiting on background tasks or a subagent (a hint explains the hold and how to send one now).
|
||||
- **`Enter` again on the emptied composer** (double-Enter) sends the **top** queued follow-up now.
|
||||
- The **send now** chord is **cancel-and-send**: it stops the current turn (background tasks, subagents, and the rest of the queue keep running) and sends your message as the next turn, so it always appears at the bottom of the transcript:
|
||||
- **Non-empty composer** → cancel and send that text now.
|
||||
- **Empty composer** + a queued follow-up → send the **top** queued follow-up now (no need to focus the queue pane). On the queue pane, the same chord (or the **[Send now]** button) sends the **selected** row.
|
||||
- **Idle**, or **empty composer with nothing queued** → no-op for that key.
|
||||
- While the agent is **blocked waiting** (on task output or a subagent), plain `Enter` with text also delivers immediately — the shell cancels the blocked turn and runs your message next.
|
||||
|
||||
| Terminal | Primary | Alternates | Action |
|
||||
|----------|---------|------------|--------|
|
||||
| Default | `Ctrl+Enter` | `Ctrl+I` | Send now (cancels the current turn, runs your message next) |
|
||||
| Apple Terminal | `Ctrl+O` | `Ctrl+Enter`, `Ctrl+I` | Send now |
|
||||
| VS Code family (VS Code, Cursor, Windsurf, Zed) | **`Ctrl+L`** | *(none)* | Send now (`Ctrl+I` not used — Tab / host chat; plugins via `/plugins`) |
|
||||
|
||||
In `/multiline` mode, `Shift+Enter` (or `Alt+Enter`) sends while plain `Enter` inserts a newline — except on an **empty** composer mid-turn with a queued follow-up, where plain `Enter` still **send now**s the top row (same as normal mode). (`Ctrl+Enter` is send-now mid-turn when bound on non–VS Code family; it does not submit a new idle turn.)
|
||||
|
||||
Send-now is intentionally interruptive — it reads as "stop what you're doing and take this". To hand the agent a note **without** stopping it, queue with plain `Enter`; the agent picks it up at the next turn boundary.
|
||||
|
||||
> **WezTerm**: These modified Enter keys need `enable_kitty_keyboard = true` in your WezTerm config. Full steps and a one-line workaround are in the [terminal support guide](21-terminal-support.md#problem-ctrlenter-doesnt-interject-in-wezterm).
|
||||
|
||||
> **Windows (non–VS Code family)**: Some consoles drop the `Ctrl` modifier on `Ctrl+Enter` (it can collapse to bare `Enter` or `Ctrl+J`). Use `Ctrl+I` as the alt — letter-key Ctrl chords are stable everywhere. On VS Code family, use **`Ctrl+L`**.
|
||||
|
||||
> **VS Code family `Ctrl+L`**: Grok uses it for interject and leaves the extensions shortcut unbound (open plugins with `/plugins` or the command palette). If your terminal profile still maps **Clear** (or another command) to `Ctrl+L`, that host binding can steal the chord — rebind or remove it so the PTY receives form feed (`\x0c`).
|
||||
|
||||
---
|
||||
|
||||
## Global
|
||||
|
||||
Actions available from any screen.
|
||||
|
||||
| Key | Alt Key | Action | Confirmation |
|
||||
|-----|---------|--------|-------------|
|
||||
| `Ctrl+N` | | Create a new session (optionally in a git worktree) | Yes (double-press within 1000ms) |
|
||||
| `Ctrl+Q` | `Ctrl+D` | Quit the application | Yes (double-press within 1000ms) |
|
||||
|
||||
**VS Code family terminal** (VS Code, Cursor, Windsurf, Zed integrated terminals): `Ctrl+Q` is captured by the host, so Grok makes **`Ctrl+D` the sole quit key** (`Ctrl+Q` is not bound). Half-page-down is rebound to bare **`Shift+D`**. Mid-turn interject uses **`Ctrl+L`** (no alternates) because `Ctrl+Enter` / `Ctrl+I` do not reliably reach the PTY; extensions are opened via `/plugins` instead of `Ctrl+L`.
|
||||
|
||||
> **Returning to the welcome screen has no key binding** — use the `/home` slash command (alias `/welcome`) from inside a session. See [Slash Commands](04-slash-commands.md).
|
||||
|
||||
### Destructive Action Confirmation
|
||||
|
||||
Actions marked with "Yes" in the confirmation column require a double-press within 1000ms. Press the key once to see a confirmation prompt, then press again to confirm. This prevents accidental session loss.
|
||||
|
||||
---
|
||||
|
||||
## Welcome Screen
|
||||
|
||||
Bindings that only fire on the welcome screen (before any agent session is open).
|
||||
|
||||
| Key | Action |
|
||||
|-----|--------|
|
||||
| `Ctrl+S` | Resume session (open the session picker) |
|
||||
| `Ctrl+W` | Open the New Worktree dialog (only inside a git repository) |
|
||||
| `Ctrl+I` | Import Claude settings (when available) |
|
||||
| `Ctrl+Shift+I` | Dismiss the Claude import row (when available) |
|
||||
|
||||
`Ctrl+W`, `Ctrl+I`, and `Ctrl+Shift+I` are only active on the welcome screen. `Ctrl+S` opens the session picker on both the welcome screen and inside an agent session (where it opens as a modal overlay, same as the `/resume` command). `Ctrl+Q` is the same global Quit binding documented above, not a welcome-specific handler.
|
||||
|
||||
---
|
||||
|
||||
## Command Palette
|
||||
|
||||
Press `Ctrl+P` or `?` to open the command palette -- a searchable list of actions. The palette shows:
|
||||
|
||||
- All keyboard shortcuts with their current bindings
|
||||
- All slash commands
|
||||
- Available skills
|
||||
|
||||
Type to filter, then press `Enter` to execute the selected action.
|
||||
|
||||
---
|
||||
|
||||
## Shortcuts Bar
|
||||
|
||||
The bottom of the TUI displays a contextual shortcuts bar showing the most relevant key bindings for the current state. The hints change based on:
|
||||
|
||||
- Which pane is focused (scrollback vs. prompt)
|
||||
- Whether the agent is currently running
|
||||
- What type of entry is selected
|
||||
|
||||
---
|
||||
|
||||
## Mouse Support
|
||||
|
||||
The TUI supports mouse interaction:
|
||||
|
||||
- **Click** on a scrollback entry to select it
|
||||
- **Scroll wheel** to scroll through the scrollback
|
||||
- **Click** on the prompt area to focus it
|
||||
- **Hover** over the prompt to see a highlight (configurable via `pager.toml`)
|
||||
- **Middle click** on Linux X11/XWayland to paste the PRIMARY selection
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference Card
|
||||
|
||||
### When scrollback is focused (Simple mode — default)
|
||||
|
||||
```
|
||||
Navigation: Up/Down (prev/next entry) Shift+Left/Right (prev/next turn)
|
||||
Scrolling: Ctrl+J/K (line) PgUp/PgDn (page) Ctrl+U/D (half page)
|
||||
Focus prompt: Space or any letter key (auto-focuses and types)
|
||||
```
|
||||
|
||||
### When scrollback is focused (Vim mode)
|
||||
|
||||
```
|
||||
Navigation: j/k (up/down) H/L (prev/next turn) K/J (prev/next response) g/G (top/bottom)
|
||||
Scrolling: Ctrl+J/K (line) Ctrl+U/D (half page; D=Shift+D in VSCode) PgUp/PgDn (page)
|
||||
Folding: h/l (collapse/expand) e (toggle) E (all)
|
||||
Content: y (copy) Y (copy cmd) Enter (fullscreen)
|
||||
View: r (raw markdown) Ctrl+E (thinking)
|
||||
Focus prompt: i, Tab, or Space
|
||||
```
|
||||
|
||||
### When prompt is focused
|
||||
|
||||
```
|
||||
Send: Enter
|
||||
Newline: Shift+Enter or Alt+Enter
|
||||
Multiline: Ctrl+M (toggle)
|
||||
Paste: Ctrl+V (text, files, screenshots on macOS/Linux)
|
||||
Selected text: Middle click or Shift+Insert (Linux X11/XWayland PRIMARY)
|
||||
Paste image: Alt+V (Windows only — for screenshots / "Copy Image")
|
||||
Select all: Cmd+A (macOS, Ghostty only — see note below)
|
||||
Leave: Tab (back to scrollback)
|
||||
Cancel (running): Ctrl+C (empty prompt; non-empty draft clears first)
|
||||
Clear (idle): Esc Esc within 800ms (non-empty prompt)
|
||||
Rewind (idle): Esc Esc within 800ms (empty prompt + messages)
|
||||
```
|
||||
|
||||
> **Cmd+A is gated to Ghostty.** Grok's in-app `Cmd+A` handler is only
|
||||
> wired up when the detected terminal is Ghostty. Other terminals
|
||||
> either swallow `Cmd+A` at the terminal layer (Apple Terminal, default
|
||||
> iTerm2) or apply their own in-terminal "Select All" behaviour (Kitty,
|
||||
> WezTerm). On a non-Ghostty terminal, the binding does nothing and the
|
||||
> key falls through to the terminal's native behaviour.
|
||||
>
|
||||
> On Ghostty, add the one-line unbind to `~/.config/ghostty/config` so
|
||||
> the keystroke reaches the running TUI:
|
||||
>
|
||||
> ```ini
|
||||
> keybind = cmd+a=unbind
|
||||
> ```
|
||||
>
|
||||
> After Ghostty reloads (it watches the config file), `Cmd+A` in the
|
||||
> prompt selects every character in the prompt buffer, including pasted
|
||||
> image chips. Image chips are always path-free (`[Image #N]`); the
|
||||
> filepath (when known) appears only in the image preview overlay on
|
||||
> hover or when the cursor is on/right after the chip.
|
||||
|
||||
### Always available
|
||||
|
||||
```
|
||||
Command palette: Ctrl+P or ?
|
||||
Model picker: Ctrl+M (from scrollback)
|
||||
Cancel: Ctrl+C (see Escape table)
|
||||
Always-approve: Ctrl+O (toggle YOLO)
|
||||
New session: Ctrl+N (press again, then choose normal/worktree)
|
||||
Quit: Ctrl+Q (or Ctrl+D in VSCode)
|
||||
```
|
||||
|
|
@ -0,0 +1,570 @@
|
|||
# Slash Commands
|
||||
|
||||
Type `/` in the prompt to access commands. Each command runs an action immediately and autocompletes as you type.
|
||||
|
||||
Slash commands come from two sources:
|
||||
|
||||
- **Shell builtins** -- handled by the agent backend (xai-grok-shell)
|
||||
- **Pager builtins** -- handled by the TUI frontend (xai-grok-pager)
|
||||
|
||||
Both sets are available in the autocomplete menu. Skills installed via SKILL.md files also appear as slash commands.
|
||||
|
||||
---
|
||||
|
||||
## Session Management
|
||||
|
||||
### `/new`
|
||||
|
||||
Start a new session, clearing the current conversation.
|
||||
|
||||
```
|
||||
/new
|
||||
```
|
||||
|
||||
Aliases: `/clear`
|
||||
|
||||
### `/resume`
|
||||
|
||||
Open the session picker to load a previous session from disk.
|
||||
|
||||
```
|
||||
/resume
|
||||
```
|
||||
|
||||
### `/compact [context]`
|
||||
|
||||
Compress conversation history to save context window space. Optionally specify what to preserve.
|
||||
|
||||
```
|
||||
/compact
|
||||
/compact keep the auth implementation details
|
||||
```
|
||||
|
||||
When the context window fills up, Grok auto-compacts at 85% usage (configurable via `[session] auto_compact_threshold_percent` in config.toml).
|
||||
|
||||
### `/context`
|
||||
|
||||
Show context window usage and session stats: a categorical breakdown (system prompt, messages, reasoning/overhead, free), plus informational rows for tool definitions, the skills listing, and MCP server announcements with their estimated token cost.
|
||||
|
||||
```
|
||||
/context
|
||||
```
|
||||
|
||||
### `/session-info`
|
||||
|
||||
Show session details including model, turn count, and context usage.
|
||||
|
||||
```
|
||||
/session-info
|
||||
```
|
||||
|
||||
|
||||
### `/fork`
|
||||
|
||||
Branch the current session into a new agent, preserving history up to this point.
|
||||
|
||||
```
|
||||
/fork
|
||||
```
|
||||
|
||||
### `/rewind`
|
||||
|
||||
Rewind the conversation to an earlier turn, discarding everything after it.
|
||||
|
||||
```
|
||||
/rewind
|
||||
```
|
||||
|
||||
### `/copy`
|
||||
|
||||
Copy the most recent response to the clipboard. Pass a number to copy the Nth-latest response.
|
||||
|
||||
```
|
||||
/copy
|
||||
/copy 2
|
||||
```
|
||||
|
||||
### `/export`
|
||||
|
||||
Export the current conversation to a file or the clipboard.
|
||||
|
||||
```
|
||||
/export
|
||||
```
|
||||
|
||||
### `/quit`
|
||||
|
||||
Quit the application.
|
||||
|
||||
```
|
||||
/quit
|
||||
```
|
||||
|
||||
Aliases: `/exit`
|
||||
|
||||
### `/home`
|
||||
|
||||
Exit the current session and return to the welcome screen.
|
||||
|
||||
```
|
||||
/home
|
||||
```
|
||||
|
||||
Aliases: `/welcome`
|
||||
|
||||
### `/rename`
|
||||
|
||||
Rename the current session.
|
||||
|
||||
```
|
||||
/rename new session title
|
||||
```
|
||||
|
||||
Aliases: `/title`
|
||||
|
||||
---
|
||||
|
||||
## Model and Mode
|
||||
|
||||
### `/model <name>`
|
||||
|
||||
Switch to a different model. Accepts model IDs or display names (case-insensitive). For reasoning models you can also pass an effort level as a second argument:
|
||||
|
||||
```
|
||||
/model grok-build
|
||||
/model Grok Build
|
||||
/model Reasoning X high
|
||||
```
|
||||
|
||||
Aliases: `/m`
|
||||
|
||||
### `/effort <level>`
|
||||
|
||||
Set reasoning effort on the **current** model without re-selecting it. Levels: `low`, `medium`, `high`, `xhigh`. Only works when the active model supports reasoning effort.
|
||||
|
||||
```
|
||||
/effort high
|
||||
/effort low
|
||||
```
|
||||
|
||||
### `/always-approve` and `/auto`
|
||||
|
||||
True **toggles** for the permission mode — both stay in the completion menu, and
|
||||
running the active mode again turns it off:
|
||||
|
||||
| Command | When off | When already on |
|
||||
|---|---|---|
|
||||
| `/always-approve` | Skip all permission prompts | Back to ask |
|
||||
| `/auto` | Classifier approves safe tools (dangerous ones may still prompt) | Back to ask |
|
||||
|
||||
Running the other command while one mode is on **switches** modes (for example,
|
||||
`/auto` while always-approve is on switches to auto).
|
||||
|
||||
`/auto` is only offered when the auto permission-mode feature is enabled. You
|
||||
can also change mode with `Shift+Tab` (cycle), `Ctrl+O`, or `/settings`.
|
||||
|
||||
```
|
||||
/always-approve
|
||||
/auto
|
||||
```
|
||||
|
||||
### `/multiline`
|
||||
|
||||
Toggle multiline input mode. When enabled, `Enter` inserts a newline and `Shift+Enter` (or `Alt+Enter`) sends the message. Mid-turn, bare `Enter` on an empty composer still force-sends the top queued follow-up (send now).
|
||||
|
||||
```
|
||||
/multiline
|
||||
```
|
||||
|
||||
Aliases: `/ml`
|
||||
|
||||
### `/history`
|
||||
|
||||
Open the prompt-history search: fuzzy-search this session's prompts, newest first — type to filter, press `Enter`/`Tab` to drop a match back into the prompt.
|
||||
|
||||
For quick recall, press `↑` on an empty prompt instead: the panel opens with your most recent prompt already filled into the input, `↑`/`↓` step through entries (each one lands in the input), `↓` at the newest entry closes the panel, and typing edits the recalled prompt in place.
|
||||
|
||||
```
|
||||
/history
|
||||
```
|
||||
|
||||
### `/compact-mode`
|
||||
|
||||
Toggle compact display mode. Reduces padding and visual spacing for denser output.
|
||||
|
||||
```
|
||||
/compact-mode
|
||||
```
|
||||
|
||||
### `/vim-mode`
|
||||
|
||||
Toggle vim-style scrollback keybindings (j/k, h/l, g/G, y/Y, …). When off
|
||||
(default), bare-letter and `Shift+letter` keys in the scrollback focus the
|
||||
prompt and type the character. Persists to `[ui].vim_mode` in `config.toml`.
|
||||
|
||||
```
|
||||
/vim-mode
|
||||
```
|
||||
|
||||
### `/minimal` and `/fullscreen`
|
||||
|
||||
Reopen the current session in the other render mode. `/minimal` (offered in
|
||||
fullscreen) switches to the experimental scrollback-native mode; `/fullscreen`
|
||||
(offered in minimal; alias `/full`) switches back to the standard alt-screen
|
||||
TUI. Both relaunch the pager on the same conversation for this session only —
|
||||
they do not write `config.toml`. Descriptions and the relaunch banner tell you
|
||||
how to switch back (`/fullscreen` ⇄ `/minimal`). The `--minimal` /
|
||||
`--fullscreen` CLI flags are likewise session-scoped. To make plain `grok` open
|
||||
in a given mode by default, use `/settings` → **Default screen mode**, or set
|
||||
`[ui] screen_mode` in `config.toml`.
|
||||
|
||||
```
|
||||
/minimal
|
||||
/fullscreen
|
||||
```
|
||||
|
||||
### `/plan`
|
||||
|
||||
Enter plan mode.
|
||||
|
||||
```
|
||||
/plan [description]
|
||||
```
|
||||
|
||||
### `/view-plan`
|
||||
|
||||
Open the current saved plan preview. Aliases: `/show-plan`, `/plan-view`.
|
||||
|
||||
```
|
||||
/view-plan
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Memory
|
||||
|
||||
The `/flush`, `/dream`, and `/memory` commands require `--experimental-memory` or `GROK_MEMORY=1`. `/remember` is always available.
|
||||
|
||||
### `/memory`
|
||||
|
||||
Browse, view, and manage your saved memories. Pass `on` or `off` to enable or disable memory.
|
||||
|
||||
```
|
||||
/memory
|
||||
/memory off
|
||||
```
|
||||
|
||||
Aliases: `/mem`
|
||||
|
||||
### `/flush`
|
||||
|
||||
Save current session knowledge to memory immediately. Triggers an LLM-generated summary of the session's most important content.
|
||||
|
||||
```
|
||||
/flush
|
||||
```
|
||||
|
||||
Use this when you want to preserve important context before compaction or at any point in a session.
|
||||
|
||||
### `/dream`
|
||||
|
||||
Run memory consolidation -- merge session logs into organized topics.
|
||||
|
||||
```
|
||||
/dream
|
||||
```
|
||||
|
||||
### `/remember`
|
||||
|
||||
Save a note to memory immediately, without waiting for an automatic summary.
|
||||
|
||||
```
|
||||
/remember the staging deploy uses the eu-west cluster
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Hooks and Plugins
|
||||
|
||||
The `/hooks`, `/plugins`, `/marketplace`, and `/skills` commands open the same extensions modal on different tabs.
|
||||
|
||||
### `/hooks`
|
||||
|
||||
Open the extensions modal on the Hooks tab. From the modal you can view loaded hooks, add or remove custom hooks, and enable or disable them individually. The modal does not grant project trust -- see [10-hooks.md](10-hooks.md) for the trust model.
|
||||
|
||||
```
|
||||
/hooks
|
||||
```
|
||||
|
||||
**Note:** The shell advertises individual `/hooks-list`, `/hooks-trust`, `/hooks-add`, `/hooks-remove`,
|
||||
and `/hooks-untrust` commands. In the TUI pager, these are consolidated into the `/hooks` modal.
|
||||
|
||||
### `/plugins`
|
||||
|
||||
Open the extensions modal on the Plugins tab. From the modal you can view installed plugins, install new ones from the marketplace, and manage trust.
|
||||
|
||||
```
|
||||
/plugins
|
||||
```
|
||||
|
||||
The shell also supports subcommands (`/plugins list`, `/plugins install <source>`,
|
||||
`/plugins uninstall <name>`, `/plugins update`). In the TUI, the `/plugins` modal
|
||||
provides the same functionality with a visual interface.
|
||||
|
||||
### `/marketplace`
|
||||
|
||||
Open the extensions modal on the Marketplace tab to browse and install plugins.
|
||||
|
||||
```
|
||||
/marketplace
|
||||
```
|
||||
|
||||
### `/skills`
|
||||
|
||||
Open the extensions modal on the Skills tab to view installed skills.
|
||||
|
||||
```
|
||||
/skills
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Media Generation
|
||||
|
||||
### `/imagine <description>`
|
||||
|
||||
Generate an image from a text description.
|
||||
|
||||
```
|
||||
/imagine a golden sunset over a calm ocean with silhouetted palm trees
|
||||
```
|
||||
|
||||
### `/imagine-video <description>`
|
||||
|
||||
Generate a video from an image or text description. Plans shots, generates source images, and animates them with `image_to_video`.
|
||||
|
||||
```
|
||||
/imagine-video a cat playing piano in a jazz club
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Scheduling
|
||||
|
||||
### `/loop [interval] <prompt>`
|
||||
|
||||
Run a prompt on a recurring interval. Specify the interval as `30m`, `1 hour`, or `every 2 days`. If you omit it, Grok prompts you.
|
||||
|
||||
```
|
||||
/loop 30m check deploy status
|
||||
/loop check deploy status every hour
|
||||
```
|
||||
|
||||
Interval format: `Ns` (seconds, min 60), `Nm` (minutes), `Nh` (hours), `Nd` (days). Intervals under 60 seconds are raised to the 60-second minimum.
|
||||
|
||||
Recurring tasks auto-expire after 7 days. Cancel with `scheduler_delete` (the job ID is provided when the loop is created).
|
||||
|
||||
---
|
||||
|
||||
## Other
|
||||
|
||||
### `/goal`
|
||||
|
||||
Set, manage, or check an autonomous goal. Grok works toward the objective across turns and reports progress.
|
||||
|
||||
```
|
||||
/goal Migrate the auth module to the new API
|
||||
/goal status
|
||||
```
|
||||
|
||||
Arguments: `<objective>`, `status`, `pause`, `resume`, or `clear`. **Availability:** appears only when the goal feature is enabled and the `update_goal` tool is in the session toolset.
|
||||
|
||||
### `/theme`
|
||||
|
||||
Switch the TUI color theme.
|
||||
|
||||
```
|
||||
/theme
|
||||
```
|
||||
|
||||
Aliases: `/t`
|
||||
|
||||
### `/feedback [message]`
|
||||
|
||||
Report an issue or send feedback.
|
||||
|
||||
```
|
||||
/feedback Something isn't working correctly
|
||||
```
|
||||
|
||||
### `/btw`
|
||||
|
||||
Send an aside to the agent without interrupting the current task.
|
||||
|
||||
```
|
||||
/btw also check the error handling
|
||||
```
|
||||
|
||||
### `/mcps`
|
||||
|
||||
Open the MCP servers management modal.
|
||||
|
||||
```
|
||||
/mcps
|
||||
```
|
||||
|
||||
### `/terminal-setup`
|
||||
|
||||
Show terminal capability detection and setup info — including color level, which themes are available, clipboard routes, and fix instructions for common issues (truecolor, tmux clipboard, keyboard protocol).
|
||||
|
||||
```
|
||||
/terminal-setup
|
||||
```
|
||||
|
||||
Aliases: `/terminal-check`, `/terminal-info`
|
||||
|
||||
### `/release-notes`
|
||||
|
||||
View release notes for the current version.
|
||||
|
||||
```
|
||||
/release-notes
|
||||
```
|
||||
|
||||
Aliases: `/changelog`
|
||||
|
||||
### `/docs`
|
||||
|
||||
Browse in-TUI How-to Guides, open online Build docs, or jump to a guide by title.
|
||||
|
||||
```
|
||||
/docs
|
||||
/docs web
|
||||
/docs Getting Started
|
||||
```
|
||||
|
||||
- Bare `/docs` (or `/docs how-to`) opens the How-to Guides picker
|
||||
- `/docs web` opens https://docs.x.ai/build/overview in the browser
|
||||
- `/docs <title>` opens a specific guide (case-insensitive title match)
|
||||
|
||||
Aliases: `/howto`, `/guides`
|
||||
|
||||
### `/import-claude`
|
||||
|
||||
Open the Claude settings import modal to bring over `~/.claude` settings: permissions, environment variables, MCP servers, hooks, and paths.
|
||||
|
||||
```
|
||||
/import-claude
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Agents and Personas
|
||||
|
||||
### `/config-agents`
|
||||
|
||||
Open the agents modal to view and manage agent definitions, set the default agent, and switch the active one.
|
||||
|
||||
```
|
||||
/config-agents
|
||||
```
|
||||
|
||||
Aliases: `/agents`
|
||||
|
||||
### `/personas`
|
||||
|
||||
Manage personas -- create, edit, and delete personas. A subagent can apply a persona to shape its behavior.
|
||||
|
||||
```
|
||||
/personas
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Account and Billing
|
||||
|
||||
### `/login`
|
||||
|
||||
Log in or re-authenticate with your account without leaving the session.
|
||||
|
||||
```
|
||||
/login
|
||||
```
|
||||
|
||||
### `/logout`
|
||||
|
||||
Log out and return to the login screen.
|
||||
|
||||
```
|
||||
/logout
|
||||
```
|
||||
|
||||
### `/usage`
|
||||
|
||||
View credit usage or manage billing.
|
||||
|
||||
```
|
||||
/usage
|
||||
```
|
||||
|
||||
### `/privacy`
|
||||
|
||||
Show or toggle privacy and data-retention status.
|
||||
|
||||
```
|
||||
/privacy
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Configuration and UI
|
||||
|
||||
### `/settings`
|
||||
|
||||
Open the settings modal to view and change configuration interactively.
|
||||
|
||||
```
|
||||
/settings
|
||||
```
|
||||
|
||||
Aliases: `/config`, `/preferences`, `/prefs`
|
||||
|
||||
### `/timestamps`
|
||||
|
||||
Toggle message timestamps on or off.
|
||||
|
||||
```
|
||||
/timestamps
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Skills as Slash Commands
|
||||
|
||||
Any enabled skill with `user-invocable: true` in its SKILL.md frontmatter appears as a slash command. (A skill turned off via `/skills` is not advertised.) For example, if you have a skill at `~/.grok/skills/commit/SKILL.md`, you can invoke it with:
|
||||
|
||||
```
|
||||
/commit fix typo in README
|
||||
```
|
||||
|
||||
Skills from plugins also appear as slash commands. When multiple skills share the same name (across scopes), use the qualified form:
|
||||
|
||||
```
|
||||
/local:commit # Project-scoped skill
|
||||
/user:commit # User-scoped skill
|
||||
```
|
||||
|
||||
Built-in slash commands always take priority over skills with the same name. If you name a skill "compact", typing `/compact` will run the built-in compact command, but `/local:compact` will invoke the skill.
|
||||
|
||||
---
|
||||
|
||||
## Autocomplete
|
||||
|
||||
The slash command menu supports fuzzy search. Start typing after `/` to filter available commands. The menu shows:
|
||||
|
||||
- Command name
|
||||
- Description
|
||||
- Argument hint (if the command accepts arguments)
|
||||
- Source (builtin, skill scope, plugin name)
|
||||
|
||||
Press `Tab` or `Enter` to select a command from the autocomplete menu.
|
||||
|
|
@ -0,0 +1,838 @@
|
|||
# Configuration
|
||||
|
||||
Grok reads configuration from local config files, environment variables, and
|
||||
CLI flags. This document covers the common options.
|
||||
|
||||
---
|
||||
|
||||
## Precedence
|
||||
|
||||
Configuration is resolved in this order (highest priority first):
|
||||
|
||||
1. **CLI flags** (e.g., `--yolo`, `--model`, `--sandbox`)
|
||||
2. **Environment variables** (e.g., `XAI_API_KEY`, `GROK_MEMORY`)
|
||||
3. **config.toml** (`~/.grok/config.toml`)
|
||||
4. **Managed / requirements config** (local files your org may deploy, e.g.
|
||||
`managed_config.toml` / `requirements.toml`)
|
||||
5. **Built-in defaults**
|
||||
|
||||
---
|
||||
|
||||
## config.toml (Main Configuration)
|
||||
|
||||
Location: `~/.grok/config.toml`
|
||||
|
||||
If the file does not exist, Grok uses built-in defaults. Specify only the values you want to override.
|
||||
|
||||
### General Settings
|
||||
|
||||
```toml
|
||||
[cli]
|
||||
auto_update = true # check for updates on launch
|
||||
|
||||
[models]
|
||||
default = "grok-build" # model used for new sessions
|
||||
web_search = "grok-4.20-multi-agent" # model used by the web_search tool
|
||||
|
||||
# Defaults applied to every model; a per-model [model.<id>] value always wins.
|
||||
# See "Custom Models" for the per-model overrides and full details.
|
||||
extra_headers = { "X-Request-Tags" = "team=example,env=prod" }
|
||||
temperature = 0.7
|
||||
top_p = 0.95
|
||||
max_completion_tokens = 8192
|
||||
max_retries = 8
|
||||
inference_idle_timeout_secs = 600
|
||||
stream_tool_calls = true
|
||||
|
||||
[ui]
|
||||
simple_mode = true # readline-style prompt editing (default); false = vim editing in the prompt
|
||||
vim_mode = false # vim-style scrollback navigation keys (default: false)
|
||||
max_thoughts_width = 120 # max column width for reasoning display
|
||||
default_selected_permission = "always_allow_all_sessions" # preselected row on the FIRST approval prompt
|
||||
remember_tool_approvals = false # show per-command "Always allow" options on permission prompts;
|
||||
# grants are remembered per project (default: false); see 22-permissions-and-safety.md
|
||||
show_thinking_blocks = true # show agent thinking blocks in the TUI (default: true)
|
||||
group_tool_verbs = true # fold runs of read/search/list tool calls and subagent rows
|
||||
# — and finished thoughts among them — into one row (default: true)
|
||||
collapsed_edit_blocks = false # show edits as one-line +N/-M diffstat summaries and merge
|
||||
# back-to-back same-file edits into one row, expand for the
|
||||
# diffs (default: false; pager.toml [scrollback.blocks.edit]
|
||||
# expanded_by_default/line_summary override its fold shape)
|
||||
screen_mode = "fullscreen" # default render mode: "fullscreen" | "minimal"
|
||||
# (unset → fullscreen); set via /settings → Default screen mode
|
||||
|
||||
[features]
|
||||
telemetry = false # anonymous usage telemetry
|
||||
feedback = true # feedback system (default: true)
|
||||
lsp_tools = false # expose the lsp tool
|
||||
codebase_indexing = true # code graph indexing
|
||||
two_pass_compaction = false # prefire two-pass compaction (default: false, opt-in)
|
||||
remote_fetch = true # allow optional online model-catalog fetches (default: true;
|
||||
# set false for firewalled/air-gapped deployments; background
|
||||
# managed-config sync has its own switch: managed_config)
|
||||
|
||||
[session]
|
||||
auto_compact_threshold_percent = 85 # auto-compact at this % of context window
|
||||
load_envrc = true # load .envrc environment variables
|
||||
|
||||
[tools]
|
||||
respect_gitignore = false # default: false; set true to make every tool skip gitignored files
|
||||
```
|
||||
|
||||
#### Input Mode
|
||||
|
||||
The `simple_mode` setting under `[ui]` controls how you edit text in the
|
||||
**prompt** — the input editor. It does not change how you navigate the
|
||||
scrollback; that is governed separately by [`vim_mode`](#vim-mode).
|
||||
|
||||
| Value | Behavior |
|
||||
|-------|----------|
|
||||
| `true` (default) | **Readline editing.** The prompt uses plain readline-style text entry. |
|
||||
| `false` | **Vim editing (experimental).** The prompt uses vim-style modal editing (normal and insert modes). When the prompt is empty, it starts in normal mode with focus on the scrollback. |
|
||||
|
||||
To switch the prompt to vim-style editing:
|
||||
|
||||
```toml
|
||||
[ui]
|
||||
simple_mode = false
|
||||
```
|
||||
|
||||
You can also toggle this setting from the settings pane (`/settings` →
|
||||
**Disable vim input mode**); Grok writes your choice to `[ui] simple_mode` in
|
||||
`config.toml`.
|
||||
|
||||
`simple_mode` and `vim_mode` are independent: `simple_mode` changes the prompt
|
||||
editor, and `vim_mode` changes scrollback navigation. See [Keyboard Shortcuts](03-keyboard-shortcuts.md)
|
||||
for the full binding reference.
|
||||
|
||||
#### Default Selected Permission
|
||||
|
||||
When the agent asks for permission to run a command (or other tool action),
|
||||
the approval menu highlights one row by default — the cursor row. The
|
||||
`default_selected_permission` setting under `[ui]` controls which row that
|
||||
is on the **first** prompt of a session.
|
||||
|
||||
| Value | Preselected row |
|
||||
|-------|-----------------|
|
||||
| `always_allow_all_sessions` (default) | The "Always allow on all sessions" row. |
|
||||
| `allow_command_always` | The "Always allow this command" row. |
|
||||
| `allow_once` | The "Yes" / allow-once row. |
|
||||
| `reject` | The reject row. |
|
||||
|
||||
```toml
|
||||
[ui]
|
||||
default_selected_permission = "allow_once"
|
||||
```
|
||||
|
||||
After you answer the first prompt, the cursor becomes **sticky**: each later
|
||||
prompt preselects the same kind of choice you last confirmed (e.g. once you
|
||||
pick "No", subsequent prompts start on their reject row), carrying across edit
|
||||
/ bash / MCP prompts until you restart. So `default_selected_permission` only
|
||||
sets the starting point.
|
||||
|
||||
The accepted values are `always_allow_all_sessions`, `allow_command_always`,
|
||||
`allow_once`, and `reject` (matching case-insensitively). When the key is unset
|
||||
— or set to any unrecognized value — it falls back to `always_allow_all_sessions`.
|
||||
The `allow_command_always` row is scoped to the specific action being approved
|
||||
(command / tool / domain / edit-session), never a global allow-everything —
|
||||
that is `always_allow_all_sessions`. Note that the per-command "Always allow"
|
||||
rows appear only when `[ui] remember_tool_approvals = true` (default: false).
|
||||
See [22-permissions-and-safety.md](22-permissions-and-safety.md).
|
||||
|
||||
The setting can also be overridden with the `GROK_DEFAULT_SELECTED_PERMISSION`
|
||||
environment variable — handy for headless / agent test runs that shouldn't
|
||||
mutate `config.toml`. Precedence: env var → `config.toml` →
|
||||
`always_allow_all_sessions` (the default).
|
||||
|
||||
#### Vim Mode
|
||||
|
||||
The `vim_mode` setting under `[ui]` controls whether vim-style bindings are
|
||||
active in the **scrollback** pane. It does not affect the input prompt.
|
||||
|
||||
| Value | Behavior |
|
||||
|-------|----------|
|
||||
| `false` (default) | Bare-letter and `Shift+letter` keys (`j`/`k`, `h`/`l`, `g`/`G`, `y`/`Y`, `o`/`O`, `r`, `x`, `e`/`E`, `H`/`L`, plus `i`) are suppressed in the scrollback. Pressing one of those letters focuses the prompt and types the character. Arrows, `Tab`, `Space`, `PageUp`/`PageDown`, and all `Ctrl+letter` shortcuts still navigate the scrollback. `Esc` is **not** a scrollback navigation key — it follows clear / rewind / mid-turn-swallow policy (see [Keyboard Shortcuts](03-keyboard-shortcuts.md#escape)). |
|
||||
| `true` | All vim-style scrollback bindings are active, exactly as listed in [Keyboard Shortcuts](03-keyboard-shortcuts.md). |
|
||||
|
||||
Toggle `vim_mode` at runtime with `/vim-mode`, or from the settings pane
|
||||
(`/settings` → **Vim scrollback navigation**). Grok writes the change to
|
||||
`[ui] vim_mode` in `~/.grok/config.toml` immediately and applies it to every
|
||||
future pager session — including new agents and subagents started in the same
|
||||
process. There is no separate per-session override; whatever is in
|
||||
`config.toml` is the source of truth on next launch.
|
||||
|
||||
`vim_mode` is independent of `simple_mode`: `vim_mode` controls scrollback
|
||||
navigation, while `simple_mode` controls editing in the prompt.
|
||||
|
||||
#### Screen Mode
|
||||
|
||||
The `screen_mode` setting under `[ui]` is the **default render mode** for plain
|
||||
`grok` launches. Configure it from `/settings` → **Default screen mode**
|
||||
(restart required), or edit `config.toml` by hand. Both choices write
|
||||
`config.toml`. CLI flags (`--minimal` / `--fullscreen`) and slash commands
|
||||
(`/minimal` / `/fullscreen`) are session-scoped and do **not** write this key —
|
||||
after a slash switch, the reverse command (`/fullscreen` ⇄ `/minimal`) returns
|
||||
you for that session only.
|
||||
|
||||
| Value | Behavior |
|
||||
|-------|----------|
|
||||
| unset | Settings shows **Fullscreen**. At startup there is no sticky preference: legacy `pager.toml` `[terminal] minimal` can still force minimal, and terminals that leak mouse reports (JediTerm/Windows) may auto-open minimal until you set an explicit value. Otherwise the alt-screen policy picks fullscreen vs inline. |
|
||||
| `"fullscreen"` | Sticky non-minimal. Fullscreen-vs-inline still follows the alt-screen policy (`--no-alt-screen`, `[terminal] alt_screen`, terminal auto-detection). |
|
||||
| `"minimal"` | Sticky minimal (scrollback-native) mode. |
|
||||
|
||||
A CLI flag always wins over the config value for that invocation.
|
||||
|
||||
#### Scrolling
|
||||
|
||||
Four `[ui]` settings tune mouse-wheel and trackpad scrolling in the
|
||||
scrollback. All apply immediately (no restart) and are editable from the
|
||||
settings pane (`/settings` → **Scroll speed** / **Scroll input** /
|
||||
**Scroll lines** / **Invert scroll**).
|
||||
|
||||
| Key | Values (default) | Behavior |
|
||||
|-----|------------------|----------|
|
||||
| `scroll_speed` | `1`–`100` (`50`) | Speed multiplier for both wheel and trackpad. `50` = 1.0x, `1` = 0.1x, `100` = 6.0x. |
|
||||
| `scroll_mode` | `auto` \| `wheel` \| `trackpad` (`auto`) | Wheel-vs-trackpad detection is heuristic (terminal scroll events carry no magnitude); force one kind when auto-detection misreads your device — e.g. a wheel notch that jumps too far, or a trackpad that feels stepped. |
|
||||
| `scroll_lines` | `1`–`10` (unset) | Lines per scroll tick, applied to **both** wheel and trackpad. While unset, each terminal's own profile applies (e.g. a conservative 1 line/event under tmux). Committing any value — even `3`, the number the settings pane displays — switches permanently to that explicit override. |
|
||||
| `invert_scroll` | `false` \| `true` (`false`) | Reverse vertical scroll direction ("natural" scrolling). |
|
||||
|
||||
```toml
|
||||
[ui]
|
||||
scroll_speed = 50
|
||||
scroll_mode = "auto" # auto | wheel | trackpad
|
||||
invert_scroll = false
|
||||
# scroll_lines is unset by default: the per-terminal profile stays in charge.
|
||||
# scroll_lines = 3
|
||||
```
|
||||
|
||||
Each setting also has an environment-variable override, applied on first load
|
||||
only — handy for headless / test runs that shouldn't mutate `config.toml`:
|
||||
`GROK_SCROLL_SPEED`, `GROK_SCROLL_MODE`, `GROK_INVERT_SCROLL`
|
||||
(`1`/`true`/`0`/`false`), and `GROK_SCROLL_LINES`. Precedence: env var →
|
||||
`config.toml` → default. Unrecognized values fall back to the default, and
|
||||
out-of-range numbers clamp to the allowed range.
|
||||
|
||||
### Tool Configuration
|
||||
|
||||
```toml
|
||||
[toolset.bash]
|
||||
timeout_secs = 120.0 # foreground command timeout in seconds (default: 120)
|
||||
output_byte_limit = 20000 # max captured output in bytes (default: 20000)
|
||||
|
||||
[toolset.ask_user_question]
|
||||
timeout_enabled = true # false = wait forever for answers (default: true)
|
||||
timeout_secs = 1800 # seconds to wait when enabled (default: 1800 / 30 min)
|
||||
|
||||
[toolset.web_fetch]
|
||||
proxy_endpoint = "https://proxy.example.com" # egress proxy URL
|
||||
allowed_domains = ["docs.rs", "x.ai"] # override the built-in allowlist
|
||||
```
|
||||
|
||||
`[toolset.ask_user_question]` is honored across **requirements.toml**, **managed
|
||||
config**, and **user `config.toml`**. Precedence: requirements → env
|
||||
(`GROK_ASK_USER_QUESTION_TIMEOUT_ENABLED` /
|
||||
`GROK_ASK_USER_QUESTION_TIMEOUT_SECS`) → user config → managed →
|
||||
defaults. Set `timeout_enabled = false` in your user config to disable the
|
||||
automatic questionnaire timeout for yourself; `timeout_secs` must be a
|
||||
positive integer. `timeout_enabled` can also be toggled from the settings
|
||||
pane (`/settings` → **Ask-Question timeout**, under Agent & Approval);
|
||||
changes apply to newly started sessions.
|
||||
|
||||
### Authentication
|
||||
|
||||
See [Authentication](02-authentication.md) for full details.
|
||||
|
||||
```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 = "0oa1b2c3d4e5f6g7h8i9"
|
||||
# scopes = ["openid", "profile", "email", "offline_access", "api:access"]
|
||||
# audience = "https://api.acme.com"
|
||||
```
|
||||
|
||||
### Custom Models
|
||||
|
||||
Add custom model endpoints to use alternative providers or self-hosted models.
|
||||
|
||||
```toml
|
||||
[model.my-model]
|
||||
model = "model-id" # model identifier sent to API
|
||||
base_url = "https://api.example.com/v1" # OpenAI-compatible endpoint
|
||||
name = "Display Name" # shown in model picker
|
||||
description = "Model description" # optional
|
||||
api_key = "sk-..." # API key for this provider
|
||||
env_key = "XAI_API_KEY" # env var(s) holding the API key; string or array (first set, non-empty wins)
|
||||
temperature = 0.7 # sampling temperature (0.0-2.0)
|
||||
top_p = 0.95 # nucleus sampling parameter
|
||||
max_completion_tokens = 8192 # max tokens per response
|
||||
context_window = 128000 # context window size (for auto-compact)
|
||||
```
|
||||
|
||||
Credential resolution: `api_key` > `env_key` > signed-in session token > `XAI_API_KEY`.
|
||||
|
||||
Override built-in models by using their name as the section key:
|
||||
|
||||
```toml
|
||||
[model.grok-build]
|
||||
api_key = "my-api-key" # only override the fields you need
|
||||
```
|
||||
|
||||
### MCP Servers
|
||||
|
||||
Configure external tool integrations via the Model Context Protocol.
|
||||
|
||||
```toml
|
||||
[mcp_servers.github]
|
||||
command = "npx"
|
||||
args = ["-y", "@modelcontextprotocol/server-github"]
|
||||
env = { GITHUB_PERSONAL_ACCESS_TOKEN = "ghp_xxx" }
|
||||
enabled = true # enable/disable (default: true)
|
||||
startup_timeout_sec = 30 # init timeout in seconds (default: 30)
|
||||
tool_timeout_sec = 6000 # tool call timeout in seconds (default: 6000)
|
||||
tool_timeouts = { create_issue = 120 } # per-tool timeout overrides
|
||||
|
||||
[mcp_servers.postgres]
|
||||
command = "npx"
|
||||
args = ["-y", "@modelcontextprotocol/server-postgres", "postgresql://user:pass@localhost/db"]
|
||||
|
||||
[mcp_servers.my-streamable-server]
|
||||
url = "https://mcp.example.com/api/mcp" # HTTP/SSE transport
|
||||
headers = { "x-mcp-session-id" = "{{session_id}}" }
|
||||
```
|
||||
|
||||
MCP servers can also be configured per-project in `.grok/config.toml`. Project-scoped config contributes `[mcp_servers]`, `[plugins]`, and `[permission]` rules; other sections load only from `~/.grok/config.toml`.
|
||||
|
||||
Priority for `[mcp_servers]` and `[plugins]`: `.grok/config.toml` (current dir) > `<repo-root>/.grok/config.toml` > `~/.grok/config.toml`. `[permission]` rules are not overridden by priority; they merge across all files with `deny` > `ask` > `allow` (see [22-permissions-and-safety.md](22-permissions-and-safety.md)).
|
||||
|
||||
### Memory
|
||||
|
||||
Persist knowledge across sessions (requires `--experimental-memory` or `GROK_MEMORY=1`).
|
||||
|
||||
```toml
|
||||
[memory]
|
||||
enabled = false # enable memory
|
||||
|
||||
[memory.session]
|
||||
save_on_end = true # write metadata summary on session end
|
||||
|
||||
[memory.watcher]
|
||||
enabled = true # watch memory files for external edits
|
||||
|
||||
[memory.search]
|
||||
max_results = 6 # default number of results
|
||||
min_score = 0.35 # minimum relevance score
|
||||
|
||||
[memory.initial_injection]
|
||||
enabled = true # auto-inject memory on first turn
|
||||
min_score = 0.0 # score threshold for first-turn injection
|
||||
|
||||
[memory.embedding]
|
||||
model = "embedding-model" # embedding model name
|
||||
dimensions = 1024 # vector dimensions
|
||||
```
|
||||
|
||||
### Subagents
|
||||
|
||||
```toml
|
||||
[subagents]
|
||||
enabled = true
|
||||
|
||||
[subagents.toggle]
|
||||
explore = true # enable/disable specific types
|
||||
plan = false
|
||||
|
||||
[subagents.models]
|
||||
explore = "grok-build" # route to different models
|
||||
```
|
||||
|
||||
To pin the model a subagent uses, set its entry under `[subagents.models]`.
|
||||
|
||||
### Skills
|
||||
|
||||
```toml
|
||||
[skills]
|
||||
paths = ["~/my-team-skills"] # additional directories to scan
|
||||
ignore = ["~/my-team-skills/wip"] # paths to exclude
|
||||
disabled = ["wip-skill"] # skill names to keep listed but inactive
|
||||
```
|
||||
|
||||
### Harness Compatibility
|
||||
|
||||
Control vendor compatibility for Cursor, Claude, and Codex. Every cell defaults to `true`; session cells remain staged/inert until the foreign-session scanner consumes them.
|
||||
|
||||
Session cells remain staged until a foreign-session scanner consumes them. Each tool requires both its `sessions` cell and corresponding `resume-claude`, `resume-codex`, or `resume-cursor` skill; a missing skill means zero foreign-session filesystem I/O.
|
||||
|
||||
```toml
|
||||
[compat.cursor]
|
||||
skills = true # scan ~/.cursor/skills/ and <cwd>/.cursor/skills/
|
||||
rules = true # scan <cwd>/.cursor/rules/
|
||||
agents = true # scan ~/.cursor/ for AGENTS.md files
|
||||
mcps = true # scan ~/.cursor/mcp.json and <cwd>/.cursor/mcp.json
|
||||
hooks = true # scan ~/.cursor/hooks.json and <cwd>/.cursor/hooks.json
|
||||
sessions = true # staged; no scanner consumer yet
|
||||
|
||||
[compat.claude]
|
||||
skills = true # scan ~/.claude/skills/ and <cwd>/.claude/skills/
|
||||
rules = true # scan <cwd>/.claude/rules/
|
||||
agents = true # scan ~/.claude/ for CLAUDE.md / CLAUDE.local.md
|
||||
mcps = true # scan ~/.claude.json for MCP servers
|
||||
hooks = true # scan ~/.claude/settings.json for hooks
|
||||
sessions = true # staged; no scanner consumer yet
|
||||
|
||||
[compat.codex]
|
||||
sessions = true # staged; no scanner consumer yet
|
||||
```
|
||||
|
||||
Codex `skills`, `rules`, `agents`, `mcps`, and `hooks` cells are reserved and currently inert; they do not enable `.codex` discovery.
|
||||
|
||||
Each cell can be toggled via environment variable or `config.toml`. See the
|
||||
environment-variables reference for the env var names. Resolution order:
|
||||
env var > config.toml > default (on).
|
||||
|
||||
`grok inspect` reports cells that still need session-start resolution as
|
||||
`?` until a value is available; cells with an explicit env or TOML value
|
||||
use that value. Affected discovery entries report
|
||||
`compatibilityStatus: "unresolved"` in JSON and `[compat unresolved]` in
|
||||
human output.
|
||||
|
||||
### Plugins
|
||||
|
||||
```toml
|
||||
[plugins]
|
||||
paths = ["~/my-plugins/custom-tools"]
|
||||
disabled = ["user/a1b2c3d4/noisy-plugin"]
|
||||
```
|
||||
|
||||
### Hints
|
||||
|
||||
The `[hints]` table holds small persisted UI preferences — mostly "stop asking me" opt-outs. Grok writes these for you when you pick a "don't ask again" / "reset in config.toml" option in the TUI, but you can edit or remove them by hand. Deleting a key restores the default behavior.
|
||||
|
||||
`[hints]` is read from the **effective config merge** (same precedence as other settings): system managed → user `managed_config.toml` → user `config.toml` → user `requirements.toml` → system `requirements.toml`. Higher-priority layers override lower ones. The TUI only **writes** opt-outs to user `~/.grok/config.toml`.
|
||||
|
||||
```toml
|
||||
[hints]
|
||||
project_picker_disabled = false # skip the project-directory picker
|
||||
memory_modal_fullscreen = false # remember the memory modal fullscreen state
|
||||
new_session_worktree_mode = "never" # /new worktree prompt: "ask" | "always" | "never"
|
||||
fork_worktree_mode = "ask" # /fork worktree prompt: "ask" | "always" | "never"
|
||||
```
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
|-----|------|---------|-------------|
|
||||
| `project_picker_disabled` | bool | `false` | When `true`, skips the picker that asks you to choose a project directory on the first prompt when Grok is launched from a non-project directory (home, Desktop, Downloads, `/tmp`). Set automatically when you choose **"Don't ask me again"** in that picker. Teams can pin this in `managed_config.toml` or `requirements.toml` via `[hints] project_picker_disabled = true`. |
|
||||
| `memory_modal_fullscreen` | bool | `false` | Remembers whether the memory modal was last opened fullscreen. |
|
||||
| `new_session_worktree_mode` | string | `"never"` | Worktree prompt for `/new`: `ask` shows the popup, `always` creates a worktree, `never` skips it. |
|
||||
| `fork_worktree_mode` | string | `"ask"` | Worktree prompt for `/fork`: `ask`, `always`, or `never`. |
|
||||
|
||||
### Notifications
|
||||
|
||||
Send terminal notifications when the agent finishes a turn or needs
|
||||
approval. Notifications use terminal-native protocols (OSC 9, OSC 99, OSC 777,
|
||||
or BEL) and are focus-gated by default so they only fire when you are not
|
||||
looking at the terminal.
|
||||
|
||||
```toml
|
||||
[ui.notifications]
|
||||
method = "auto" # auto|osc9|osc99|osc777|bel|none
|
||||
condition = "unfocused" # unfocused|always|never
|
||||
idle_threshold_secs = 3 # seconds unfocused before a notification fires
|
||||
events = ["turn_complete", "approval_required"]
|
||||
sleep_prevention = true # prevent display sleep during agent turns
|
||||
progress_bar = true # show tab progress bar (OSC 9;4)
|
||||
|
||||
[ui.notifications.title]
|
||||
enabled = true
|
||||
items = ["action-required", "spinner", "activity", "session-name", "grok"]
|
||||
```
|
||||
|
||||
| Option | Type | Default | Description |
|
||||
|--------|------|---------|-------------|
|
||||
| `method` | string | `"auto"` | Notification protocol. `auto` picks the best for your terminal. |
|
||||
| `condition` | string | `"unfocused"` | When to notify: `unfocused` (only when terminal lost focus), `always`, or `never`. |
|
||||
| `idle_threshold_secs` | integer | `3` | Minimum seconds the terminal must be unfocused before a notification fires. |
|
||||
| `events` | array | `["turn_complete", "approval_required"]` | Events that trigger notifications. Options: `turn_complete`, `approval_required`, `session_ready`, `task_complete`, `agent_error`. |
|
||||
| `sleep_prevention` | bool | `true` | Keep the display awake while the agent is working (macOS/Linux). |
|
||||
| `progress_bar` | bool | `true` | Show a progress indicator in the terminal tab (OSC 9;4). |
|
||||
| `title.enabled` | bool | `true` | Set the terminal title to reflect agent state. |
|
||||
| `title.items` | array | (see above) | Items shown in the title bar. Options: `action-required`, `spinner`, `activity`, `session-name`, `cwd`, `model`, `turn-timer`, `grok`. |
|
||||
|
||||
#### Terminal Support Matrix
|
||||
|
||||
| Terminal | Auto Protocol | Focus Tracking | Progress Bar |
|
||||
|----------|---------------|----------------|--------------|
|
||||
| iTerm2 | OSC 9 | Yes | Yes |
|
||||
| Kitty | OSC 99 | Yes | No |
|
||||
| Ghostty | OSC 777 | Yes | Yes |
|
||||
| WezTerm | OSC 9 | Yes | Yes |
|
||||
| Warp | OSC 9 | Yes | No |
|
||||
| Alacritty | BEL | Yes | No |
|
||||
| VS Code | BEL | Yes | No |
|
||||
| Apple Terminal | BEL | No | No |
|
||||
| VTE (GNOME Terminal) | OSC 777 | Yes | No |
|
||||
| Grok Desktop | None (native) | N/A | N/A |
|
||||
| Unknown | BEL | No | No |
|
||||
|
||||
When `method = "auto"`, Grok detects the terminal brand and selects the best
|
||||
protocol automatically. Set `method` explicitly to override auto-detection.
|
||||
|
||||
#### Notification Hooks
|
||||
|
||||
Run custom commands when events occur. Hooks receive environment variables
|
||||
`$GROK_EVENT`, `$GROK_MESSAGE`, and `$GROK_SESSION_ID`.
|
||||
|
||||
```toml
|
||||
# macOS native notification
|
||||
[[ui.notifications.hooks]]
|
||||
command = "terminal-notifier -title 'Grok' -message '$GROK_MESSAGE'"
|
||||
events = ["turn_complete", "approval_required"]
|
||||
only_unfocused = true
|
||||
timeout_secs = 10
|
||||
|
||||
# Push to ntfy server
|
||||
[[ui.notifications.hooks]]
|
||||
command = "curl -s -d '$GROK_MESSAGE' ntfy.sh/my-grok-alerts"
|
||||
events = ["turn_complete"]
|
||||
only_unfocused = true
|
||||
timeout_secs = 10
|
||||
|
||||
# Play a sound
|
||||
[[ui.notifications.hooks]]
|
||||
command = "afplay /System/Library/Sounds/Glass.aiff"
|
||||
events = ["turn_complete"]
|
||||
only_unfocused = true
|
||||
timeout_secs = 5
|
||||
```
|
||||
|
||||
| Hook Option | Type | Default | Description |
|
||||
|-------------|------|---------|-------------|
|
||||
| `command` | string | (required) | Shell command to run. |
|
||||
| `events` | array | `[]` | Events that trigger this hook (empty = all events). |
|
||||
| `only_unfocused` | bool | `true` | Only fire when the terminal has lost focus. |
|
||||
| `timeout_secs` | integer | `10` | Kill the hook process after this many seconds (default: 10). |
|
||||
|
||||
#### Troubleshooting
|
||||
|
||||
**Notifications not working in tmux:**
|
||||
tmux blocks escape sequences by default. Enable passthrough for your terminal:
|
||||
|
||||
```bash
|
||||
# In ~/.tmux.conf
|
||||
set -g allow-passthrough on
|
||||
```
|
||||
|
||||
Then restart tmux. If passthrough is not available (tmux < 3.3), set
|
||||
`method` explicitly to `"bel"` which works without passthrough.
|
||||
|
||||
**Focus tracking not working:**
|
||||
Some terminals do not report focus events. If `condition = "unfocused"` never
|
||||
fires, try `condition = "always"` as a fallback. Grok supports focus tracking
|
||||
in every detected terminal except Apple Terminal and unrecognized terminals.
|
||||
|
||||
**Sleep prevention not taking effect:**
|
||||
On macOS, sleep prevention uses `IOPMAssertionCreateWithName` via CoreFoundation.
|
||||
On Linux, it uses `systemd-inhibit` (must be on `$PATH`). Check that the
|
||||
relevant tool is available. Sleep prevention is only active during agent turns
|
||||
and releases automatically when the turn ends.
|
||||
|
||||
### Keyboard Shortcuts
|
||||
|
||||
Keyboard shortcuts are **not configurable** via config files. All bindings are built in.
|
||||
See [Keyboard Shortcuts](03-keyboard-shortcuts.md) for the complete reference.
|
||||
|
||||
### Telemetry
|
||||
|
||||
The `[features] telemetry` toggle (in the `[features]` block above) is the master switch for anonymous usage telemetry. When telemetry is enabled, enterprises that run their own collector can redirect it or selectively disable parts of it under `[telemetry]`:
|
||||
|
||||
```toml
|
||||
[telemetry]
|
||||
events_url = "https://telemetry.your-company.com/events" # send events to your own collector
|
||||
events_api_key = "your-collector-token" # auth for your collector, if required
|
||||
mixpanel_enabled = false # disable Mixpanel product analytics
|
||||
trace_upload = false # disable session/trace uploads (inherits the telemetry toggle when unset)
|
||||
```
|
||||
|
||||
Set these only to point telemetry at your own infrastructure or to turn parts of it off. The built-in endpoint and credentials are managed by Grok; leave them unset to use the defaults.
|
||||
|
||||
The same `[telemetry]` table also configures the **external OpenTelemetry stream** — an independent opt-in (it does not require the telemetry toggle above) that ships a curated, content-free usage schema to your *own* OTLP collector. Collector auth is supplied via `OTEL_EXPORTER_OTLP_HEADERS` and is never stored on disk. See [Monitoring & Usage](24-monitoring-usage.md) for the full schema, env vars, and privacy model.
|
||||
|
||||
```toml
|
||||
[telemetry]
|
||||
otel_enabled = true # external OTEL master switch (= GROK_EXTERNAL_OTEL)
|
||||
otel_metrics_exporter = "otlp" # otlp | console | none
|
||||
otel_logs_exporter = "otlp" # otlp | console | none
|
||||
otel_endpoint = "https://collector.corp.example:4318" # OTLP base endpoint
|
||||
otel_protocol = "http/protobuf" # http/protobuf | grpc
|
||||
otel_log_user_prompts = false # content gate (admins can pin via requirements)
|
||||
otel_log_tool_details = false # content gate (admins can pin via requirements)
|
||||
```
|
||||
|
||||
### Enterprise Deployment
|
||||
|
||||
A complete config for enterprise use:
|
||||
|
||||
```toml
|
||||
[cli]
|
||||
auto_update = false
|
||||
|
||||
[auth]
|
||||
auth_provider_command = "/usr/local/bin/my-company-auth-provider"
|
||||
auth_provider_label = "Acme Corp"
|
||||
auth_token_ttl = 3600
|
||||
|
||||
[models]
|
||||
default = "company-grok"
|
||||
|
||||
[model.company-grok]
|
||||
model = "grok-build"
|
||||
base_url = "https://grok-proxy.acme.com/"
|
||||
name = "Grok Build Latest (Proxy)"
|
||||
context_window = 128000
|
||||
|
||||
[features]
|
||||
telemetry = false
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## pager.toml (Appearance Configuration)
|
||||
|
||||
Location: `~/.grok/pager.toml`
|
||||
|
||||
Controls the visual appearance and behavior of the TUI. Changes are applied on restart.
|
||||
|
||||
### Terminal
|
||||
|
||||
```toml
|
||||
[terminal]
|
||||
alt_screen = "auto" # fullscreen mode: "auto", "always", "never"
|
||||
```
|
||||
|
||||
- `auto` (default): Use alternate screen when the terminal supports it
|
||||
- `always`: Always use alternate screen
|
||||
- `never`: Run inline in the terminal's main scrollback buffer
|
||||
|
||||
### Animation
|
||||
|
||||
```toml
|
||||
[animation]
|
||||
fps = 30 # animation frame rate (ticks per second)
|
||||
wave_rows = 32 # rows per wave cycle for accent animation
|
||||
```
|
||||
|
||||
### Prompt
|
||||
|
||||
```toml
|
||||
[prompt]
|
||||
collapse_unfocused = true # collapse prompt when scrollback is focused
|
||||
mouse_hover = true # show hover highlight on the prompt widget
|
||||
show_prefix = true # show the prompt prefix character
|
||||
```
|
||||
|
||||
Compact mode is not persisted here. Control it at runtime with `[ui] compact_mode` or the `/compact-mode` command.
|
||||
|
||||
### Scrollback
|
||||
|
||||
```toml
|
||||
[scrollback.layout]
|
||||
outer_vpad = 1 # vertical padding
|
||||
outer_hpad_left = 2 # left horizontal padding
|
||||
outer_hpad_right = 2 # right horizontal padding
|
||||
block_pad_left = 2 # padding inside block, left of content
|
||||
block_pad_right = 2 # padding inside block, right of content
|
||||
|
||||
[scrollback.scrollbar]
|
||||
enabled = true # show scrollbar
|
||||
gap_left = 0 # gap between content and scrollbar
|
||||
gap_right = 0 # gap between scrollbar and screen edge
|
||||
|
||||
[scrollback.scroll]
|
||||
margin = 0 # minimum context lines above/below selection
|
||||
min_page_fraction = 0 # minimum scroll as % of viewport (0-100)
|
||||
follow_indicator = "center" # follow indicator: "center" or "none"
|
||||
follow_auto_select = true # auto-select latest entry in follow mode
|
||||
follow_by_overscroll = true # scrolling past bottom engages follow mode
|
||||
anchor_on_fold = true # keep block position when folding
|
||||
respect_manual_folds = true # opt-in (default: false): keep manually folded blocks as-is during streaming/finish; expanding while following stops auto-scroll
|
||||
|
||||
[scrollback.display]
|
||||
sticky_headers = true # pin user prompts as sticky headers
|
||||
tab_width = 4 # spaces per tab character
|
||||
expandable_indicator = true # show expand indicator on foldable entries
|
||||
expandable_indicator_running = true # show indicator on running entries
|
||||
expandable_indicator_char = "›" # character for the expand indicator (default: "›")
|
||||
selection_buttons = false # show copy/view buttons on selection
|
||||
line_under_last_entry = false # horizontal line below last entry
|
||||
group_selection_split = true # split selection box for expanded blocks
|
||||
highlight_overlays_border = false # highlight extends over selection box border
|
||||
dim_accent = 0.5 # dimming factor for collapsed accents (0.0-1.0)
|
||||
```
|
||||
|
||||
`respect_manual_folds` is off by default; set it to `true` to opt in. When
|
||||
enabled, a block you fold by hand is pinned: streaming updates and finish
|
||||
events (such as a thinking block ending) leave its fold state alone, and
|
||||
expanding a block while follow-mode is tailing new content stops the
|
||||
auto-scroll so the view stays put. Follow resumes via `Shift+G`, `j` at the
|
||||
last entry, scrolling past the bottom, or sending a new prompt. `Shift+E`
|
||||
clears all pins; `Ctrl+E` clears pins on thinking blocks.
|
||||
|
||||
### Block Configuration
|
||||
|
||||
```toml
|
||||
[scrollback.blocks.edit]
|
||||
indent = true # indent diff content
|
||||
vpad = false # vertical padding
|
||||
# expanded_by_default = true # unset: follows [ui] collapsed_edit_blocks in config.toml
|
||||
# (flag on = collapsed one-liner); uncomment to pin either shape
|
||||
dual_line_numbers = false # two-column line numbers (old + new)
|
||||
# line_summary = false # show +N/-M in the collapsed header; unset follows the same flag
|
||||
hunk_separator = "…" # separator between diff hunks (default: "…")
|
||||
|
||||
[scrollback.blocks.prompt]
|
||||
vpad = true # vertical padding
|
||||
show_prefix = true # show prompt prefix character
|
||||
min_lines = 2 # minimum content lines in sticky mode
|
||||
|
||||
[scrollback.blocks.thinking]
|
||||
animate = true # animated accent while thinking
|
||||
truncated_lines = 3 # lines in truncated mode
|
||||
```
|
||||
|
||||
### Todo
|
||||
|
||||
```toml
|
||||
[todo]
|
||||
badge_format = "default" # "default", "colon", or "comma"
|
||||
```
|
||||
|
||||
Badge format examples:
|
||||
- `default`: `2/5` -- a `done/total` progress fraction (done = completed, total = all tasks except cancelled)
|
||||
- `colon`: `[>:1 [ ]:4 ok:3 x:2]` -- icon:count
|
||||
- `comma`: `[1 >, 4 [ ], 3 ok, 2 x]` -- count icon, comma-separated
|
||||
|
||||
### Plugins
|
||||
|
||||
```toml
|
||||
disable_plugins = false # hide hooks/plugins UI entirely
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Environment Variables
|
||||
|
||||
Key environment variables. See the README for the complete list.
|
||||
|
||||
### Authentication
|
||||
|
||||
| Variable | Description |
|
||||
|----------|-------------|
|
||||
| `XAI_API_KEY` | API key from console.x.ai |
|
||||
| `GROK_AUTH_PROVIDER_COMMAND` | External auth binary path |
|
||||
| `GROK_AUTH_PROVIDER_LABEL` | Display name on TUI login screen |
|
||||
| `GROK_AUTH_TOKEN_TTL` | Token lifetime in seconds |
|
||||
| `GROK_AUTH_EARLY_INVALIDATION_SECS` | Seconds before expiry to refresh (default: 300) |
|
||||
| `GROK_OIDC_ISSUER` | OIDC issuer URL |
|
||||
| `GROK_OIDC_CLIENT_ID` | OIDC client ID |
|
||||
|
||||
### Endpoints
|
||||
|
||||
| Variable | Description |
|
||||
|----------|-------------|
|
||||
| `GROK_CLI_CHAT_PROXY_BASE_URL` | Override API proxy base URL |
|
||||
|
||||
### Features
|
||||
|
||||
| Variable | Description |
|
||||
|----------|-------------|
|
||||
| `GROK_MEMORY` | Enable (`1`) or disable (`0`) cross-session memory |
|
||||
| `GROK_SUBAGENTS` | Enable (`1`) or disable (`0`) subagents |
|
||||
| `GROK_WEB_FETCH` | Enable (`1`) or disable (`0`) the web_fetch tool |
|
||||
| `GROK_AGENT` | Custom agent definition path or name |
|
||||
| `GROK_SANDBOX` | Sandbox profile (off, workspace, devbox, read-only, strict; or a custom profile name) |
|
||||
|
||||
### Logging
|
||||
|
||||
| Variable | Description |
|
||||
|----------|-------------|
|
||||
| `GROK_LOG_FILE` | Write logs to this file path (the value is used verbatim as the path) |
|
||||
| `RUST_LOG` | Log level filter (for example `debug`); controls the `GROK_LOG_FILE` log and headless stderr output |
|
||||
|
||||
### Paths
|
||||
|
||||
| Variable | Description |
|
||||
|----------|-------------|
|
||||
| `GROK_HOME` | Override config directory (default: `~/.grok`) |
|
||||
| `GROK_RESPECT_GITIGNORE` | Force gitignore filtering on (`1`) or off (`0`); overrides `[tools] respect_gitignore` |
|
||||
|
||||
### Telemetry
|
||||
|
||||
| Variable | Description |
|
||||
|----------|-------------|
|
||||
| `GROK_TELEMETRY_ENABLED` | Enable/disable telemetry |
|
||||
| `GROK_FEEDBACK_ENABLED` | Enable/disable feedback system |
|
||||
| `GROK_DEPLOYMENT_KEY` | Management API key for enterprise |
|
||||
|
||||
---
|
||||
|
||||
## File Locations
|
||||
|
||||
| Path | Description |
|
||||
|------|-------------|
|
||||
| `~/.grok/config.toml` | Main configuration file |
|
||||
| `~/.grok/pager.toml` | TUI appearance configuration |
|
||||
| `~/.grok/auth.json` | Authentication credentials (auto-managed) |
|
||||
| `~/.grok/sessions/` | Persisted sessions (organized by working directory) |
|
||||
| `~/.grok/memory/` | Cross-session memory files and index |
|
||||
| `~/.grok/skills/` | User-scoped skill definitions |
|
||||
| `~/.grok/plugins/` | User-scoped plugins |
|
||||
| `~/.grok/agents/` | User-scoped agent definitions |
|
||||
| `~/.grok/lsp.json` | LSP server configuration (user-scoped) |
|
||||
| `~/.grok/logs/` | Internal log files (for example `unified.jsonl`, MCP server logs) |
|
||||
| `.grok/config.toml` | Project-scoped MCP servers, plugins, and permission rules |
|
||||
| `.grok/skills/` | Project-scoped skill definitions |
|
||||
| `.grok/plugins/` | Project-scoped plugins |
|
||||
| `.grok/agents/` | Project-scoped agent definitions |
|
||||
| `.grok/hooks/` | Project-scoped hooks |
|
||||
| `.grok/lsp.json` | LSP server configuration |
|
||||
|
||||
---
|
||||
|
||||
## Project-Scoped Configuration
|
||||
|
||||
Some configuration can be set per-project by placing files in `.grok/` within your repository:
|
||||
|
||||
| File | What it configures |
|
||||
|------|--------------------|
|
||||
| `.grok/config.toml` | MCP servers, plugins, permission rules, and the `[mcp] max_output_bytes` tool-result cap (other sections load only from `~/.grok/config.toml`) |
|
||||
| `.grok/skills/` | Project-specific skills |
|
||||
| `.grok/hooks/` | Project-specific lifecycle hooks |
|
||||
| `.grok/agents/` | Project-specific agent definitions |
|
||||
| `.grok/lsp.json` | LSP server configuration |
|
||||
| `.grok/sandbox.toml` | Custom sandbox profiles |
|
||||
| `AGENTS.md` | Project instructions (system prompt) |
|
||||
|
||||
Project-scoped MCP servers override global ones with the same name (full replacement, not merge).
|
||||
|
||||
---
|
||||
|
||||
## LSP Servers
|
||||
|
||||
Language servers power passive diagnostics and the optional `lsp` tool (see the [`lsp_tools`](#general-settings) feature flag). Server definitions are collected from three sources and merged by server name:
|
||||
|
||||
| Source | Location | Scope |
|
||||
|--------|----------|-------|
|
||||
| User | `~/.grok/lsp.json` | All projects |
|
||||
| Project | `.grok/lsp.json` | Current repository |
|
||||
| Plugin | A trusted plugin's `.lsp.json` file, or an inline `lspServers` block in its `plugin.json` | Wherever the plugin is enabled |
|
||||
|
||||
When the same server name is defined by more than one source, it is resolved in this order (highest priority first):
|
||||
|
||||
1. **Project** -- `.grok/lsp.json`
|
||||
2. **User** -- `~/.grok/lsp.json`
|
||||
3. **Plugins** -- file-based `.lsp.json`, then inline `lspServers`, in plugin load order
|
||||
|
||||
Project and user entries replace lower-priority ones with the same name. Plugin entries only add servers whose names are not already defined by a local file, so a local `lsp.json` always wins over a plugin. Plugin LSP servers load only after the plugin is trusted (see [Plugins](09-plugins.md)).
|
||||
344
crates/codegen/xai-grok-pager/docs/user-guide/06-theming.md
Normal file
344
crates/codegen/xai-grok-pager/docs/user-guide/06-theming.md
Normal file
|
|
@ -0,0 +1,344 @@
|
|||
# Theming and Appearance Customization
|
||||
|
||||
Grok Build draws all TUI colors from a central theme. You can switch themes while Grok is running, follow your operating system's light or dark appearance, and adjust scrollback layout, animations, and block styling through configuration files.
|
||||
|
||||
---
|
||||
|
||||
## Available Themes
|
||||
|
||||
Grok includes five built-in themes, plus an `auto` option that follows your system appearance:
|
||||
|
||||
| Theme | Config Names | Description | Truecolor Required |
|
||||
|-------|-------------|-------------|--------------------|
|
||||
| **GrokNight** | `groknight`, `grok-night`, `dark` | Neutral dark base with a magenta accent. Default theme. Survives quantization cleanly on 256-color and 16-color terminals. | No |
|
||||
| **GrokDay** | `grokday`, `grok-day`, `light`, `day` | Light theme for bright terminal backgrounds. | No |
|
||||
| **TokyoNight** | `tokyonight`, `tokyo-night`, `tokyo` | Dark, blue-tinted backgrounds from the Tokyo Night palette. Loses its character when quantized. | Yes |
|
||||
| **RosePineMoon** | `rosepine`, `rose-pine`, `rosepine-moon`, `rose-pine-moon` | Muted dark palette with mauve accents, from the Rosé Pine family. | Yes |
|
||||
| **OscuraMidnight** | `oscura`, `oscura-midnight` | Deep dark base with purple accents. | Yes |
|
||||
|
||||
Theme names are case-insensitive. The `auto` option (alias `system`) is documented under [Auto Theme (System Appearance)](#auto-theme-system-appearance).
|
||||
|
||||
### Minimal Mode Has No Theming
|
||||
|
||||
**Minimal mode** (`--minimal`) always renders with a single fixed terminal-native palette and ignores the `theme` settings entirely (they still apply to the full TUI). Minimal draws directly on your terminal's own background, so it uses your terminal's default foreground/background plus its 16-color ANSI palette — the same colors `git` or `ls` use — which stays readable on any light or dark terminal profile without detection or configuration. `/theme` and the theme rows in `/settings` are unavailable in minimal mode.
|
||||
|
||||
---
|
||||
|
||||
## Switching Themes
|
||||
|
||||
### In the TUI
|
||||
|
||||
Run the `/theme` slash command (alias `/t`) to open the theme picker. As you move through the list with the arrow keys, Grok previews each theme in real time. Press Enter to apply and save your choice, or press Escape to revert.
|
||||
|
||||
To switch without the picker, pass a name directly:
|
||||
|
||||
```
|
||||
/theme tokyonight
|
||||
```
|
||||
|
||||
Submitting `/theme` on its own -- without choosing from the picker -- cycles to the next theme.
|
||||
|
||||
### Via Config File
|
||||
|
||||
Set the theme in `~/.grok/config.toml`:
|
||||
|
||||
```toml
|
||||
[ui]
|
||||
theme = "tokyonight"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Auto Theme (System Appearance)
|
||||
|
||||
Set `theme = "auto"` to have Grok follow your operating system's light/dark appearance and switch themes automatically:
|
||||
|
||||
```toml
|
||||
[ui]
|
||||
theme = "auto"
|
||||
```
|
||||
|
||||
By default, dark mode maps to **GrokNight** and light mode maps to **GrokDay**. Override either mapping with `auto_dark_theme` and `auto_light_theme`:
|
||||
|
||||
```toml
|
||||
[ui]
|
||||
theme = "auto"
|
||||
auto_dark_theme = "tokyonight"
|
||||
auto_light_theme = "grokday"
|
||||
```
|
||||
|
||||
`theme = "system"` is an alias for `theme = "auto"`.
|
||||
|
||||
### How Detection Works
|
||||
|
||||
| Platform | Method |
|
||||
|----------|--------|
|
||||
| **macOS** | Reads `AppleInterfaceStyle` system preference |
|
||||
| **Linux** | Queries XDG Desktop Portal (`org.freedesktop.appearance.color-scheme`) |
|
||||
| **Windows** | Reads the system personalization registry |
|
||||
| **SSH / headless** | Falls back to an OSC 11 terminal background query at startup |
|
||||
|
||||
Once running, Grok polls for appearance changes every 5 seconds. Toggling your OS between light and dark mode takes effect within seconds without restarting.
|
||||
|
||||
### Via the Settings Pane
|
||||
|
||||
Run `/settings` (alias `/config`) and open the **Appearance** category to set the **Auto dark theme** and **Auto light theme** interactively. Selecting `auto` in the `/theme` picker enables auto mode using these mappings.
|
||||
|
||||
---
|
||||
|
||||
## Color Support Detection
|
||||
|
||||
On startup, Grok detects your terminal's color capability level:
|
||||
|
||||
| Level | Description | Detection |
|
||||
|-------|-------------|-----------|
|
||||
| **Truecolor** (24-bit) | Full RGB color. All themes render as designed. | `COLORTERM=truecolor` or equivalent terminal capability |
|
||||
| **256-color** | Indexed palette. RGB values are mapped to the nearest palette entry. | Standard xterm-256color |
|
||||
| **16-color** | ANSI names only. Colors are mapped to the closest ANSI color. | Basic terminal support |
|
||||
|
||||
When you set `NO_COLOR`, Grok emits no color and renders in monochrome.
|
||||
|
||||
Run `/terminal-setup` to see the detected level (`color` row) and which themes the picker offers on this terminal (`themes` row). When truecolor is missing, the issues section explains how to enable it (or that Terminal.app cannot).
|
||||
|
||||
### Automatic Quantization
|
||||
|
||||
Every theme is defined using full RGB values. At startup, Grok quantizes all colors to match the detected capability level. This means:
|
||||
|
||||
- On **truecolor** terminals, colors pass through unchanged.
|
||||
- On **256-color** terminals, each RGB value is mapped to the nearest indexed palette entry.
|
||||
- On **16-color** terminals, colors map to ANSI names.
|
||||
|
||||
GrokNight and GrokDay use neutral grays that quantize cleanly. TokyoNight, RosePineMoon, and OscuraMidnight use distinctive tinted backgrounds that lose their character when quantized, which is why the theme picker hides them on non-truecolor terminals.
|
||||
|
||||
### Runtime-Generated Colors
|
||||
|
||||
Colors generated at runtime (syntax highlighting, background blending) are also quantized through the same pipeline, ensuring consistent appearance across all terminal types.
|
||||
|
||||
---
|
||||
|
||||
## Cursor Color
|
||||
|
||||
Grok sets your terminal cursor to the current theme's `accent_user` color using the OSC 12 escape sequence, to indicate an active Grok session. The cursor color is:
|
||||
|
||||
- Applied on startup and on theme switch.
|
||||
- Reset to the terminal's default on exit via OSC 112.
|
||||
|
||||
This works in terminals that support OSC 12 (most modern terminals).
|
||||
|
||||
---
|
||||
|
||||
## Compact Mode
|
||||
|
||||
Toggle compact mode with the `/compact-mode` slash command. Compact mode:
|
||||
|
||||
- Removes outer vertical padding (top/bottom margins become 0).
|
||||
- Reduces horizontal padding to the minimum (1 column).
|
||||
- Reduces top padding in the prompt area and info blocks.
|
||||
|
||||
The setting is persisted in `~/.grok/config.toml` under `[ui].compact_mode` and survives restarts.
|
||||
|
||||
Use compact mode on small screens to maximize content area.
|
||||
|
||||
---
|
||||
|
||||
## Syntax Highlighting
|
||||
|
||||
Grok bundles three `.tmTheme` files for code-block syntax highlighting and selects one based on the active theme:
|
||||
|
||||
- `grok-night.tmTheme` -- GrokNight, RosePineMoon, and OscuraMidnight
|
||||
- `grok-day.tmTheme` -- GrokDay
|
||||
- `tokyo-night.tmTheme` -- TokyoNight
|
||||
|
||||
Grok selects the matching file automatically when you switch themes. The `.tmTheme` files are built into the binary, so you cannot replace them with your own.
|
||||
|
||||
---
|
||||
|
||||
## Deep Customization with pager.toml
|
||||
|
||||
For fine-grained control over the TUI appearance, create `~/.grok/pager.toml`. This file controls scrollback layout, block styling, animations, and more. All settings have defaults; specify only the values you override. (Dev builds generate this file as a template with every default commented out — uncomment a line to override it; commented values keep tracking future defaults.)
|
||||
|
||||
### Layout
|
||||
|
||||
Control viewport padding and block spacing:
|
||||
|
||||
```toml
|
||||
[scrollback.layout]
|
||||
outer_vpad = 1 # Vertical padding (top/bottom) for the viewport
|
||||
outer_hpad_left = 2 # Left margin (minimum: 1)
|
||||
outer_hpad_right = 2 # Right margin (minimum: 1)
|
||||
block_pad_left = 2 # Padding between accent line and content
|
||||
block_pad_right = 2 # Padding after content at right edge
|
||||
```
|
||||
|
||||
### Scrollbar
|
||||
|
||||
```toml
|
||||
[scrollback.scrollbar]
|
||||
enabled = true # Show/hide the scrollbar
|
||||
gap_left = 0 # Gap between content and scrollbar (0 = adjacent)
|
||||
gap_right = 0 # Gap between scrollbar and screen edge (0 = at edge)
|
||||
# scrollbar_bg = "none" # Override background color (or "none" for theme default)
|
||||
# scrollbar_fg = "none" # Override thumb color (or "none" for theme default)
|
||||
```
|
||||
|
||||
### Scroll Behavior
|
||||
|
||||
```toml
|
||||
[scrollback.scroll]
|
||||
margin = 0 # Context lines above/below selected entry (0 = edge)
|
||||
min_page_fraction = 0 # Minimum scroll as % of viewport (0-100)
|
||||
follow_indicator = "center" # "center" = show down-arrow, "none" = hidden
|
||||
follow_auto_select = true # Auto-select latest entry when following
|
||||
follow_by_overscroll = true # Scrolling past bottom engages follow mode
|
||||
anchor_on_fold = true # Keep block header at same screen position when folding
|
||||
```
|
||||
|
||||
### Display Options
|
||||
|
||||
```toml
|
||||
[scrollback.display]
|
||||
sticky_headers = true # Pin user prompts as headers when scrolled past
|
||||
tab_width = 4 # Spaces per tab character (0 = pass through)
|
||||
expandable_indicator = true # Show "›" on foldable collapsed entries
|
||||
expandable_indicator_char = "›" # Character to use (default: "›")
|
||||
collapsed_accent_char = "❙" # Accent for collapsed groupable blocks (falls back to "|" on the legacy Windows console)
|
||||
dim_accent = 0.5 # Blend factor for dimmed accents (0.0-1.0)
|
||||
line_under_last_entry = false # Horizontal line below last entry
|
||||
selection_buttons = false # Show copy/view buttons on selection box
|
||||
```
|
||||
|
||||
### Animation
|
||||
|
||||
```toml
|
||||
[animation]
|
||||
fps = 30 # Frame rate (1-60). Higher = smoother, more CPU
|
||||
wave_rows = 32 # Rows per wave cycle for accent animation
|
||||
```
|
||||
|
||||
### Block Styling: Edit Diffs
|
||||
|
||||
```toml
|
||||
[scrollback.blocks.edit]
|
||||
indent = true # Indent diff content
|
||||
vpad = false # Vertical padding around diffs
|
||||
# expanded_by_default = true # Unset: follows [ui] collapsed_edit_blocks in config.toml
|
||||
# (flag on = collapsed one-liner); uncomment to pin either shape
|
||||
hunk_separator = "…" # Separator between hunks ("…", "───", "⋯", or "" for none)
|
||||
dual_line_numbers = false # Two-column line numbers (old + new, like GitHub)
|
||||
# line_summary = false # Show +N/-M in the collapsed header; unset follows the same flag
|
||||
# bg = "none" # Block background ("none", "light", "dark")
|
||||
```
|
||||
|
||||
### Block Styling: Thinking/Reasoning
|
||||
|
||||
```toml
|
||||
[scrollback.blocks.thinking]
|
||||
accent_enabled = true # Show accent line for thinking blocks
|
||||
animate = true # Animate accent line while thinking
|
||||
truncated_lines = 3 # Lines to show in truncated mode
|
||||
bg_blend = 70 # Markdown-color blend with background (0-100)
|
||||
header = true # Show "Thinking..." header
|
||||
header_bright = false # Bright header style (vs dim/muted)
|
||||
```
|
||||
|
||||
### Block Styling: Tool Calls
|
||||
|
||||
```toml
|
||||
[scrollback.blocks.tool]
|
||||
muted_collapsed = true # Gray out collapsed tool calls
|
||||
dim_details = true # Dim parenthetical details (line counts, match counts)
|
||||
bullet = "diamond" # Bullet style before tool headers
|
||||
```
|
||||
|
||||
Available bullet styles:
|
||||
|
||||
| Config Value | Character | Description |
|
||||
|-------------|-----------|-------------|
|
||||
| `none` | (none) | No bullet |
|
||||
| `dot` | `·` | Middle dot (smallest) |
|
||||
| `small-circle` | `•` | Bullet |
|
||||
| `circle` | `●` | Filled circle |
|
||||
| `small-triangle` | `▸` | Right-pointing small triangle |
|
||||
| `triangle` | `▶` | Right-pointing triangle |
|
||||
| `diamond` | `◆` | Filled diamond (default) |
|
||||
|
||||
### Block Styling: Execute (Shell Commands)
|
||||
|
||||
```toml
|
||||
[scrollback.blocks.execute]
|
||||
first_lines = 2 # Output lines shown at start in truncated mode
|
||||
last_lines = 3 # Output lines shown at end in truncated mode
|
||||
accent_enabled = true # Show accent line (animated while running)
|
||||
header_style = "label" # "shell" ($ prefix) or "label" (Run prefix)
|
||||
muted_command_collapsed = true # Mute command text when collapsed
|
||||
```
|
||||
|
||||
### Block Styling: User Prompts (Scrollback)
|
||||
|
||||
```toml
|
||||
[scrollback.blocks.prompt]
|
||||
vpad = true # Vertical padding
|
||||
bg = "light" # Background ("none", "light", "dark")
|
||||
show_prefix = true # Show the prompt prefix character
|
||||
min_lines = 2 # Minimum content lines in truncated/sticky mode
|
||||
```
|
||||
|
||||
### Prompt Input Widget
|
||||
|
||||
```toml
|
||||
[prompt]
|
||||
collapse_unfocused = true # Collapse when scrollback is focused
|
||||
mouse_hover = true # Show hover highlight on mouse over
|
||||
show_prefix = true # Show the prompt prefix character
|
||||
```
|
||||
|
||||
### Todo Badges
|
||||
|
||||
```toml
|
||||
[todo]
|
||||
badge_format = "default" # "default" = 2/5 (done/total), "colon" = [▶:1 □:4 ✓:3 ✗:2], "comma" = [1 ▶, 4 □, 3 ✓, 2 ✗]
|
||||
```
|
||||
|
||||
### Terminal Behavior
|
||||
|
||||
```toml
|
||||
[terminal]
|
||||
alt_screen = "auto" # "auto", "always", or "never"
|
||||
```
|
||||
|
||||
Alt-screen policies:
|
||||
- `auto` -- fullscreen in plain terminals and normal tmux; inline in tmux control mode and Zellij.
|
||||
- `always` -- always enter fullscreen.
|
||||
- `never` -- never enter fullscreen; run inline in the main scrollback.
|
||||
|
||||
### Plugins UI
|
||||
|
||||
```toml
|
||||
disable_plugins = false # Set to true to hide /hooks, /plugins commands and annotations
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Theme Color Slots
|
||||
|
||||
Each theme defines the following color slots that are used throughout the TUI:
|
||||
|
||||
**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:** `text_primary`, `text_secondary`
|
||||
|
||||
**Grays:** `gray_dim`, `gray`, `gray_bright`
|
||||
|
||||
**Semantic:** `command`, `path`, `running`, `warning`, `fuzzy_accent`
|
||||
|
||||
**Borders and 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:** heading colors (`md_heading_h1`-`md_heading_h6`), `md_code`, `md_code_bg`, `md_text`, `md_muted`, `md_task_checked`, `md_task_unchecked`, `link_fg`
|
||||
|
||||
The theme system manages these slots internally and quantizes them automatically for your terminal.
|
||||
349
crates/codegen/xai-grok-pager/docs/user-guide/07-mcp-servers.md
Normal file
349
crates/codegen/xai-grok-pager/docs/user-guide/07-mcp-servers.md
Normal file
|
|
@ -0,0 +1,349 @@
|
|||
# MCP Servers
|
||||
|
||||
MCP (Model Context Protocol) servers extend Grok with external tool integrations. They let Grok interact with any service that implements the MCP standard.
|
||||
|
||||
---
|
||||
|
||||
## What Are MCP Servers?
|
||||
|
||||
An MCP server is a process that exposes tools to Grok over a standardized protocol. When you configure an MCP server, its tools become available to the model alongside Grok's built-in tools. The model can discover and call these tools during a session.
|
||||
|
||||
For example, a GitHub MCP server might expose tools like `create_issue`, `list_pull_requests`, and `search_code`. A database server might expose `query`, `list_tables`, and `describe_schema`.
|
||||
|
||||
See the [MCP specification](https://modelcontextprotocol.io) for protocol details.
|
||||
|
||||
---
|
||||
|
||||
## Configuration
|
||||
|
||||
MCP servers are configured in `~/.grok/config.toml` under `[mcp_servers.<name>]` sections.
|
||||
|
||||
### stdio Transport (Local Process)
|
||||
|
||||
Grok spawns a local process and communicates over stdin/stdout:
|
||||
|
||||
```toml
|
||||
[mcp_servers.my-server]
|
||||
command = "/path/to/server" # Server executable
|
||||
args = ["--flag", "value"] # Command arguments
|
||||
env = { API_KEY = "sk-..." } # Environment variables
|
||||
enabled = true # Enable or disable the server (default: true)
|
||||
startup_timeout_sec = 30 # Server startup timeout, seconds (default: 30)
|
||||
tool_timeout_sec = 6000 # Per-tool-call timeout fallback, seconds (default: 6000)
|
||||
tool_timeouts = { slow_op = 120 } # Per-tool timeout overrides, seconds
|
||||
```
|
||||
|
||||
> **Global startup-timeout override:** instead of setting `startup_timeout_sec`
|
||||
> per server, you can change the default for all servers via the `MCP_TIMEOUT`
|
||||
> environment variable (milliseconds, compatible with Claude Code) or
|
||||
> `GROK_MCP_STARTUP_TIMEOUT_SECS` (seconds). A per-server `startup_timeout_sec`
|
||||
> still takes precedence over both. Cold-start `npx`/`uvx` servers that download
|
||||
> packages on first launch often need this; the default is 30s.
|
||||
>
|
||||
> **MCP tool-result size cap:** large MCP / `use_tool` results are truncated
|
||||
> inline (full payload spilled under the session `mcp/` folder). Default is
|
||||
> **20_000 bytes**. Override via:
|
||||
>
|
||||
> - env `GROK_MAX_MCP_OUTPUT_BYTES` or `MAX_MCP_OUTPUT_BYTES` (bytes; Grok-native
|
||||
> wins if both set; Claude-style name, but we bound by **bytes** not tokens)
|
||||
> - `config.toml` — user-level (`~/.grok/config.toml`) **or repo-level**
|
||||
> (`.grok/config.toml` anywhere on the cwd → git-root chain; the deepest
|
||||
> file wins, and the repo value applies only once the folder is trusted):
|
||||
>
|
||||
> ```toml
|
||||
> [mcp]
|
||||
> max_output_bytes = 40000
|
||||
> ```
|
||||
>
|
||||
> Precedence: requirements.toml > env > repo `.grok/config.toml` >
|
||||
> user/managed config > default. Repo edits apply to running sessions in that
|
||||
> directory via config hot-reload.
|
||||
|
||||
### HTTP/SSE Transport (Remote Server)
|
||||
|
||||
For remote MCP servers accessible over HTTP:
|
||||
|
||||
```toml
|
||||
[mcp_servers.remote-api]
|
||||
url = "https://mcp.example.com/api"
|
||||
headers = { "Authorization" = "Bearer token" }
|
||||
```
|
||||
|
||||
### Streamable HTTP with Session ID
|
||||
|
||||
```toml
|
||||
[mcp_servers.my-streamable-server]
|
||||
url = "https://mcp.example.com/api/mcp"
|
||||
headers = { "x-mcp-session-id" = "{{session_id}}" }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## CLI Management
|
||||
|
||||
Manage MCP servers from the command line without editing config files:
|
||||
|
||||
```bash
|
||||
# List configured MCP servers
|
||||
grok mcp list
|
||||
grok mcp list --json # Machine-readable output
|
||||
|
||||
# Add a stdio server. Everything after -- is the server command, so flags
|
||||
# like -y reach the server instead of being parsed by grok.
|
||||
grok mcp add filesystem -- npx -y @modelcontextprotocol/server-filesystem /path/to/dir
|
||||
|
||||
# Add a stdio server with environment variables (-e is repeatable)
|
||||
grok mcp add postgres -e DATABASE_URL=postgres://localhost/mydb -- npx -y @modelcontextprotocol/server-postgres
|
||||
|
||||
# Add a remote HTTP server
|
||||
grok mcp add --transport http sentry https://mcp.sentry.dev/mcp
|
||||
|
||||
# Add a remote server with an authentication header (--header is repeatable)
|
||||
grok mcp add --transport http api https://mcp.example.com/mcp --header "Authorization: Bearer YOUR_TOKEN"
|
||||
|
||||
# Add a remote SSE server
|
||||
grok mcp add --transport sse linear https://mcp.linear.app/sse
|
||||
|
||||
# Remove a server
|
||||
grok mcp remove github
|
||||
|
||||
# Diagnose a server's configuration and connectivity
|
||||
grok mcp doctor # Check every configured server
|
||||
grok mcp doctor github # Check one server
|
||||
grok mcp doctor --json # Machine-readable output
|
||||
```
|
||||
|
||||
The transport defaults to `stdio`; pass `--transport http` or `--transport sse` for remote servers.
|
||||
|
||||
By default `grok mcp add` writes to `~/.grok/config.toml` (`--scope user`). Use `--scope project` to write to `.grok/config.toml` in the current directory instead, which can be committed and shared with your team (see [Project-Scoped MCP Servers](#project-scoped-mcp-servers)). Header and environment variable values are stored verbatim, so reference secrets as `${VAR}` instead of pasting them into a committed project config (see [Example Configurations](#example-configurations)). `grok mcp list` shows servers from both scopes, marking project-scoped ones with `(project)`.
|
||||
|
||||
`grok mcp remove` searches both scopes and exits 0 after removing the server. It exits 1 when the name is not found, or when the name is defined in both user and project scope — pass `--scope` to say which one to remove.
|
||||
|
||||
Breaking changes from earlier releases: `--env` now takes one `KEY=value` per flag (use `-e A=1 -e B=2`, not `--env A=1 B=2`), and server names may only contain letters, numbers, hyphens, and underscores.
|
||||
|
||||
---
|
||||
|
||||
## Project-Scoped MCP Servers
|
||||
|
||||
MCP servers can be configured per-project by placing a `.grok/config.toml` in your repository:
|
||||
|
||||
```
|
||||
my-project/
|
||||
.grok/
|
||||
config.toml
|
||||
src/
|
||||
...
|
||||
```
|
||||
|
||||
```toml
|
||||
# .grok/config.toml
|
||||
[mcp_servers.linear]
|
||||
url = "https://mcp.linear.app/mcp"
|
||||
enabled = true
|
||||
```
|
||||
|
||||
When a server exposes a native HTTP/SSE endpoint, prefer the `url` form over wrapping it in a stdio proxy such as `npx mcp-remote <url>`. Grok handles HTTP/SSE and OAuth directly, so the native form avoids an extra subprocess per session. It also registers Grok's own OAuth client with the provider.
|
||||
|
||||
Grok walks from the current directory up to the git repo root, loading `.grok/config.toml` at each level:
|
||||
|
||||
| Location | Scope | Priority |
|
||||
|----------|-------|----------|
|
||||
| `~/.grok/config.toml` | All projects | Lowest |
|
||||
| `<repo-root>/.grok/config.toml` | This repository | Medium |
|
||||
| `<cwd>/.grok/config.toml` | Current directory | Highest |
|
||||
|
||||
If a project defines a server with the same name as a global one, the project version replaces it entirely (fields are not merged).
|
||||
|
||||
Project-scoped files contribute `[mcp_servers]`, `[plugins]`, and `[permission]` entries. Grok reads most other config sections only from `~/.grok/config.toml`.
|
||||
|
||||
---
|
||||
|
||||
## Tool Naming
|
||||
|
||||
MCP tools are namespaced with the server name to avoid collisions:
|
||||
|
||||
- Server `filesystem` with tool `read_file` becomes `filesystem__read_file`
|
||||
- Server `github` with tool `create_issue` becomes `github__create_issue`
|
||||
|
||||
---
|
||||
|
||||
## Toggle Servers at Runtime
|
||||
|
||||
You can enable or disable MCP servers during a session without restarting Grok.
|
||||
|
||||
### The /mcps Modal
|
||||
|
||||
Open the MCP servers modal in the TUI:
|
||||
|
||||
- Run `/mcps` as a slash command
|
||||
- Or press `Ctrl+L` (non–VS Code family) and navigate to the MCP Servers tab; on VS Code family use `/plugins` or `/mcp` and open the MCP Servers tab
|
||||
|
||||
From the modal you can:
|
||||
|
||||
- See each server's source, enabled state, and tool count
|
||||
- Enable or disable a server with `Space`
|
||||
- Expand a server to view the tools it provides
|
||||
- Refresh the list with `r` after you edit `config.toml`
|
||||
- Authenticate an OAuth server with `i`
|
||||
- Add a server with `a`, or remove one with `x`
|
||||
|
||||
### Tool Discovery
|
||||
|
||||
The model has access to two built-in tools for working with MCP servers:
|
||||
|
||||
- `search_tool` — Discover available integration tools across all enabled MCP servers. Use this to find tools by name or description.
|
||||
- `use_tool` — Call an integration tool discovered via `search_tool`. Specify the fully-qualified tool name (e.g., `github__create_issue`).
|
||||
|
||||
---
|
||||
|
||||
## Compatibility
|
||||
|
||||
Grok loads MCP server configurations from multiple sources for compatibility:
|
||||
|
||||
| Source | Format | Location | Configurable |
|
||||
|--------|--------|----------|-------------|
|
||||
| `config.toml` | Native Grok config | `~/.grok/config.toml`, `.grok/config.toml` | Always on |
|
||||
| `.claude.json` | Claude Code format | `~/.claude.json` | `[compat.claude] mcps` |
|
||||
| `.cursor/mcp.json` | Cursor format | `~/.cursor/mcp.json`, `<project>/.cursor/mcp.json` | `[compat.cursor] mcps` |
|
||||
| `.mcp.json` | MCP standard format | Project root (cwd to git root) | Loaded unless you have imported or dismissed the Claude import prompt (the import marker is set) |
|
||||
|
||||
All sources are merged in priority order: config.toml > Claude > Cursor > `.mcp.json`. Servers from higher-priority sources take precedence when names conflict.
|
||||
|
||||
The Claude and Cursor MCP sources are scanned by default. To disable scanning for a specific vendor, set `[compat.<vendor>] mcps = false` in `~/.grok/config.toml` or the corresponding environment variable (`GROK_CURSOR_MCPS_ENABLED`, `GROK_CLAUDE_MCPS_ENABLED`). See [Configuration](05-configuration.md#harness-compatibility) for details. Use `grok inspect` to see which MCP servers were loaded and their vendor origin (`[cursor]`, `[claude]`).
|
||||
|
||||
---
|
||||
|
||||
## MCP OAuth
|
||||
|
||||
For MCP servers that require OAuth authentication, Grok handles the credential flow automatically. When an MCP server requests OAuth credentials, Grok opens a browser-based authorization flow and stores the resulting tokens for future use.
|
||||
|
||||
---
|
||||
|
||||
## Example Configurations
|
||||
|
||||
Use the `url` form for hosted MCP servers and the `command` / `args` form for local stdio tools.
|
||||
|
||||
### Native HTTP (hosted services)
|
||||
|
||||
You must authenticate OAuth-based MCP servers before you can use them. Grok stores the resulting tokens under `~/.grok/mcp_credentials.json`. After you edit `config.toml`, press `r` in the `/mcps` modal to refresh the server list.
|
||||
|
||||
```toml
|
||||
[mcp_servers.linear]
|
||||
url = "https://mcp.linear.app/mcp"
|
||||
enabled = true
|
||||
|
||||
[mcp_servers.sentry]
|
||||
url = "https://mcp.sentry.dev/mcp"
|
||||
enabled = true
|
||||
|
||||
[mcp_servers.mixpanel]
|
||||
url = "https://mcp.mixpanel.com/mcp"
|
||||
enabled = true
|
||||
```
|
||||
|
||||
For internal or self-hosted servers that authenticate with a static bearer token rather than OAuth, set the `Authorization` header explicitly:
|
||||
|
||||
```toml
|
||||
[mcp_servers.internal-tools]
|
||||
url = "https://mcp.internal.example.com/mcp"
|
||||
enabled = true
|
||||
|
||||
[mcp_servers.internal-tools.headers]
|
||||
Authorization = "Bearer <token>"
|
||||
```
|
||||
|
||||
To avoid putting secrets in the config file, reference an environment variable with `${VAR}` (or `${VAR:-default}`). Grok expands string fields in `[mcp_servers.*]` — `url`, `command`, `args`, and the values in `env` and `headers` — at load time:
|
||||
|
||||
```toml
|
||||
[mcp_servers.internal-tools]
|
||||
url = "https://mcp.internal.example.com/mcp"
|
||||
enabled = true
|
||||
headers = { "Authorization" = "Bearer ${INTERNAL_MCP_TOKEN}" }
|
||||
```
|
||||
|
||||
### Local stdio
|
||||
|
||||
Use stdio for tools that must run locally (filesystem access, local databases, in-house servers).
|
||||
|
||||
```toml
|
||||
# Filesystem access scoped to a directory
|
||||
[mcp_servers.filesystem]
|
||||
command = "npx"
|
||||
args = ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/allowed/directory"]
|
||||
|
||||
# Local Postgres
|
||||
[mcp_servers.postgres]
|
||||
command = "npx"
|
||||
args = ["-y", "@modelcontextprotocol/server-postgres", "postgresql://user:pass@localhost/db"]
|
||||
|
||||
# Custom server with a longer startup timeout and tuned per-tool timeouts
|
||||
[mcp_servers.my-tools]
|
||||
command = "/usr/local/bin/my-mcp-server"
|
||||
args = ["--config", "/etc/my-mcp.json"]
|
||||
startup_timeout_sec = 30
|
||||
tool_timeout_sec = 120
|
||||
tool_timeouts = { slow_analysis = 300, quick_lookup = 10 }
|
||||
```
|
||||
|
||||
On Windows, npm installs launchers like `npx`, `npm`, `pnpm`, and `yarn` as `.cmd` batch shims (there is no `npx.exe`). Grok resolves a bare `command` such as `npx` to its real launcher path on `PATH` (honoring `PATHEXT`) before spawning, so these work without manually wrapping them in `cmd /c`. A `command` given as an absolute path or one containing a path separator is used as-is.
|
||||
|
||||
---
|
||||
|
||||
## Available MCP Servers
|
||||
|
||||
A partial list of MCP servers you can configure with the `url` or `command` forms shown above. Confirm the current endpoint or package name with each provider before use:
|
||||
|
||||
| Server | Transport | Endpoint / Package |
|
||||
|--------|-----------|--------------------|
|
||||
| Linear | HTTP (OAuth) | `https://mcp.linear.app/mcp` |
|
||||
| Sentry | HTTP (OAuth) | `https://mcp.sentry.dev/mcp` |
|
||||
| Mixpanel | HTTP (OAuth) | `https://mcp.mixpanel.com/mcp` |
|
||||
| Filesystem | stdio | `@modelcontextprotocol/server-filesystem` |
|
||||
| Git | stdio | `@modelcontextprotocol/server-git` |
|
||||
| GitHub | stdio | `@modelcontextprotocol/server-github` |
|
||||
| GitLab | stdio | `@modelcontextprotocol/server-gitlab` |
|
||||
| PostgreSQL | stdio | `@modelcontextprotocol/server-postgres` |
|
||||
| SQLite | stdio | `@modelcontextprotocol/server-sqlite` |
|
||||
| Puppeteer | stdio | `@modelcontextprotocol/server-puppeteer` |
|
||||
|
||||
See the [MCP Server Registry](https://github.com/modelcontextprotocol/servers) for the full list of community servers and the [MCP specification](https://modelcontextprotocol.io) for protocol details.
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Server Not Starting
|
||||
|
||||
```bash
|
||||
# Test the server command manually
|
||||
npx -y @modelcontextprotocol/server-filesystem /path
|
||||
|
||||
# Increase startup timeout
|
||||
# In config.toml:
|
||||
[mcp_servers.filesystem]
|
||||
startup_timeout_sec = 30
|
||||
```
|
||||
|
||||
For stdio servers, Grok captures the process's standard error to `~/.grok/logs/mcp/<server>.stderr.log`, truncated on each launch. Check this file when a server starts but fails to handshake:
|
||||
|
||||
```bash
|
||||
tail -f ~/.grok/logs/mcp/filesystem.stderr.log
|
||||
```
|
||||
|
||||
### Viewing Server Status
|
||||
|
||||
Use `grok inspect` to see all loaded MCP servers and their sources:
|
||||
|
||||
```bash
|
||||
grok inspect # Human-readable
|
||||
grok inspect --json # Machine-readable
|
||||
```
|
||||
|
||||
### Debug Logging
|
||||
|
||||
```bash
|
||||
RUST_LOG=debug GROK_LOG_FILE=/tmp/grok.log grok
|
||||
tail -f /tmp/grok.log
|
||||
```
|
||||
|
||||
Look for log entries containing `mcp` to trace server startup, tool discovery, and tool call execution.
|
||||
222
crates/codegen/xai-grok-pager/docs/user-guide/08-skills.md
Normal file
222
crates/codegen/xai-grok-pager/docs/user-guide/08-skills.md
Normal file
|
|
@ -0,0 +1,222 @@
|
|||
# Skills
|
||||
|
||||
Skills are reusable prompt packages that extend Grok with task-specific instructions. They let you capture a repeatable procedure once, instead of re-explaining it each session.
|
||||
|
||||
---
|
||||
|
||||
## What Are Skills?
|
||||
|
||||
A skill is a directory that contains a `SKILL.md` file. Its markdown body tells Grok how to handle a specific type of task: step-by-step instructions, conventions, and tool-usage patterns.
|
||||
|
||||
Use a skill for a repeatable procedure that's too specific for AGENTS.md but too long to retype. Grok activates a skill only when it applies to your current task.
|
||||
|
||||
---
|
||||
|
||||
## Skill Locations
|
||||
|
||||
Grok discovers skills from these directories, in priority order:
|
||||
|
||||
| Location | Scope | Priority | Notes |
|
||||
|----------|-------|----------|-------|
|
||||
| `./.grok/skills/`, `./.grok/commands/` | Local (CWD) | Highest | Current directory skills / legacy command markdown |
|
||||
| `<repo_root>/.grok/skills/`, `…/commands/` | Repo | Medium | Shared across the repo |
|
||||
| `~/.grok/skills/`, `~/.grok/commands/` | User | Lowest | Personal skills for all projects |
|
||||
| `~/.claude/skills/`, `~/.claude/commands/` | User | Lowest | Claude Code compatibility (configurable) |
|
||||
| `./.claude/skills/`, `./.claude/commands/` | Local / Repo | High | Project Claude skills and legacy custom slash commands |
|
||||
| `~/.cursor/skills/` | User | Lowest | Cursor compatibility (configurable) |
|
||||
| `./.cursor/skills/` | Local / Repo | High | Project Cursor skills (when cursor compat skills are enabled) |
|
||||
|
||||
Grok deduplicates skills by name -- a higher-priority location overrides a lower one. Grok also scans `.agents/skills/` (and `commands/`) at each tier (alongside `.grok/`) and walks every directory between your working directory and the repo root.
|
||||
|
||||
Flat `*.md` files under a `commands/` directory become user-invocable slash commands (filename stem = command name), matching Claude Code's legacy custom-command layout.
|
||||
|
||||
Skill and command discovery does **not** use `.gitignore`. Paths under known skill roots (`.grok/`, `.agents/`, `.claude/`, `.cursor/`) always load when present on disk — teams often ignore `.claude/**` as local-only config while still expecting `/frontend`-style project commands to work. To hide a skill, use `[skills] ignore` in config (not repo ignore rules).
|
||||
|
||||
Grok scans the Claude and Cursor skill directories by default. To stop scanning a vendor, set its `skills` cell to `false` under `[compat.cursor]` or `[compat.claude]` in `~/.grok/config.toml`, or set the `GROK_CURSOR_SKILLS_ENABLED` or `GROK_CLAUDE_SKILLS_ENABLED` environment variable to `false`. See [Configuration](05-configuration.md#harness-compatibility) for details. Grok always filters out known vendor-shipped default skills (such as Cursor's `shell`, `canvas`, and `statusline`), regardless of these settings.
|
||||
|
||||
### Additional Skill Directories
|
||||
|
||||
Add directories, exclude paths, or disable individual skills via `[skills]` in `~/.grok/config.toml`:
|
||||
|
||||
```toml
|
||||
[skills]
|
||||
paths = ["~/my-team-skills"] # Additional directories to scan
|
||||
ignore = ["~/my-team-skills/wip"] # Paths to exclude (hidden entirely)
|
||||
disabled = ["wip-skill"] # Skill names to keep listed but inactive
|
||||
```
|
||||
|
||||
Each entry in `paths` is a `SKILL.md` file or a directory that Grok walks recursively. `ignore` hides a skill completely; `disabled` keeps it in the list but excludes it from the system prompt and from invocation. `paths` and `ignore` take filesystem paths and support `~` expansion; `disabled` takes skill names.
|
||||
|
||||
---
|
||||
|
||||
## Creating a Skill
|
||||
|
||||
### Directory Structure
|
||||
|
||||
Each skill lives in its own directory with a `SKILL.md` file:
|
||||
|
||||
```
|
||||
~/.grok/skills/
|
||||
commit/
|
||||
SKILL.md
|
||||
review-pr/
|
||||
SKILL.md
|
||||
deploy/
|
||||
SKILL.md
|
||||
```
|
||||
|
||||
### SKILL.md Format
|
||||
|
||||
A skill file has YAML frontmatter followed by markdown instructions:
|
||||
|
||||
```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.
|
||||
---
|
||||
|
||||
# Git Commit Skill
|
||||
|
||||
Review staged changes and create a commit with a clear, conventional message.
|
||||
|
||||
## Steps
|
||||
|
||||
1. Run `git diff --staged` to see changes
|
||||
2. Summarize what changed and why
|
||||
3. Create commit message following conventional commits format
|
||||
4. Run `git commit -m "..."` with the message
|
||||
```
|
||||
|
||||
### Core Frontmatter Fields
|
||||
|
||||
| Field | Description |
|
||||
|-------|-------------|
|
||||
| `name` | Skill identifier. Use lowercase letters, digits, and hyphens, up to 64 characters. Grok normalizes spaces and underscores to hyphens. If you omit `name`, Grok uses the skill's directory name. |
|
||||
| `description` | What the skill does and when to use it. Grok reads this to decide whether to invoke the skill. If you omit it, Grok uses the first paragraph of the body. |
|
||||
|
||||
Write a specific `description`. It determines when Grok invokes the skill automatically. Name the trigger phrases and use cases.
|
||||
|
||||
### Optional Frontmatter Fields
|
||||
|
||||
Multi-word frontmatter keys use kebab-case (single-word keys like `model` are written as-is).
|
||||
|
||||
| Field | Description |
|
||||
|-------|-------------|
|
||||
| `when-to-use` | Trigger phrases for automatic invocation, kept separate from `description`. |
|
||||
| `allowed-tools` | Tools the skill uses, as a YAML list or a comma- or space-separated string. |
|
||||
| `argument-hint` | Hint text shown in the slash-command autocomplete (for example, `commit message`). |
|
||||
| `user-invocable` | Whether you can run the skill as a slash command. Defaults to `true`; set `false` to hide it from slash commands. (To stop the model from invoking a skill, set `disable-model-invocation` instead.) |
|
||||
| `disable-model-invocation` | When `true`, only your slash command runs the skill -- the model cannot invoke it automatically. Defaults to `false`. |
|
||||
| `model` | Model override for running the skill. |
|
||||
| `effort` | Reasoning-effort override. |
|
||||
| `license` | License identifier (for example, `Apache-2.0`). |
|
||||
| `compatibility` | Environment requirements (for example, `Requires git, docker, jq`). |
|
||||
| `metadata` | Arbitrary string key-value pairs. Grok promotes `metadata.author` and `metadata.short-description` for display. |
|
||||
|
||||
---
|
||||
|
||||
## Creating Skills with /create-skill
|
||||
|
||||
The `/create-skill` command walks you through building a new skill interactively. Grok asks what you want, drafts the files, and writes them to disk.
|
||||
|
||||
### How It Works
|
||||
|
||||
When you run `/create-skill`, Grok:
|
||||
|
||||
1. **Gathers requirements.** Grok asks for the skill name, the scope to save it under, and a description of the workflow you want to capture. Use a name with lowercase letters, digits, and hyphens (2–64 characters, starting and ending with a letter or digit).
|
||||
|
||||
2. **Drafts the description.** Grok writes a `description` that states what the skill does, the phrases that trigger it, and the slash command name. You approve or edit the draft before continuing.
|
||||
|
||||
3. **Creates the skill directory.** Grok creates the `<scope>/.grok/skills/<name>/` directory, plus `scripts/` or `references/` subdirectories when the skill needs them.
|
||||
|
||||
4. **Writes SKILL.md.** Grok writes the frontmatter (`name` and `description`) and a markdown body of instructions, along with any supporting files.
|
||||
|
||||
5. **Verifies and confirms.** Grok reads the file back, confirms it wrote correctly, and tells you how to run the skill.
|
||||
|
||||
### Choosing a Scope
|
||||
|
||||
Grok asks where to save the skill:
|
||||
|
||||
- **Project** (`<repo_root>/.grok/skills/<name>/`) -- available only in this repository and shareable with teammates through version control. Grok recommends this scope inside a git repository.
|
||||
- **User** (`~/.grok/skills/<name>/`) -- available across all your projects.
|
||||
|
||||
The new skill appears in the slash menu within a few seconds, because Grok reloads skills when files change on disk.
|
||||
|
||||
---
|
||||
|
||||
## Using Skills
|
||||
|
||||
### Run a Skill by Name
|
||||
|
||||
Each skill is a slash command named after the skill. Run one by typing its name:
|
||||
|
||||
```
|
||||
/commit # Runs the "commit" skill
|
||||
/review-pr # Runs the "review-pr" skill
|
||||
```
|
||||
|
||||
Running a skill loads its instructions into the conversation and directs the model to follow them. To pass arguments, type them after the name:
|
||||
|
||||
```
|
||||
/commit fix the build
|
||||
```
|
||||
|
||||
To browse your skills, type `/` to open the slash-command menu. Grok lists every built-in command and skill and filters them as you type. To list skills from the command line instead, run `grok inspect` (see [Viewing Skill Details](#viewing-skill-details)).
|
||||
|
||||
### Qualified Names
|
||||
|
||||
When a skill's name collides with another skill or a built-in command, Grok advertises a qualified name prefixed by the skill's scope -- `local:`, `repo:`, `user:`, or the plugin name. Use the qualified form to choose a specific skill:
|
||||
|
||||
```
|
||||
/local:commit # The "commit" skill from ./.grok/skills/
|
||||
/user:commit # The "commit" skill from ~/.grok/skills/
|
||||
```
|
||||
|
||||
### Automatic Invocation
|
||||
|
||||
Grok can invoke a skill on its own when it recognizes a relevant task. Grok matches your prompt against the skill's `description` and `when-to-use` fields, so write both to describe the triggering situation.
|
||||
|
||||
For example, if a skill's description says "Use when the user wants to commit changes," then saying "commit my changes" can trigger that skill automatically. To require an explicit slash command and prevent automatic invocation, set `disable-model-invocation: true` in the frontmatter.
|
||||
|
||||
---
|
||||
|
||||
## Viewing Skill Details
|
||||
|
||||
Run `grok inspect` to see every skill Grok discovers, along with the rest of your configuration:
|
||||
|
||||
```bash
|
||||
grok inspect # Human-readable summary
|
||||
grok inspect --json # Machine-readable report
|
||||
```
|
||||
|
||||
In the human-readable output, the Skills section lists each skill's name and its source -- `project`, `user`, `bundled`, `config` (a `[skills].paths` entry), `server` (skills synced from the skill store in managed workspaces), or `plugin: <name>`. Grok tags any skill disabled via `[skills].disabled` or from a disabled vendor surface with `[disabled]`.
|
||||
|
||||
The report honors your `[skills]` config the same way a live session does: skills from `paths` are listed, skills under an `ignore` prefix are hidden, and skills named in `disabled` stay listed but tagged `[disabled]`.
|
||||
|
||||
The `--json` report includes the full detail for each skill: its `name`, `description`, `source` (with the path to the SKILL.md file), and `userInvocable` flag.
|
||||
|
||||
---
|
||||
|
||||
## Bundled and Plugin Skills
|
||||
|
||||
Grok ships with built-in skills and extracts them to `~/.grok/skills/` on startup -- among them `/create-skill`, `/help`, and `/check-work`. Bundled skills behave like user skills, and a same-named skill in a higher-priority location (local or repo) overrides the bundled copy; `grok inspect` labels the extracted copies `bundled` so they stay distinguishable from skills you authored yourself. (A plugin skill of the same name does not override it; it stays available under its qualified `plugin:name` form.)
|
||||
|
||||
Skills can also come from plugins. When you install a plugin that includes skills, they appear alongside your user and project skills. `grok inspect` labels each plugin-provided skill with its source as `plugin: <name>`.
|
||||
|
||||
See the [Plugins guide](09-plugins.md) for more on installing plugins that provide skills.
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Write specific descriptions.** The description drives automatic invocation. "Create git commits" is too vague; "Create well-formatted git commits following conventional commit standards. Use when the user wants to commit changes or asks for /commit." works better.
|
||||
|
||||
2. **Include concrete steps.** Skills work best when they give Grok a clear, ordered procedure to follow.
|
||||
|
||||
3. **Reference tools by name.** When a skill relies on specific tools (such as `run_terminal_command` or `search_replace`), name them so the model knows what to use.
|
||||
|
||||
4. **Keep skills focused.** Write one skill per workflow. A "deploy" skill and a "rollback" skill work better than a single "deploy-and-rollback" skill.
|
||||
|
||||
5. **Version-control project skills.** Commit `.grok/skills/` to your repository so the whole team benefits. User skills in `~/.grok/skills/` stay personal and unshared.
|
||||
|
||||
6. **Test by running it.** Invoke `/name` and confirm the skill works before you rely on automatic invocation.
|
||||
273
crates/codegen/xai-grok-pager/docs/user-guide/09-plugins.md
Normal file
273
crates/codegen/xai-grok-pager/docs/user-guide/09-plugins.md
Normal file
|
|
@ -0,0 +1,273 @@
|
|||
# Plugins
|
||||
|
||||
A plugin bundles skills, slash commands, agents, hooks, MCP server configurations, and LSP server configurations into one installable unit.
|
||||
|
||||
---
|
||||
|
||||
## What a plugin contains
|
||||
|
||||
A plugin is a directory that holds any combination of these components:
|
||||
|
||||
- **Skills** -- a `skills/` directory of SKILL.md files
|
||||
- **Slash commands** -- a `commands/` directory of command files
|
||||
- **Agents** -- an `agents/` directory of agent definitions
|
||||
- **Hooks** -- a `hooks/hooks.json` file of lifecycle hooks. Plugin hooks also receive `GROK_PLUGIN_ROOT` and `GROK_PLUGIN_DATA` (see the [Hooks guide](10-hooks.md) for every environment variable passed to hooks).
|
||||
- **MCP servers** -- a `.mcp.json` file of server configurations
|
||||
- **LSP servers** -- a `.lsp.json` file of language server configurations
|
||||
|
||||
If a plugin includes a `plugin.json` manifest, the manifest can override paths or add metadata; otherwise components load from the convention directories. The manifest is optional: without one, Grok discovers the components above from their standard directories.
|
||||
|
||||
For example, a `team-tools` plugin might include a deploy skill, a code-review agent, pre-commit hooks, and a Linear MCP server. Install them together in one step.
|
||||
|
||||
## Environment variables in plugin hooks
|
||||
|
||||
Plugin hooks receive two environment variables beyond the standard ones set for every hook:
|
||||
|
||||
| Variable | Description |
|
||||
|----------------------|-------------|
|
||||
| `GROK_PLUGIN_ROOT` | Absolute path to the plugin's installed directory. |
|
||||
| `GROK_PLUGIN_DATA` | Absolute path to the plugin's writable data directory, for plugin state, caches, and logs. |
|
||||
|
||||
Grok sets these values and overrides any value you declare for the same key in the hook JSON's `env` map. (Grok also sets the `CLAUDE_PLUGIN_ROOT` and `CLAUDE_PLUGIN_DATA` aliases for compatibility.) See the [Hooks guide](10-hooks.md) for every environment variable passed to hooks.
|
||||
|
||||
---
|
||||
|
||||
## Plugin locations
|
||||
|
||||
Grok discovers plugins from these locations, in priority order:
|
||||
|
||||
| Location | Scope | Trust |
|
||||
|----------|-------|-------|
|
||||
| `_meta.pluginDirs` (`session/new` / `session/load`) | Session -- loaded for that session only | Trusted automatically |
|
||||
| `--plugin-dir` (CLI flag, `grok agent`) | Process -- loaded for that agent process only | Trusted automatically |
|
||||
| `.grok/plugins/` | Project -- shared with the team through version control | Requires trust |
|
||||
| `~/.grok/plugins/` | User -- personal plugins for every project | Trusted automatically |
|
||||
| `[plugins].paths` (config) | Custom directories you add in `config.toml` | Depends on location |
|
||||
|
||||
Grok also reads the `.claude/plugins/` equivalents for compatibility. When two plugins share a name, the higher-priority location wins.
|
||||
|
||||
The Agent SDKs load per-session plugins through `GrokOptions.plugins`, which arrives as `_meta.pluginDirs` on `session/new` and `session/load`; because the caller controls the directory, these plugins are always trusted -- their hooks and MCP servers activate without a prompt, and they never persist beyond the session. The `--plugin-dir` flag is the process-wide equivalent for direct CLI use (repeatable: `grok agent --no-leader --plugin-dir A --plugin-dir B stdio`); it applies to dedicated agent processes only and is ignored in leader mode (the shared leader discovers its own plugins).
|
||||
|
||||
---
|
||||
|
||||
## Manage plugins in the TUI
|
||||
|
||||
### Open the modal
|
||||
|
||||
| Action | Opens |
|
||||
|--------|-------|
|
||||
| `Ctrl+L` (from any pane; **non–VS Code family**) | Plugins tab |
|
||||
| `/plugins` (any terminal; **required on VS Code family**) | Plugins tab |
|
||||
|
||||
The modal has five tabs: **Hooks**, **Plugins**, **Marketplace**, **Skills**, and **MCP Servers**. Switch tabs with `Tab` (forward) or `Shift+Tab` (backward). The `/hooks`, `/marketplace`, `/skills`, and `/mcps` commands each open the modal on the matching tab.
|
||||
|
||||
### Plugins tab
|
||||
|
||||
Press `Enter` to expand a plugin row and show its details:
|
||||
|
||||
- **Name** and **version**
|
||||
- **Scope** -- `cli`, `project`, `user`, `custom path`, or the marketplace source name
|
||||
- **Skills** -- names or count
|
||||
- **Agents** -- names or count
|
||||
- **Hooks** -- count
|
||||
- **MCP servers** -- count (or `blocked` when the plugin is not trusted)
|
||||
- **Description** and **path**
|
||||
|
||||
Use these keys in the Plugins tab:
|
||||
|
||||
| Key | Action |
|
||||
|-----|--------|
|
||||
| `r` | Reload all plugins |
|
||||
| `a` | Add a plugin from `owner/repo`, a URL, or a local path |
|
||||
| `Space` | Enable or disable the selected plugin |
|
||||
| `x` | Uninstall the selected plugin |
|
||||
| `f` | Filter by status (all, enabled, or disabled) |
|
||||
| `Enter` | Expand or collapse plugin details |
|
||||
| `/` | Search plugins by name |
|
||||
|
||||
### Marketplace tab
|
||||
|
||||
Browse and install plugins from your configured marketplace sources.
|
||||
|
||||
Use these keys in the Marketplace tab:
|
||||
|
||||
| Key | Action |
|
||||
|-----|--------|
|
||||
| `i` | Install the selected plugin |
|
||||
| `d` | Uninstall the selected plugin |
|
||||
| `a` | Add a marketplace source |
|
||||
| `x` | Remove the selected source and its plugins |
|
||||
| `r` | Refresh marketplace sources |
|
||||
| `u` | Update the selected marketplace plugin |
|
||||
| `Enter` | Expand or collapse a source or plugin |
|
||||
| `/` | Search plugins by name |
|
||||
|
||||
Component summaries on list rows and per-category component details in the
|
||||
expanded view appear only for marketplaces that publish a `plugin-index.json`
|
||||
catalog.
|
||||
|
||||
---
|
||||
|
||||
## CLI commands
|
||||
|
||||
Manage plugins without starting an interactive session.
|
||||
|
||||
### Plugin commands
|
||||
|
||||
```bash
|
||||
grok plugin list [--json] [--available] # List installed plugins (--available requires --json)
|
||||
grok plugin install <source> --trust # Git URL, GitHub shorthand (user/repo), or local path
|
||||
grok plugin uninstall <name> [--confirm] [--keep-data] # Aliases: rm, remove
|
||||
grok plugin update [<name>] # Omit the name to update all plugins
|
||||
grok plugin enable <name>
|
||||
grok plugin disable <name>
|
||||
grok plugin details <name> # Show the plugin's component inventory
|
||||
grok plugin validate [<path>] # Validate plugin.json (default: current directory)
|
||||
grok plugin tag [<path>] [--push] [--force] [--dry-run] # Tag a release from the manifest version
|
||||
```
|
||||
|
||||
Run `grok plugin install <source>` without `--trust` and Grok prints the source and warns that installing will activate the plugin's hooks, MCP servers, and skills, then stops without installing. Add `--trust` to install it.
|
||||
|
||||
The `<source>` argument accepts:
|
||||
|
||||
- `user/repo` -- GitHub shorthand
|
||||
- `user/repo@v1.0` -- pinned to a ref
|
||||
- `user/repo#subdir` -- subdirectory within the repo
|
||||
- `https://github.com/user/repo.git` -- full URL
|
||||
- `git@github.com:user/repo.git` -- SSH
|
||||
- `./local-dir` or `/absolute/path` -- local directory
|
||||
|
||||
### Marketplace commands
|
||||
|
||||
```bash
|
||||
grok plugin marketplace list [--json]
|
||||
grok plugin marketplace add <url> # Git URL, GitHub shorthand (user/repo), or local path
|
||||
grok plugin marketplace remove <url> # Git URL or local path of a configured source
|
||||
grok plugin marketplace update [<name>] # Omit the name to refresh all sources
|
||||
```
|
||||
|
||||
### Example: set up a team marketplace
|
||||
|
||||
```bash
|
||||
grok plugin marketplace add my-org/team-plugins
|
||||
grok plugin marketplace list
|
||||
grok plugin install my-org/team-plugins --trust
|
||||
grok plugin list
|
||||
grok plugin update
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Slash commands
|
||||
|
||||
In an interactive session, these commands open the modal on a specific tab. They take no arguments — manage plugins from the modal or with the `grok plugin` CLI.
|
||||
|
||||
| Command | Opens |
|
||||
|---------|-------|
|
||||
| `/plugins` | Plugins tab |
|
||||
| `/hooks` | Hooks tab |
|
||||
| `/marketplace` | Marketplace tab |
|
||||
| `/skills` | Skills tab |
|
||||
| `/mcps` | MCP Servers tab |
|
||||
|
||||
---
|
||||
|
||||
## Configuration
|
||||
|
||||
Configure plugin directories and per-plugin state in `~/.grok/config.toml`:
|
||||
|
||||
```toml
|
||||
[plugins]
|
||||
paths = ["~/my-plugins/custom-tools"] # Additional plugin directories
|
||||
disabled = ["user/a1b2c3d4/noisy-plugin"] # Plugin IDs or names to skip
|
||||
enabled = ["project/9f8e7d6c/team-tools"] # Plugin IDs or names to force on
|
||||
```
|
||||
|
||||
List a plugin in `disabled` to discover it but skip loading its components. List a plugin in `enabled` to activate it — plugins are disabled by default unless a CLI override or an explicit config path enables them, so add them here to turn them on. Each entry is either a plain plugin name (as shown by `grok plugin list`) or a full plugin ID in the form `<scope>/<hash>/<name>`.
|
||||
|
||||
### Hide the plugins UI
|
||||
|
||||
To hide the hooks and plugins UI — the `/hooks` and `/plugins` commands and the scrollback annotations — set this in `~/.grok/pager.toml`:
|
||||
|
||||
```toml
|
||||
disable_plugins = true
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Marketplace sources
|
||||
|
||||
Add git or local marketplace sources to discover and install plugins.
|
||||
|
||||
### In config.toml
|
||||
|
||||
Each source needs a `name` and either a `git` URL (with an optional `branch`) or a local `path`:
|
||||
|
||||
```toml
|
||||
[[marketplace.sources]]
|
||||
name = "My Team Plugins"
|
||||
git = "https://github.com/my-org/plugins.git"
|
||||
|
||||
[[marketplace.sources]]
|
||||
name = "Local Dev"
|
||||
path = "~/dev/my-plugins"
|
||||
```
|
||||
|
||||
### In settings.json
|
||||
|
||||
Add sources under `extraKnownMarketplaces`, keyed by name. Each entry's `source` is one of `git` (with `url`), `github` (with `repo`), or `local` (with `path`):
|
||||
|
||||
```json
|
||||
{
|
||||
"extraKnownMarketplaces": {
|
||||
"my-marketplace": {
|
||||
"source": { "source": "git", "url": "git@github.com:my-org/plugins.git" }
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Place this file at `~/.grok/settings.json` or `~/.claude/settings.json`.
|
||||
|
||||
---
|
||||
|
||||
## Trust model
|
||||
|
||||
Enabling a plugin loads its skills, slash commands, and agents. Trust is separate and controls whether a plugin's code runs: even for an enabled plugin, its hooks, MCP servers, and LSP servers stay inactive until you trust it. This prevents an untrusted repository from running code on your machine.
|
||||
|
||||
Grok trusts plugins from `~/.grok/plugins/` automatically. Project plugins in `.grok/plugins/` require explicit trust. To trust a plugin, install it with `--trust`:
|
||||
|
||||
```bash
|
||||
grok plugin install <source> --trust
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Inspect plugins
|
||||
|
||||
Run `grok inspect` to see every discovered plugin and what it provides:
|
||||
|
||||
```bash
|
||||
grok inspect # Show plugins with their skills, agents, hooks, and MCP servers
|
||||
grok inspect --json # Emit machine-readable JSON
|
||||
```
|
||||
|
||||
Plugin-provided components appear in their sections (Skills, Agents, MCP Servers, and so on) with a `plugin: <name>` label, so you can see where each component originates.
|
||||
|
||||
---
|
||||
|
||||
## General keyboard shortcuts
|
||||
|
||||
These keys work across every tab in the modal:
|
||||
|
||||
| Key | Action |
|
||||
|-----|--------|
|
||||
| `Tab` | Next tab |
|
||||
| `Shift+Tab` | Previous tab |
|
||||
| `j` / down-arrow | Move selection down |
|
||||
| `k` / up-arrow | Move selection up |
|
||||
| `Enter` | Expand or collapse the selected item |
|
||||
| `/` | Search the current tab by name |
|
||||
| `Esc` | Clear the search, or close the modal |
|
||||
|
||||
Some actions, such as uninstalling a plugin, ask for confirmation. Press `y` to confirm or `Esc` to cancel.
|
||||
381
crates/codegen/xai-grok-pager/docs/user-guide/10-hooks.md
Normal file
381
crates/codegen/xai-grok-pager/docs/user-guide/10-hooks.md
Normal file
|
|
@ -0,0 +1,381 @@
|
|||
# Hooks
|
||||
|
||||
Hooks let you run a script or send an HTTP request at key moments in a Grok session. Use them to automate tasks, enforce safety checks, log activity, send notifications, and integrate your own tools.
|
||||
|
||||
---
|
||||
|
||||
## What Are Hooks?
|
||||
|
||||
A hook is a shell command or HTTP endpoint that Grok calls when a specific lifecycle event occurs. Hooks can:
|
||||
|
||||
- **Block actions** -- A `PreToolUse` hook can deny a dangerous command before it runs.
|
||||
- **React to events** -- A `PostToolUse` hook can log every tool execution to a file.
|
||||
- **Set up context** -- A `SessionStart` hook can export environment variables or run setup scripts.
|
||||
|
||||
---
|
||||
|
||||
## Common Use Cases
|
||||
|
||||
- **Safety guards**: Block commands such as `rm -rf /` before they run.
|
||||
- **Audit logging**: Record tool use and sessions to a file or external service.
|
||||
- **Notifications**: Send a message when a task finishes.
|
||||
- **Auto-formatting**: Run `cargo fmt` or `prettier` after edits.
|
||||
- **Environment setup**: Export variables at session start.
|
||||
- **Custom workflows**: Trigger builds, tests, or deployments on specific events.
|
||||
|
||||
---
|
||||
|
||||
## Quick Start
|
||||
|
||||
1. Create the hooks directory:
|
||||
|
||||
```sh
|
||||
mkdir -p ~/.grok/hooks
|
||||
```
|
||||
|
||||
2. Create a hook file, e.g. `~/.grok/hooks/session-start.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"hooks": {
|
||||
"SessionStart": [
|
||||
{
|
||||
"hooks": [
|
||||
{ "type": "command", "command": "echo 'Grok session started in '$(pwd)" }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
3. Start (or restart) a Grok session. The hook runs automatically on `SessionStart`.
|
||||
|
||||
4. Press `Ctrl+L` on non–VS Code family terminals (or run `/hooks` anywhere — preferred on VS Code family) and check the Hooks tab to confirm it loaded.
|
||||
|
||||
---
|
||||
|
||||
## Hook Locations
|
||||
|
||||
Hooks are discovered from several places (all are merged):
|
||||
|
||||
| Scope | Path | Trusted? | Notes |
|
||||
|-------|------|----------|-------|
|
||||
| Global | `~/.grok/hooks/*.json` | Always | Personal hooks |
|
||||
| Global | `~/.claude/settings.json` (and `settings.local.json`) | Always | Claude Code compatibility (configurable) |
|
||||
| Global | `~/.cursor/hooks.json` | Always | Cursor compatibility (configurable) |
|
||||
| Project | `<project>/.grok/hooks/*.json` | Requires trust | Per-repo automation |
|
||||
| Project | `<project>/.claude/settings.json` (and `settings.local.json`) | Requires trust | Claude compatibility (configurable) |
|
||||
| Project | `<project>/.cursor/hooks.json` | Requires trust | Cursor compatibility (configurable) |
|
||||
| Plugin | Bundled inside installed plugins | Per-plugin | Shared team hooks |
|
||||
|
||||
The Claude and Cursor hook sources are scanned by default. To disable scanning for a specific vendor, set `[compat.<vendor>] hooks = false` in `~/.grok/config.toml` or the corresponding environment variable. See [Configuration](05-configuration.md#harness-compatibility) for details.
|
||||
|
||||
**Trusting a project**: The first time you open a project with hooks, you must trust it before its project hooks will run -- until then they are silently skipped. Grant trust by running `/hooks-trust` (or launching with `--trust`); the decision is recorded in the unified folder-trust store (`~/.grok/trusted_folders.toml`), the same gate that governs repo-local MCP/LSP servers. Global hooks in `~/.grok/hooks/` are always trusted and need no entry. This prevents untrusted repos from running arbitrary code.
|
||||
|
||||
Because hooks are unified under folder-trust, a `--trust` / `/hooks-trust` grant trusts the whole folder for **MCP, LSP, and hooks** together, and cascades to subdirectories. Conversely, disabling folder-trust (`GROK_FOLDER_TRUST=0` or `[folder_trust] enabled = false`) ungates project hooks along with MCP/LSP.
|
||||
|
||||
---
|
||||
|
||||
## Hook Events
|
||||
|
||||
| Event | When it fires | Blocking? |
|
||||
|-------|---------------|-----------|
|
||||
| `SessionStart` | A session starts. | No |
|
||||
| `UserPromptSubmit` | You submit a prompt. | No |
|
||||
| `PreToolUse` | A tool is about to run. | Yes — can deny |
|
||||
| `PostToolUse` | A tool completes successfully. | No |
|
||||
| `PostToolUseFailure` | A tool fails. | No |
|
||||
| `PermissionDenied` | The permission system denies a tool call. | No |
|
||||
| `Stop` | An agent turn ends (completed, cancelled, or error). | No |
|
||||
| `StopFailure` | A turn ends because of an API error. | No |
|
||||
| `Notification` | The agent sends a notification. | No |
|
||||
| `SubagentStart` | A subagent starts. | No |
|
||||
| `SubagentStop` | A subagent finishes. | No |
|
||||
| `PreCompact` | Conversation compaction is about to run. | No |
|
||||
| `PostCompact` | Conversation compaction completes. | No |
|
||||
| `SessionEnd` | The session ends. | No |
|
||||
|
||||
`SubagentEnd` is accepted as an alias for `SubagentStop`. Only `PreToolUse` can block a tool call; every other event is passive.
|
||||
|
||||
### Cursor Hook Compatibility
|
||||
|
||||
Grok accepts Cursor's camelCase hook event names, so `~/.cursor/hooks.json` loads unchanged:
|
||||
|
||||
| Cursor event | Maps to |
|
||||
|---|---|
|
||||
| `sessionStart`, `sessionEnd` | `SessionStart`, `SessionEnd` |
|
||||
| `preToolUse`, `postToolUse`, `postToolUseFailure` | `PreToolUse`, `PostToolUse`, `PostToolUseFailure` |
|
||||
| `beforeShellExecution`, `beforeMCPExecution`, `beforeReadFile` | `PreToolUse` |
|
||||
| `afterShellExecution`, `afterMCPExecution`, `afterFileEdit` | `PostToolUse` |
|
||||
| `afterAgentResponse`, `afterAgentThought` | `PostToolUse` |
|
||||
| `beforeSubmitPrompt` | `UserPromptSubmit` |
|
||||
| `subagentStart`, `subagentStop` | `SubagentStart`, `SubagentStop` |
|
||||
| `preCompact`, `stop` | `PreCompact`, `Stop` |
|
||||
|
||||
Cursor's per-operation hooks (`beforeShellExecution`, `afterFileEdit`, etc.) map to the generic `PreToolUse`/`PostToolUse` events. The hook script receives the tool name in the JSON input and can filter accordingly, or use the `matcher` field.
|
||||
|
||||
---
|
||||
|
||||
## The Hook JSON Format
|
||||
|
||||
Each `.json` file can define hooks for multiple events:
|
||||
|
||||
```json
|
||||
{
|
||||
"hooks": {
|
||||
"PreToolUse": [
|
||||
{
|
||||
"matcher": "Bash",
|
||||
"hooks": [
|
||||
{ "type": "command", "command": "bin/safety-check.sh", "timeout": 10 }
|
||||
]
|
||||
}
|
||||
],
|
||||
"PostToolUse": [
|
||||
{
|
||||
"hooks": [
|
||||
{ "type": "command", "command": "bin/log-activity.sh" }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Key Fields
|
||||
|
||||
- **Event name** (top-level key): any event listed in [Hook Events](#hook-events). Grok skips unrecognized event names so a shared Claude or Cursor settings file still loads.
|
||||
- **matcher** (optional): A regular expression that selects which invocations trigger the hook. It applies to the tool events — `PreToolUse`, `PostToolUse`, `PostToolUseFailure`, and `PermissionDenied` — where it tests the tool name, and to `Notification`, where it tests the notification type. The lifecycle events (`SessionStart`, `SessionEnd`, `Stop`, `UserPromptSubmit`) reject a matcher; other events ignore it. An empty or omitted matcher matches everything. The matcher tests the real tool name; MCP calls routed through the internal `use_tool` dispatcher appear as the qualified `server__tool` name (e.g. `linear__save_issue`), so match on that, not the dispatcher name.
|
||||
- **type**: `"command"` (run a script or shell one-liner) or `"http"` (POST the event to a URL).
|
||||
- **command**: Path to executable (relative to the JSON file) or inline shell command.
|
||||
- **timeout**: Seconds before killing the hook (default: 5). All hook failures (timeouts, crashes, malformed output, missing required env vars) are fail-open: the failure is recorded for the UI scrollback but the tool call is not blocked. Only an explicit `deny` decision returned by the hook blocks a tool call.
|
||||
|
||||
### Tool Name Aliases
|
||||
|
||||
In a `matcher`, Grok maps Claude-style tool names to its own so hooks migrated from Claude fire correctly. Common aliases include:
|
||||
|
||||
- `Bash` → `run_terminal_command`
|
||||
- `Read` → `read_file`
|
||||
- `Edit`, `Write`, and `MultiEdit` → `search_replace`
|
||||
- `Grep` → `grep`
|
||||
- `Glob` and `ListDir` → `list_dir`
|
||||
- `WebSearch` → `web_search`
|
||||
- `Task` → `spawn_subagent`
|
||||
|
||||
A matcher keeps its original name too, so `Bash` matches both `Bash` and `run_terminal_command`.
|
||||
|
||||
---
|
||||
|
||||
## Writing Hook Scripts
|
||||
|
||||
### Input
|
||||
|
||||
The event is sent as JSON on **stdin** (for example, a `PreToolUse` event; the payload also always includes `toolUseId` and `toolInputTruncated`):
|
||||
|
||||
```json
|
||||
{
|
||||
"hookEventName": "pre_tool_use",
|
||||
"sessionId": "abc-123",
|
||||
"cwd": "/Users/you/project",
|
||||
"workspaceRoot": "/Users/you/project",
|
||||
"toolName": "run_terminal_command",
|
||||
"toolInput": { "command": "npm test" },
|
||||
"timestamp": "2026-04-14T12:00:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
### Output (Blocking Hooks)
|
||||
|
||||
For `PreToolUse` hooks, write JSON to **stdout**:
|
||||
|
||||
- **Allow**: `{"decision": "allow"}`
|
||||
- **Deny**: `{"decision": "deny", "reason": "Unsafe command detected"}`
|
||||
|
||||
### Exit Codes
|
||||
|
||||
| Exit Code | Meaning |
|
||||
|-----------|---------|
|
||||
| `0` | Success / allow (for blocking hooks) |
|
||||
| `2` | Explicit deny (blocking hooks only) |
|
||||
| Other | Fail-open — the failure is recorded but the tool call is not blocked. To block a call, emit a `deny` decision in stdout JSON (honored regardless of exit code). |
|
||||
|
||||
### Passive Hooks
|
||||
|
||||
For events like `SessionStart` or `PostToolUse`, stdout is ignored. Just exit 0 on success.
|
||||
|
||||
### Environment Variables
|
||||
|
||||
Grok sets several environment variables on every hook process. These are useful when writing context-aware or plugin-aware hook scripts.
|
||||
|
||||
#### Runner-injected variables (always available)
|
||||
|
||||
These variables are set by the hook runner for **every** hook:
|
||||
|
||||
| Variable | Description |
|
||||
|-----------------------|-------------|
|
||||
| `GROK_HOOK_EVENT` | The name of the event that triggered the hook (e.g. `pre_tool_use`, `session_start`, `post_tool_use`, `session_end`, `stop`, `notification`). |
|
||||
| `GROK_HOOK_NAME` | The configured name of this specific hook (includes the plugin prefix for plugin-provided hooks). |
|
||||
| `GROK_SESSION_ID` | The unique identifier of the current Grok session. |
|
||||
| `GROK_WORKSPACE_ROOT` | Absolute path to the root of the current workspace. |
|
||||
| `CLAUDE_PROJECT_DIR` | Absolute path to the workspace root. A Claude Code-compatible alias for `GROK_WORKSPACE_ROOT`, set for every hook. |
|
||||
|
||||
These variables are **reserved**. Any values you attempt to set for them via the `env` field in your hook JSON are stripped at load time (a warning is logged), and the runner always injects the real values at spawn time.
|
||||
|
||||
#### Plugin hook variables
|
||||
|
||||
When a hook originates from a plugin, Grok additionally injects the following variables:
|
||||
|
||||
| Variable | Description |
|
||||
|----------------------|-------------|
|
||||
| `GROK_PLUGIN_ROOT` | Absolute path to the plugin's installed directory. |
|
||||
| `GROK_PLUGIN_DATA` | Absolute path to the plugin's writable data directory (for storing plugin state, caches, etc.). |
|
||||
|
||||
These values are provided by the plugin system. For the four plugin-related keys (`GROK_PLUGIN_ROOT`, `GROK_PLUGIN_DATA`, and their Claude aliases), the plugin adapter ensures the official plugin values always win over any user-declared values in the hook's `env` map.
|
||||
|
||||
#### User-defined environment variables
|
||||
|
||||
You can supply additional environment variables for an individual hook handler using the `env` field:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "command",
|
||||
"command": "bin/my-hook.sh",
|
||||
"env": {
|
||||
"MY_SECRET": "value",
|
||||
"LOG_LEVEL": "debug"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
These variables are passed through to the hook process, but they cannot override the reserved runner or plugin variables listed above.
|
||||
|
||||
#### Using variables in `command` and `url` fields
|
||||
|
||||
Both `command` and `url` support `${VAR}` and `$VAR` expansion. See the custom-hooks reference for full details on load-time vs runtime expansion, the `env` map lookup order, and how parameter-expansion modifiers (e.g. `${VAR:-default}`) are handled.
|
||||
|
||||
---
|
||||
|
||||
## HTTP Hooks
|
||||
|
||||
Instead of a local script, call a remote endpoint:
|
||||
|
||||
```json
|
||||
{ "type": "http", "url": "https://hooks.example.com/grok-event", "timeout": 15 }
|
||||
```
|
||||
|
||||
The full event envelope is POSTed as JSON.
|
||||
|
||||
---
|
||||
|
||||
## Managing Hooks in the TUI
|
||||
|
||||
### The Hooks Tab
|
||||
|
||||
Press `Ctrl+L` on non–VS Code family terminals to open the Extensions modal (Plugins tab), or run `/hooks` (any terminal; required on VS Code family where `Ctrl+L` is interject) to open it on the Hooks tab. In the **Hooks** tab:
|
||||
|
||||
| Key | Action |
|
||||
|-----|--------|
|
||||
| `r` | Reload all hooks from disk |
|
||||
| `a` | Add a custom hook by path |
|
||||
| `x` | Remove the selected hook |
|
||||
| `Space` | Enable or disable the selected hook |
|
||||
| `f` | Cycle the status filter (All / Enabled / Disabled) |
|
||||
|
||||
Hooks are grouped by source: **Global**, **Project**, **Plugin**, and **Custom**.
|
||||
|
||||
Each hook shows:
|
||||
- **Event** it triggers on
|
||||
- **Command** or **URL** that runs
|
||||
- **Timeout** duration
|
||||
- **Status** -- enabled or `[disabled]`
|
||||
|
||||
### Slash Commands
|
||||
|
||||
```
|
||||
/hooks-list # Show hooks loaded in this session
|
||||
/hooks-trust # Trust this project for hook execution
|
||||
/hooks-add <path> # Add a custom hook file or directory
|
||||
/hooks-remove <path> # Remove a custom hook
|
||||
/hooks-untrust # Revoke trust for this project
|
||||
```
|
||||
|
||||
In the TUI pager, the individual `/hooks-*` commands do not appear in the slash-command list. The `/hooks` modal covers listing, adding, removing, and enabling or disabling hooks; project trust is managed via `/hooks-trust` (or the modal's Trust action), which writes the unified folder-trust store described above.
|
||||
|
||||
### Per-Hook Enable/Disable
|
||||
|
||||
Enable or disable an individual hook at runtime by pressing `Space` in the Hooks tab. The change takes effect immediately, without restarting the session.
|
||||
|
||||
### Mid-Session Reload
|
||||
|
||||
Press `r` in the Hooks tab to reload all hooks from disk. Grok re-reads every hook source, so this picks up changes you made to hook files during the session.
|
||||
|
||||
---
|
||||
|
||||
## Hook Annotations in Scrollback
|
||||
|
||||
When hooks execute, their results appear as annotations in the TUI scrollback. You can see which hooks ran, whether they allowed or denied an action, and any output they produced. These annotations appear only when the plugins UI is enabled (the default).
|
||||
|
||||
---
|
||||
|
||||
## Example: Safe Shell Guard
|
||||
|
||||
Block dangerous shell commands:
|
||||
|
||||
```json
|
||||
{
|
||||
"hooks": {
|
||||
"PreToolUse": [
|
||||
{
|
||||
"matcher": "Bash",
|
||||
"hooks": [
|
||||
{ "type": "command", "command": "bin/safe-shell.sh", "timeout": 5 }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Where `bin/safe-shell.sh`:
|
||||
|
||||
```bash
|
||||
#!/bin/sh
|
||||
INPUT=$(cat)
|
||||
CMD=$(echo "$INPUT" | jq -r '.toolInput.command // empty')
|
||||
|
||||
# Block destructive patterns
|
||||
if echo "$CMD" | grep -qE '(rm -rf /|mkfs|dd if=|:(){ :|& };:)'; then
|
||||
echo '{"decision": "deny", "reason": "Blocked potentially destructive command"}'
|
||||
exit 2
|
||||
fi
|
||||
|
||||
echo '{"decision": "allow"}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Security Notes
|
||||
|
||||
- Global hooks (`~/.grok/hooks/`) run with your user permissions -- treat them like shell scripts.
|
||||
- Project hooks require folder trust (`/hooks-trust` or `--trust`, the same gate as repo-local MCP/LSP) to prevent supply-chain attacks from malicious repos.
|
||||
- HTTP hooks send session data -- only use trusted endpoints.
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Keep hooks fast** -- long-running hooks block the UI. Use background processes (`&`) or async where possible.
|
||||
2. **Use explicit `deny` to block** -- hooks fail-open on any error, so a hook that crashes will not block the tool. To enforce policy, your hook must run to completion and emit `{"decision":"deny","reason":"..."}` on stdout. Always handle errors inside your script so it can return an explicit decision.
|
||||
3. **Use absolute paths or relative to hook file** -- scripts in `bin/` next to the JSON file are portable.
|
||||
4. **Test with the modal** -- press `Ctrl+L` (non–VS Code family) or run `/hooks` to verify hooks are loaded and matching before relying on them.
|
||||
5. **Version control project hooks** -- commit `.grok/hooks/` (but never secrets).
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
- **Hook not running?** Press `Ctrl+L` on non–VS Code family (or run `/hooks` anywhere) to see if it is loaded and matched.
|
||||
- **Project hooks ignored?** The folder may be untrusted. Run `/hooks-trust` (or relaunch with `--trust`).
|
||||
- **Script not found?** Check the path is relative to the `.json` file and executable (`chmod +x`).
|
||||
- **See errors?** Capture logs by launching with `RUST_LOG=debug GROK_LOG_FILE=/tmp/grok.log grok`, then check `/tmp/grok.log`.
|
||||
|
|
@ -0,0 +1,382 @@
|
|||
# Custom Models
|
||||
|
||||
Grok connects to custom model endpoints for alternative providers, self-hosted models, and overriding built-in settings. This guide explains how to select models, configure endpoints, and integrate third-party providers.
|
||||
|
||||
---
|
||||
|
||||
## Default Models
|
||||
|
||||
By default, Grok uses models hosted by SpaceXAI, and new sessions start with `grok-build`. Default models require no configuration. Authenticate with `grok login` or an API key, then start a session.
|
||||
|
||||
List all available models:
|
||||
|
||||
```bash
|
||||
grok models
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Selecting a Model
|
||||
|
||||
### CLI Flag
|
||||
|
||||
```bash
|
||||
grok -p "Hello" -m grok-build
|
||||
```
|
||||
|
||||
### Slash Command
|
||||
|
||||
In the TUI, switch models during a session:
|
||||
|
||||
```
|
||||
/model grok-build
|
||||
```
|
||||
|
||||
Or use the alias:
|
||||
|
||||
```
|
||||
/m grok-build
|
||||
```
|
||||
|
||||
### Model Picker (Ctrl+M)
|
||||
|
||||
Press `Ctrl+M` from the scrollback pane to open the model picker. It lists all available models, both built-in and custom, and lets you switch with a single keystroke. With the prompt focused, `Ctrl+M` toggles multiline input instead -- use `/model` to switch without leaving the prompt.
|
||||
|
||||
### Config Default
|
||||
|
||||
Set a persistent default in `~/.grok/config.toml`:
|
||||
|
||||
```toml
|
||||
[models]
|
||||
default = "grok-build"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Supported API Backends
|
||||
|
||||
Grok supports three API backends. Set `api_backend` in your `[model.*]` config to choose which protocol the model uses:
|
||||
|
||||
| Value | API | Default |
|
||||
|-------|-----|---------|
|
||||
| `"chat_completions"` | OpenAI Chat Completions (`/v1/chat/completions`) | Yes |
|
||||
| `"responses"` | OpenAI Responses (`/v1/responses`) | |
|
||||
| `"messages"` | Anthropic Messages (`/v1/messages`) | |
|
||||
|
||||
When you omit `api_backend`, Grok uses `chat_completions`.
|
||||
|
||||
To send provider-specific authentication or version headers -- for example, Anthropic's `x-api-key` -- use the `extra_headers` field described below. Grok sends those headers verbatim with every request to the endpoint.
|
||||
|
||||
---
|
||||
|
||||
## Configuring Custom Models
|
||||
|
||||
Add custom model endpoints in `~/.grok/config.toml` under `[model.<name>]` sections:
|
||||
|
||||
```toml
|
||||
[model.my-model]
|
||||
model = "model-id" # Model identifier sent to the API
|
||||
base_url = "https://api.example.com/v1" # OpenAI-compatible endpoint
|
||||
name = "Display Name" # Shown in the model picker
|
||||
description = "Model description" # Optional description
|
||||
api_key = "sk-..." # API key for this provider (optional)
|
||||
env_key = "XAI_API_KEY" # Env var holding the API key (optional; string or array)
|
||||
api_backend = "chat_completions" # "chat_completions", "responses", or "messages"
|
||||
temperature = 0.7 # Sampling temperature
|
||||
top_p = 0.95 # Nucleus sampling parameter
|
||||
max_completion_tokens = 8192 # Maximum tokens per response
|
||||
context_window = 128000 # Total context window in tokens
|
||||
extra_headers = { "x-api-key" = "sk-..." } # Extra request headers, sent verbatim (optional)
|
||||
```
|
||||
|
||||
### Credential Resolution
|
||||
|
||||
Grok resolves the API key in this order:
|
||||
|
||||
1. The `api_key` field in the model config
|
||||
2. The environment variable(s) named by `env_key` — a single string or an array of names. The first set, non-empty value wins (for example `env_key = ["ANTHROPIC_AUTH_TOKEN", "LC_ANTHROPIC_AUTH_TOKEN"]` for SSH `LC_*` forwarding)
|
||||
3. Your signed-in session token (from `grok login`), for a model with no `api_key`/`env_key` of its own
|
||||
4. The `XAI_API_KEY` environment variable (global fallback; Grok also accepts `GROK_CODE_XAI_API_KEY` for backward compatibility)
|
||||
|
||||
### Context Window
|
||||
|
||||
The `context_window` value tells Grok when to trigger auto-compaction. When you override a known model, Grok inherits that model's context window. When you define a new model and omit `context_window`, Grok defaults to 200,000 tokens, so set it explicitly to match your provider.
|
||||
|
||||
### Global Default Headers
|
||||
|
||||
To apply the same headers to *every* model in the catalog -- built-in, prefetched from `/v1/models`, or custom -- set them once under the global `[models]` section instead of repeating them per model:
|
||||
|
||||
```toml
|
||||
[models]
|
||||
extra_headers = { "X-Request-Tags" = "team=example,env=prod" }
|
||||
```
|
||||
|
||||
These act as a base for each model's inference requests. A per-model `[model.<id>].extra_headers` entry overrides the global default **per key** (matched case-insensitively): a key set on the model wins, while any global-only keys are still inherited by that model. Like the per-model field, they ride on that model's inference calls -- not on separate services such as image generation or video generation -- which makes them handy for attribution tags (for example, cost tracking) without re-declaring them whenever a new model appears.
|
||||
|
||||
### Global Default Values
|
||||
|
||||
A few common per-model settings can also be set once under `[models]` as a default for *every* model. A per-model `[model.<id>]` value always wins; the global only fills in where a model (or the server's model list) left the field unset:
|
||||
|
||||
```toml
|
||||
[models]
|
||||
temperature = 0.7
|
||||
top_p = 0.95
|
||||
max_completion_tokens = 8192
|
||||
max_retries = 8
|
||||
inference_idle_timeout_secs = 600
|
||||
stream_tool_calls = true
|
||||
```
|
||||
|
||||
This is a small, fixed set of environment-wide knobs. Settings that identify a specific model (`model`, `base_url`, `api_key`, `context_window`, ...) cannot be defaulted this way, and a few settings with their own dedicated configuration -- auto-compaction (`[session]`), the system-prompt label (`[agent]`), and reasoning effort (`[models].default_reasoning_effort`) -- keep their existing homes.
|
||||
|
||||
> **Note on `stream_tool_calls`:** this one affects request *shape*, not just sampling. A few endpoints (some BYOK providers) expect it left unset; if a global `stream_tool_calls = true` causes problems for such a model, opt that model out with `stream_tool_calls = false` in its `[model.<id>]` block.
|
||||
|
||||
---
|
||||
|
||||
## Overriding Built-in Models
|
||||
|
||||
You can override specific fields of built-in models without redefining everything. Only specify the fields you want to change:
|
||||
|
||||
```toml
|
||||
# Override only the API key for a default model
|
||||
[model.grok-build]
|
||||
api_key = "my-api-key"
|
||||
|
||||
# Override temperature and add a custom API key
|
||||
[model.grok-build]
|
||||
temperature = 0.5
|
||||
api_key = "sk-custom"
|
||||
```
|
||||
|
||||
When you override a built-in model, Grok starts with the default configuration (including the correct `base_url`), then applies only the fields you specify. Unspecified fields inherit from the default.
|
||||
|
||||
### Priority Order
|
||||
|
||||
1. Your config (`[model.*]`) -- highest priority
|
||||
2. Prefetched models from remote `/v1/models`
|
||||
3. Hardcoded defaults -- lowest priority
|
||||
|
||||
---
|
||||
|
||||
## Provider Examples
|
||||
|
||||
### Anthropic (Claude)
|
||||
|
||||
Use Claude models directly via the Anthropic Messages API:
|
||||
|
||||
```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" }
|
||||
```
|
||||
|
||||
The `messages` backend uses the Anthropic Messages protocol. Anthropic authenticates with an `x-api-key` header rather than `Authorization: Bearer`, so pass your key through `extra_headers`, which Grok sends verbatim.
|
||||
|
||||
### 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"
|
||||
```
|
||||
|
||||
`api_backend` defaults to `"chat_completions"`, so you don't need to set it explicitly for OpenAI.
|
||||
|
||||
### OpenAI (Responses API)
|
||||
|
||||
If your provider supports the newer Responses API:
|
||||
|
||||
```toml
|
||||
[model.gpt-4o-responses]
|
||||
model = "gpt-4o"
|
||||
base_url = "https://api.openai.com/v1"
|
||||
name = "GPT-4o (Responses)"
|
||||
api_backend = "responses"
|
||||
env_key = "OPENAI_API_KEY"
|
||||
```
|
||||
|
||||
### Ollama (Local Models)
|
||||
|
||||
Run models locally with [Ollama](https://ollama.ai):
|
||||
|
||||
```toml
|
||||
[model.ollama-codellama]
|
||||
model = "codellama"
|
||||
base_url = "http://localhost:11434/v1"
|
||||
name = "CodeLlama (Ollama)"
|
||||
```
|
||||
|
||||
Make sure Ollama is running (`ollama serve`) and the model is pulled (`ollama pull codellama`).
|
||||
|
||||
### Together AI
|
||||
|
||||
```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"
|
||||
```
|
||||
|
||||
### Local OpenAI-Compatible Server
|
||||
|
||||
Any server that implements the OpenAI Chat Completions or Responses API:
|
||||
|
||||
```toml
|
||||
[model.local-llama]
|
||||
model = "llama-3.1-70b"
|
||||
base_url = "http://localhost:8080/v1"
|
||||
name = "Local Llama"
|
||||
temperature = 0.8
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Custom Models Endpoint
|
||||
|
||||
Point Grok at a custom OpenAI-compatible `/v1/models` endpoint instead of the default. Use this when your models sit behind a corporate gateway or a self-hosted inference service.
|
||||
|
||||
### Environment Variables
|
||||
|
||||
| Variable | Required | Description |
|
||||
|----------|----------|-------------|
|
||||
| `GROK_MODELS_BASE_URL` | Yes | Base URL for inference. Grok fetches the model list from `{base_url}/models`. |
|
||||
| `XAI_API_KEY` | Yes | API key sent as `Authorization: Bearer`. Grok also accepts `GROK_CODE_XAI_API_KEY`. |
|
||||
| `GROK_MODELS_LIST_URL` | No | Override the model-list URL when it differs from `{base_url}/models`. |
|
||||
|
||||
### Setup
|
||||
|
||||
```bash
|
||||
export GROK_MODELS_BASE_URL="https://api.acme.com/v1"
|
||||
export XAI_API_KEY="xai-..."
|
||||
grok
|
||||
```
|
||||
|
||||
### Config File Alternative
|
||||
|
||||
```toml
|
||||
[endpoints]
|
||||
models_base_url = "https://api.acme.com/v1"
|
||||
|
||||
# Override only the API key for a specific model
|
||||
[model.grok-build]
|
||||
api_key = "my-api-key"
|
||||
```
|
||||
|
||||
When you use `[endpoints]` with partial model overrides, Grok inherits the `base_url` from the endpoints config, so you do not need to specify it in each `[model.*]` section.
|
||||
|
||||
### Auth Behavior
|
||||
|
||||
When you set `models_base_url`, Grok uses API key auth (`Authorization: Bearer`) instead of session auth. You do not need `grok login` -- the API key is enough.
|
||||
|
||||
---
|
||||
|
||||
## Web Search Model
|
||||
|
||||
The `web_search` tool uses a separate model. Configure it with:
|
||||
|
||||
```toml
|
||||
[models]
|
||||
web_search = "grok-4.20-multi-agent"
|
||||
```
|
||||
|
||||
Or via environment variable:
|
||||
|
||||
```bash
|
||||
export GROK_WEB_SEARCH_MODEL="grok-4.20-multi-agent"
|
||||
```
|
||||
|
||||
If you point web search at a custom model, you also need a `[model.*]` entry so Grok can reach it. Server-side ("backend") web search runs only when the model sets `supports_backend_search = true` (and the build enables backend search); it does not depend on `api_backend`:
|
||||
|
||||
```toml
|
||||
[models]
|
||||
web_search = "my-custom-model"
|
||||
|
||||
[model.my-custom-model]
|
||||
model = "my-custom-model"
|
||||
supports_backend_search = true
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Using Custom Models
|
||||
|
||||
```bash
|
||||
# List available models (including custom)
|
||||
grok models
|
||||
|
||||
# Use in the TUI via slash command
|
||||
/model my-model
|
||||
|
||||
# Use in headless mode
|
||||
grok -p "Hello" -m my-model
|
||||
|
||||
# Set as default in config.toml:
|
||||
[models]
|
||||
default = "my-model"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Enterprise Deployment
|
||||
|
||||
A complete config for an enterprise deployment with custom models:
|
||||
|
||||
```toml
|
||||
[cli]
|
||||
auto_update = false
|
||||
|
||||
[auth]
|
||||
auth_provider_command = "/usr/local/bin/my-company-auth-provider"
|
||||
auth_provider_label = "Acme Corp"
|
||||
auth_token_ttl = 3600
|
||||
|
||||
[models]
|
||||
default = "company-grok"
|
||||
|
||||
[model.company-grok]
|
||||
model = "grok-build"
|
||||
base_url = "https://grok-proxy.acme.com/"
|
||||
name = "Grok Build Latest (Proxy)"
|
||||
context_window = 128000
|
||||
|
||||
[features]
|
||||
telemetry = false
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Model Not Found
|
||||
|
||||
```bash
|
||||
# List available models
|
||||
grok models
|
||||
|
||||
# Check config.toml for typos in [model.*] sections
|
||||
```
|
||||
|
||||
### Connection Errors
|
||||
|
||||
Verify the endpoint is reachable:
|
||||
|
||||
```bash
|
||||
curl -s https://api.example.com/v1/models \
|
||||
-H "Authorization: Bearer $XAI_API_KEY"
|
||||
```
|
||||
|
||||
### Debug Logging
|
||||
|
||||
```bash
|
||||
RUST_LOG=debug GROK_LOG_FILE=/tmp/grok.log grok
|
||||
tail -f /tmp/grok.log
|
||||
```
|
||||
|
||||
Look for log entries containing `model` or `sampling` to trace model selection and API calls.
|
||||
|
|
@ -0,0 +1,229 @@
|
|||
# Project Rules (AGENTS.md)
|
||||
|
||||
Project rules let you configure Grok per project or directory. By placing an AGENTS.md file in your repository, you can set coding conventions, build instructions, style guides, and any other instructions that Grok should follow when working in that codebase.
|
||||
|
||||
---
|
||||
|
||||
## What Are Project Rules?
|
||||
|
||||
Project rules are Markdown files that Grok reads and adds to its context. Grok follows their content for every interaction in that tree.
|
||||
|
||||
This is the primary mechanism for teaching Grok about your project's conventions, so you need not restate them each session.
|
||||
|
||||
---
|
||||
|
||||
## Supported File Names
|
||||
|
||||
Grok checks for these filenames (in this order) within each directory:
|
||||
|
||||
- `Agents.md`
|
||||
- `Claude.md`
|
||||
- `CLAUDE.md`
|
||||
- `CLAUDE.local.md`
|
||||
- `AGENT.md`
|
||||
- `AGENTS.md`
|
||||
|
||||
Grok loads every matching file in a directory, so a folder that contains both `AGENTS.md` and `CLAUDE.md` contributes both. On case-insensitive filesystems, names that resolve to the same file (such as `Agents.md` and `AGENTS.md`) are deduplicated and counted once. `Claude.md`, `CLAUDE.md`, and `CLAUDE.local.md` are supported for compatibility with Claude Code workflows. When Claude compatibility is enabled (the default), Grok also scans your home-level `~/.claude/` directory for these filenames and, at each directory level, checks `.claude/CLAUDE.md` and `.claude/CLAUDE.local.md` -- the locations Claude Code uses for project memory. With Cursor compatibility enabled, the home-level `~/.cursor/` directory is scanned the same way.
|
||||
|
||||
### Rules Directories
|
||||
|
||||
In addition to AGENTS.md files, Grok scans for `*.md` files in rules directories at each level (`<dir>`) from the repo root to the current working directory:
|
||||
|
||||
| Location | Notes |
|
||||
|----------|-------|
|
||||
| `<dir>/.grok/rules/` | Always scanned |
|
||||
| `<dir>/.claude/rules/` | Claude compatibility (configurable) |
|
||||
| `<dir>/.cursor/rules/` | Cursor compatibility (configurable) |
|
||||
|
||||
Grok scans the Claude and Cursor rules directories by default. To disable scanning for a specific vendor, set its cell in the `[compat]` config section or the corresponding environment variable. See [Configuration](05-configuration.md#harness-compatibility) for details.
|
||||
|
||||
---
|
||||
|
||||
## How Discovery Works
|
||||
|
||||
Grok scans for project rules in this order:
|
||||
|
||||
1. **Global rules**: `~/.grok/` (applies to all projects)
|
||||
2. **Repo rules**: If inside a git repo, every directory from the repo root down to the current working directory (inclusive)
|
||||
3. **CWD-only**: If not inside a git repo, only the current working directory
|
||||
|
||||
### Example
|
||||
|
||||
Given this project structure:
|
||||
|
||||
```
|
||||
~/projects/my-app/
|
||||
AGENTS.md # "Use TypeScript. Follow ESLint rules."
|
||||
src/
|
||||
AGENTS.md # "Prefer functional components."
|
||||
components/
|
||||
AGENTS.md # "Use CSS modules for styling."
|
||||
```
|
||||
|
||||
When Grok runs in `~/projects/my-app/src/components/`, it loads all three files. The instructions accumulate, so Grok sees all of them.
|
||||
|
||||
### Deeper Files Take Precedence
|
||||
|
||||
Grok orders the files from the repo root to the current working directory, so files in deeper directories appear later in its context and take precedence when instructions conflict. In the example above, if the root says "Use styled-components" but `components/AGENTS.md` says "Use CSS modules", the CSS modules instruction wins because it appears later.
|
||||
|
||||
### Auto-Loading Behavior
|
||||
|
||||
- Grok loads the files from the repo root to the current working directory automatically at session start.
|
||||
- When Grok reads, lists, or edits files in directories outside that initial set, it detects any project instruction files there, notes their paths, and reads them when they apply to the task.
|
||||
|
||||
---
|
||||
|
||||
## What to Put in Project Rules
|
||||
|
||||
### Coding Conventions
|
||||
|
||||
```markdown
|
||||
# Coding Standards
|
||||
|
||||
- Use TypeScript for all new code
|
||||
- Prefer functional components with hooks over class components
|
||||
- Use `const` by default; only use `let` when reassignment is needed
|
||||
- Maximum line length: 100 characters
|
||||
```
|
||||
|
||||
### Build and Test Instructions
|
||||
|
||||
```markdown
|
||||
# Build & Test
|
||||
|
||||
- Run `npm test` before committing
|
||||
- Use `npm run lint` to check code style
|
||||
- Build with `npm run build` -- ensure no TypeScript errors
|
||||
- Integration tests: `npm run test:e2e` (requires Docker)
|
||||
```
|
||||
|
||||
### Style Guides
|
||||
|
||||
```markdown
|
||||
# Style Guide
|
||||
|
||||
- Follow the Airbnb JavaScript Style Guide
|
||||
- Use 2-space indentation
|
||||
- Always use trailing commas in multi-line arrays/objects
|
||||
- Prefer template literals over string concatenation
|
||||
```
|
||||
|
||||
### PR and Commit Requirements
|
||||
|
||||
```markdown
|
||||
# Version Control
|
||||
|
||||
- Write commit messages in conventional commits format
|
||||
- Prefix branch names with `feature/`, `fix/`, or `chore/`
|
||||
- All PRs require at least one approval before merge
|
||||
- Squash-merge feature branches
|
||||
```
|
||||
|
||||
### Architecture Notes
|
||||
|
||||
```markdown
|
||||
# Architecture
|
||||
|
||||
- API routes go in `src/routes/` with one file per resource
|
||||
- Business logic goes in `src/services/`
|
||||
- Database queries go in `src/repositories/`
|
||||
- Never import from `src/routes/` in `src/services/`
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Scoping Rules to Subdirectories
|
||||
|
||||
AGENTS.md files scope to the entire directory tree rooted at their folder. Use this to provide different instructions for different parts of your codebase:
|
||||
|
||||
```
|
||||
my-monorepo/
|
||||
AGENTS.md # Monorepo-wide rules
|
||||
packages/
|
||||
frontend/
|
||||
AGENTS.md # "Use React. Prefer CSS modules."
|
||||
backend/
|
||||
AGENTS.md # "Use Express. Follow REST conventions."
|
||||
shared/
|
||||
AGENTS.md # "No framework-specific code in this package."
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Session Rules Flags
|
||||
|
||||
To add rules for a single session without editing files, pass `--rules` (alias `--append-system-prompt`):
|
||||
|
||||
```bash
|
||||
grok --rules "Always use TypeScript. Prefer functional components."
|
||||
```
|
||||
|
||||
Grok appends this text to the session's system prompt. Use it for session-specific customization.
|
||||
|
||||
To replace the system prompt entirely, pass `--system-prompt-override` (alias `--system-prompt`). Grok uses the text verbatim and skips both the default system prompt and `--rules`. (Text passed with `--rules`, by contrast, is wrapped in a `<human_rules>` block and appended to the default prompt.)
|
||||
|
||||
---
|
||||
|
||||
## File Size
|
||||
|
||||
Grok loads each project instruction file in full; there is no character cap and no truncation. Even so, keep instructions concise and focused. Shorter, specific rules are easier for Grok to follow than long ones, and every file you load consumes context.
|
||||
|
||||
---
|
||||
|
||||
## Gitignore Filtering
|
||||
|
||||
Files ignored by `.gitignore` are skipped during discovery. To keep personal overrides out of the shared repository, gitignore a recognized filename such as `CLAUDE.local.md`:
|
||||
|
||||
```gitignore
|
||||
# .gitignore
|
||||
CLAUDE.local.md
|
||||
```
|
||||
|
||||
As top-level instruction files, Grok discovers only the recognized filenames listed under [Supported File Names](#supported-file-names) — not custom names such as `AGENTS.local.md` or `notes.md`. (Inside a rules directory such as `.grok/rules/`, every `*.md` file is loaded regardless of name.)
|
||||
|
||||
---
|
||||
|
||||
## The .grok/ Project Directory
|
||||
|
||||
Beyond AGENTS.md files, the `.grok/` directory in your project root can contain additional project-level configuration:
|
||||
|
||||
| Path | Purpose |
|
||||
|------|---------|
|
||||
| `.grok/config.toml` | Project-scoped MCP servers, plugins, and permission rules (other settings load only from `~/.grok/config.toml`) |
|
||||
| `.grok/skills/` | Project-scoped skill definitions |
|
||||
| `.grok/plugins/` | Project-scoped plugins |
|
||||
| `.grok/agents/` | Project-scoped agent definitions |
|
||||
| `.grok/hooks/` | Project-scoped lifecycle hooks |
|
||||
| `.grok/lsp.json` | LSP server configuration |
|
||||
|
||||
These are all optional. See the respective guides for details on each.
|
||||
|
||||
---
|
||||
|
||||
## Inspecting Loaded Rules
|
||||
|
||||
Use `grok inspect` to see all loaded project instructions:
|
||||
|
||||
```bash
|
||||
grok inspect
|
||||
```
|
||||
|
||||
This shows each project instruction file it finds, with its path and approximate token count. Use it to confirm Grok picks up your rules.
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Start with the root.** Put the most important, project-wide rules in the repo root AGENTS.md.
|
||||
|
||||
2. **Be specific.** "Use TypeScript" is better than "Use modern JavaScript". "Run `cargo fmt` before committing" is better than "Format your code".
|
||||
|
||||
3. **Keep it short.** Concise instructions are more likely to be followed than lengthy ones.
|
||||
|
||||
4. **Use subdirectory scoping for large repos.** Different parts of a monorepo may have different conventions. Use per-directory AGENTS.md to scope rules appropriately.
|
||||
|
||||
5. **Version control your rules.** Commit AGENTS.md to the repository so the whole team benefits. User-specific overrides belong in `~/.grok/` (global rules).
|
||||
|
||||
6. **Do not duplicate documentation.** AGENTS.md should contain actionable instructions, not a copy of your project's README. Link to external docs if needed.
|
||||
|
||||
7. **Review periodically.** As your project evolves, update your rules to match current conventions.
|
||||
476
crates/codegen/xai-grok-pager/docs/user-guide/13-memory.md
Normal file
476
crates/codegen/xai-grok-pager/docs/user-guide/13-memory.md
Normal file
|
|
@ -0,0 +1,476 @@
|
|||
# Cross-Session Memory
|
||||
|
||||
Memory lets Grok recall facts, decisions, and patterns from earlier sessions. Grok indexes the information you save and searches it automatically, so a new session can reuse relevant context.
|
||||
|
||||
---
|
||||
|
||||
## What Is Memory?
|
||||
|
||||
Without memory, each Grok session starts fresh: the model knows nothing about previous sessions. When you enable memory, Grok can:
|
||||
|
||||
- Recall project conventions you explained before.
|
||||
- Reuse debugging steps that worked.
|
||||
- Carry architectural decisions forward across sessions.
|
||||
- Avoid re-asking questions it already has answers to.
|
||||
|
||||
Memory is experimental and disabled by default.
|
||||
|
||||
---
|
||||
|
||||
## Enabling Memory
|
||||
|
||||
### Per-Session Flag
|
||||
|
||||
```bash
|
||||
grok --experimental-memory
|
||||
```
|
||||
|
||||
### Environment Variable
|
||||
|
||||
```bash
|
||||
export GROK_MEMORY=1
|
||||
grok
|
||||
```
|
||||
|
||||
### Config File (Persistent)
|
||||
|
||||
```toml
|
||||
# ~/.grok/config.toml
|
||||
[memory]
|
||||
enabled = true
|
||||
```
|
||||
|
||||
### Force-Disable
|
||||
|
||||
To disable memory even when other settings enable it:
|
||||
|
||||
```bash
|
||||
grok --no-memory
|
||||
```
|
||||
|
||||
Or:
|
||||
|
||||
```bash
|
||||
export GROK_MEMORY=0
|
||||
```
|
||||
|
||||
The `--no-memory` flag has absolute highest priority and always disables memory.
|
||||
|
||||
### Mid-Session Toggle
|
||||
|
||||
Toggle memory on or off during a session without restarting:
|
||||
|
||||
```
|
||||
/memory on
|
||||
/memory off
|
||||
```
|
||||
|
||||
The toggle is session-scoped -- it does not persist to `config.toml`. Toggling off removes access to memory tools but keeps existing files on disk. Toggling on re-initializes memory storage and registers the memory tools.
|
||||
|
||||
You can also toggle from inside the `/memory` modal by pressing `t`.
|
||||
|
||||
### Priority Order
|
||||
|
||||
1. `--no-memory` CLI flag (always disables)
|
||||
2. `--experimental-memory` CLI flag (enables)
|
||||
3. `GROK_MEMORY` env var: `1`/`true` enables, `0`/`false` disables
|
||||
4. `[memory]` section in config.toml
|
||||
5. Default: disabled
|
||||
|
||||
---
|
||||
|
||||
## How Memory Is Stored
|
||||
|
||||
Memory is stored as Markdown files under `~/.grok/memory/`:
|
||||
|
||||
| Location | Scope | Description |
|
||||
|----------|-------|-------------|
|
||||
| `~/.grok/memory/MEMORY.md` | Global | Facts that apply across all your projects |
|
||||
| `~/.grok/memory/<project-slug>-<hash8>/MEMORY.md` | Workspace | Project-specific conventions and context |
|
||||
| `~/.grok/memory/<project-slug>-<hash8>/sessions/` | Sessions | Per-session summaries and logs |
|
||||
|
||||
Grok suffixes each workspace directory with a short hash of the repository's identity. The identity is the `origin` remote in `org/repo` form when the directory is a Git repository with an `origin` remote, or the directory path otherwise. Because clones and worktrees of the same repository share an `origin` remote, they also share one memory directory.
|
||||
|
||||
An SQLite index supports hybrid search across all memory files:
|
||||
- **FTS5** provides full-text search for keyword matching.
|
||||
- **vec0** provides vector search for semantic similarity. Vector search is optional and requires an embedding.
|
||||
|
||||
---
|
||||
|
||||
## Automatic Saves
|
||||
|
||||
When a session ends, Grok saves a structured metadata summary to that session's daily log. The summary contains:
|
||||
|
||||
- Message counts (user, assistant, and tool results).
|
||||
- Topics: the first few substantive user prompts from the session, up to five.
|
||||
- The session date and time (UTC).
|
||||
|
||||
Grok builds the summary from conversation metadata without an LLM call, without added latency. Grok skips the save for trivial sessions -- those with fewer than three substantive prompts, or fewer than 50 bytes of user text.
|
||||
|
||||
The summary does not record tool usage, file paths, or shell commands. The session ID forms part of the log filename. To turn automatic saves off, set `session.save_on_end = false`. For richer capture of decisions, patterns, and reasoning, use `/flush`.
|
||||
|
||||
---
|
||||
|
||||
## Saving Rich Knowledge with /flush
|
||||
|
||||
For richer capture -- decisions, patterns, debugging workflows, API discoveries -- use `/flush` in the TUI:
|
||||
|
||||
```
|
||||
/flush
|
||||
```
|
||||
|
||||
This triggers an LLM-generated summary of the current session's most important content and writes it to a dated session log. The summary is indexed and searchable in future sessions.
|
||||
|
||||
Use `/flush` when you want to preserve important context:
|
||||
- Before compaction (which discards old conversation turns)
|
||||
- At the end of a productive debugging session
|
||||
- After discovering important patterns or conventions
|
||||
|
||||
---
|
||||
|
||||
## Working with Memory
|
||||
|
||||
### Remember
|
||||
|
||||
Ask Grok to remember something, and it appends the note to a `MEMORY.md` file -- the workspace file for project-specific items, or the global `~/.grok/memory/MEMORY.md` for cross-project preferences:
|
||||
|
||||
```
|
||||
> remember to always open PR links after pushing
|
||||
```
|
||||
|
||||
Grok records entries as durable statements under organized headings, such as `## Preferences`, `## Project Context`, or `## Debugging`. The file watcher reindexes the change on the next memory search, so the new entry is searchable within the current session.
|
||||
|
||||
You can also save a note directly with the `/remember` command:
|
||||
|
||||
```
|
||||
/remember always open PR links after pushing
|
||||
```
|
||||
|
||||
Run `/remember` with no text to enter remember mode, where the next line you type becomes the note. Either way, Grok opens a review panel showing the note (with an optional rewritten version you can toggle with `Tab`); the note is written only after you confirm. On save, Grok shows `Memory saved to ~/.grok/memory/MEMORY.md`.
|
||||
|
||||
### Forget
|
||||
|
||||
Ask Grok to forget something, and it finds and removes the matching entry:
|
||||
|
||||
```
|
||||
> forget the snake_case convention
|
||||
```
|
||||
|
||||
Forget is best-effort: the model searches memory and removes entries that match. For guaranteed removal, edit the files under `~/.grok/memory/` directly and delete the entry yourself. To locate a file, open the `/memory` browser and press `y` to copy its path.
|
||||
|
||||
### Recall
|
||||
|
||||
Ask what Grok remembers:
|
||||
|
||||
```
|
||||
> what do you remember?
|
||||
```
|
||||
|
||||
Grok searches across all memory files and summarizes what it knows, grouped by source: global preferences, project-specific knowledge, and session history. Use `/memory` to browse the raw files.
|
||||
|
||||
### Direct Editing
|
||||
|
||||
You can edit memory files directly under `~/.grok/memory/`. The file watcher reindexes your changes on the next memory search. Use `/flush` to save the current session now, and `/dream` to consolidate session logs into organized topics.
|
||||
|
||||
---
|
||||
|
||||
## Browsing Memory with /memory
|
||||
|
||||
The `/memory` command opens a modal showing all memory files:
|
||||
|
||||
```
|
||||
/memory
|
||||
```
|
||||
|
||||
Files are grouped by scope:
|
||||
- **Global** -- cross-project memory (`MEMORY.md`).
|
||||
- **Workspace** -- project-specific memory (`MEMORY.md`).
|
||||
- **Sessions** -- per-session summaries, in reverse chronological order.
|
||||
|
||||
The modal uses a split-pane layout: the file list on the left, a read-only content preview on the right. The preview updates as you move through the list.
|
||||
|
||||
### Keyboard Shortcuts
|
||||
|
||||
| Key | Action |
|
||||
|-----|--------|
|
||||
| `↑`/`↓` or `j`/`k` | Move through the file list |
|
||||
| `PgUp`/`PgDn` | Jump 10 entries |
|
||||
| `/` | Filter the file list |
|
||||
| `y` | Copy the selected file's path to the clipboard |
|
||||
| `x` | Delete the selected session file (press `x` again to confirm) |
|
||||
| `t` | Toggle memory on or off |
|
||||
| `Ctrl+F` | Toggle fullscreen |
|
||||
| `Esc` | Close the modal, or exit filter mode |
|
||||
|
||||
The preview pane is read-only. Scroll it with the mouse wheel or by dragging its scrollbar. You can delete only session files, not the global or workspace `MEMORY.md`.
|
||||
|
||||
When the memory modal's content area is under 80 columns, the modal hides the preview pane and shows the file list only.
|
||||
|
||||
You can also open `/memory` from the command palette.
|
||||
|
||||
---
|
||||
|
||||
## Memory Notifications
|
||||
|
||||
When you save a note with `/remember`, Grok confirms in the scrollback:
|
||||
|
||||
```
|
||||
Memory saved to ~/.grok/memory/MEMORY.md
|
||||
```
|
||||
|
||||
Background saves — flush, dream, and session-end — run silently and do not post a scrollback message. Use `/memory` at any time to browse what Grok has stored.
|
||||
|
||||
---
|
||||
|
||||
## Dream Consolidation with /dream
|
||||
|
||||
The `/dream` command consolidates scattered memory fragments into organized topics:
|
||||
|
||||
```
|
||||
/dream
|
||||
```
|
||||
|
||||
Dream reorganizes individual session logs and memory entries into a coherent, deduplicated knowledge base, which reduces noise and improves search quality over time. `/dream` requires memory to be enabled.
|
||||
|
||||
### Auto-Dream
|
||||
|
||||
Dream also runs automatically. By default, Grok checks the consolidation gates when a session ends and runs Dream once enough time has passed and enough sessions have accumulated:
|
||||
|
||||
```toml
|
||||
[memory.dream]
|
||||
enabled = true # Run automatic consolidation (default: true)
|
||||
min_hours = 4 # Minimum hours between consolidations
|
||||
min_sessions = 3 # Minimum sessions since the last consolidation
|
||||
# check_interval_secs is unset by default, so Dream runs only at session end.
|
||||
# Set it to a positive number of seconds to also check on a periodic interval.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## How Memory Affects Prompts
|
||||
|
||||
### First-Turn Injection
|
||||
|
||||
On the first turn of each session, Grok automatically searches memory for content relevant to the current project and injects it as context. This means Grok starts with knowledge from previous sessions without a reminder.
|
||||
|
||||
First-turn injection can be configured:
|
||||
|
||||
```toml
|
||||
[memory.initial_injection]
|
||||
enabled = true # Enable or disable first-turn injection
|
||||
min_score = 0.0 # Optional score threshold; unset by default, which applies no filtering
|
||||
```
|
||||
|
||||
### After Compaction
|
||||
|
||||
Memory is also searched after auto-compaction to recover relevant context that may have been discarded.
|
||||
|
||||
---
|
||||
|
||||
## Memory Search
|
||||
|
||||
Grok searches memory automatically, but you can also trigger searches manually in the chat:
|
||||
|
||||
```
|
||||
Search memory for "auth middleware patterns"
|
||||
Read my workspace MEMORY.md
|
||||
```
|
||||
|
||||
The model has access to two memory tools:
|
||||
- `memory_search` -- Hybrid search across all memory (vector + full-text)
|
||||
- `memory_get` -- Read a specific memory file by path
|
||||
|
||||
### Hybrid Scoring
|
||||
|
||||
Memory search uses a weighted combination of:
|
||||
- **Vector similarity** (semantic) -- weight: 0.7
|
||||
- **BM25 text similarity** (keyword) -- weight: 0.3
|
||||
|
||||
Results are filtered by a minimum score threshold (default: 0.35).
|
||||
|
||||
### Source Weights
|
||||
|
||||
Each memory source has a weight multiplier applied to its score. All sources default to `1.0`, and you can adjust any of them under `[memory.search.source_weights]`:
|
||||
|
||||
| Source | Weight | Description |
|
||||
|--------|--------|-------------|
|
||||
| `workspace` | 1.0 | Project-specific memory |
|
||||
| `session` | 1.0 | Session logs |
|
||||
| `global` | 1.0 | Cross-project memory |
|
||||
|
||||
### Temporal Decay
|
||||
|
||||
Session memories decay over time so recent sessions are prioritized:
|
||||
|
||||
```toml
|
||||
[memory.search.temporal_decay]
|
||||
enabled = true # Enable time-based decay
|
||||
half_life_days = 7.0 # Score halves after this many days
|
||||
```
|
||||
|
||||
Only session chunks decay. Global and workspace memories are exempt since they contain curated long-term knowledge.
|
||||
|
||||
### MMR (Maximal Marginal Relevance)
|
||||
|
||||
MMR re-ranking penalizes redundant results to improve diversity:
|
||||
|
||||
```toml
|
||||
[memory.search.mmr]
|
||||
enabled = false # Opt-in diversity re-ranking
|
||||
lambda = 0.7 # 0.0 = max diversity, 1.0 = pure relevance
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## CLI Commands
|
||||
|
||||
The `grok memory` command manages memory from the shell. It has one subcommand, `clear`:
|
||||
|
||||
```bash
|
||||
# Clear workspace memory (MEMORY.md, sessions/, and index.sqlite). This is the default scope.
|
||||
grok memory clear
|
||||
|
||||
# The same scope, stated explicitly
|
||||
grok memory clear --workspace
|
||||
|
||||
# Clear the global MEMORY.md
|
||||
grok memory clear --global
|
||||
|
||||
# Clear both workspace and global memory
|
||||
grok memory clear --all
|
||||
|
||||
# Skip the confirmation prompt (-y is the short form)
|
||||
grok memory clear --yes
|
||||
```
|
||||
|
||||
To edit memory from the shell, open the files in your editor directly -- for example, `$EDITOR ~/.grok/memory/MEMORY.md`.
|
||||
|
||||
---
|
||||
|
||||
## Configuration Reference
|
||||
|
||||
### Core Settings (`[memory]`)
|
||||
|
||||
| Key | Default | Description |
|
||||
|-----|---------|-------------|
|
||||
| `enabled` | `false` | Enable memory |
|
||||
| `session.save_on_end` | `true` | Write metadata summary on session end |
|
||||
| `watcher.enabled` | `true` | Watch `~/.grok/memory/` for external edits and reindex |
|
||||
|
||||
### Index Settings (`[memory.index]`)
|
||||
|
||||
| Key | Default | Description |
|
||||
|-----|---------|-------------|
|
||||
| `max_chunk_chars` | `1600` | Maximum chunk size in characters |
|
||||
| `chunk_overlap_chars` | `320` | Character overlap between chunks |
|
||||
|
||||
### Embedding Settings (`[memory.embedding]`)
|
||||
|
||||
| Key | Default | Description |
|
||||
|-----|---------|-------------|
|
||||
| `provider` | `"api"` | Embedding provider (currently `"api"`) |
|
||||
| `model` | *(provider default)* | Embedding model name |
|
||||
| `dimensions` | `1024` | Embedding vector dimensions |
|
||||
|
||||
### Search Settings (`[memory.search]`)
|
||||
|
||||
| Key | Default | Description |
|
||||
|-----|---------|-------------|
|
||||
| `max_results` | `6` | Maximum search results |
|
||||
| `min_score` | `0.35` | Minimum relevance score |
|
||||
| `vector_weight` | `0.7` | Weight for vector similarity |
|
||||
| `text_weight` | `0.3` | Weight for BM25 text similarity |
|
||||
|
||||
### Initial Injection Settings (`[memory.initial_injection]`)
|
||||
|
||||
| Key | Default | Description |
|
||||
|-----|---------|-------------|
|
||||
| `enabled` | `true` | Enable first-turn memory injection |
|
||||
| `min_score` | unset | Score threshold for first-turn results. When unset, Grok applies no threshold, which is equivalent to `0.0`. |
|
||||
|
||||
### Dream Settings (`[memory.dream]`)
|
||||
|
||||
| Key | Default | Description |
|
||||
|-----|---------|-------------|
|
||||
| `enabled` | `true` | Enable automatic Dream consolidation |
|
||||
| `min_hours` | `4` | Minimum hours between consolidations |
|
||||
| `min_sessions` | `3` | Minimum sessions since the last consolidation |
|
||||
| `stale_lock_secs` | `3600` | Seconds before a stale consolidation lock is reclaimed |
|
||||
| `check_interval_secs` | unset | Periodic check interval in seconds. When unset, Dream runs only at session end. |
|
||||
|
||||
### Flush Settings (`[compaction.memory_flush]`)
|
||||
|
||||
You configure flush under `[compaction]`, not `[memory]`, because it is a compaction behavior.
|
||||
|
||||
| Key | Default | Description |
|
||||
|-----|---------|-------------|
|
||||
| `enabled` | `true` | Enable the pre-compaction memory flush |
|
||||
| `soft_threshold_tokens` | `4000` | Token headroom before the compact threshold that triggers a flush |
|
||||
| `max_flush_write_chars` | `8000` | Maximum characters the flush may write to memory |
|
||||
| `flush_model` | unset | Model for the flush turn. When unset, Grok uses the session's primary model. |
|
||||
| `idle_timeout_secs` | unset | Idle seconds before a background flush. When unset, flush runs only before compaction. |
|
||||
| `semantic_dedup_threshold` | unset | Cosine-similarity threshold for de-duplicating flushed content. When unset, defaults to `0.92`. |
|
||||
|
||||
### Pruning Settings (`[compaction.pruning]`)
|
||||
|
||||
You configure pruning under `[compaction]`, not `[memory]`, because it is a compaction behavior.
|
||||
|
||||
| Key | Default | Description |
|
||||
|-----|---------|-------------|
|
||||
| `enabled` | `true` | Enable tool-result pruning |
|
||||
| `keep_last_n_turns` | `3` | Number of recent turns whose tool results are never pruned |
|
||||
| `soft_trim_threshold` | `4000` | Character threshold above which old tool results are soft-trimmed |
|
||||
| `soft_trim_head` | `1500` | Characters kept from the start of a soft-trimmed result |
|
||||
| `soft_trim_tail` | `1500` | Characters kept from the end of a soft-trimmed result |
|
||||
| `hard_clear_age_turns` | `10` | Turn age after which tool results are replaced with a placeholder |
|
||||
|
||||
---
|
||||
|
||||
## Memory Staleness
|
||||
|
||||
When a session memory is old, Grok attaches a staleness note to it in search results. Older results get a stronger reminder to verify the current state before you rely on them. These notes help you spot stored facts that might no longer be accurate. Global and workspace memories never receive staleness notes, because they hold curated long-term knowledge.
|
||||
|
||||
---
|
||||
|
||||
## File Watcher
|
||||
|
||||
By default, Grok watches `~/.grok/memory/` for external file changes. If you edit memory files directly (e.g., in your editor), the changes are picked up automatically on the next memory search:
|
||||
|
||||
- Created or modified files are reindexed.
|
||||
- Deleted files have their stale chunks removed from the index.
|
||||
|
||||
```toml
|
||||
[memory.watcher]
|
||||
enabled = true # default
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Memory Not Working
|
||||
|
||||
1. Verify memory is enabled: check `grok inspect` output.
|
||||
2. Check the flag: `grok --experimental-memory` or `GROK_MEMORY=1`.
|
||||
3. Check for `--no-memory` or `GROK_MEMORY=0` overriding your config.
|
||||
|
||||
### Memory Not Appearing in Sessions
|
||||
|
||||
Memory is injected on the first turn. If you started a session before enabling memory, start a new session with `/new`.
|
||||
|
||||
### Viewing Memory Files
|
||||
|
||||
Use `/memory` in the TUI to browse all memory files with a preview. You can also access them directly:
|
||||
|
||||
```bash
|
||||
ls ~/.grok/memory/
|
||||
cat ~/.grok/memory/MEMORY.md
|
||||
$EDITOR ~/.grok/memory/MEMORY.md
|
||||
```
|
||||
|
||||
### Debug Logging
|
||||
|
||||
```bash
|
||||
RUST_LOG=debug GROK_LOG_FILE=/tmp/grok.log grok
|
||||
grep "memory" /tmp/grok.log
|
||||
```
|
||||
|
|
@ -0,0 +1,593 @@
|
|||
# Headless Mode and Scripting
|
||||
|
||||
Headless mode runs Grok non-interactively from the command line. It accepts a single prompt, executes it with full tool access, and returns the result. Use it to automate tasks, script workflows, build integrations, and parse output programmatically.
|
||||
|
||||
---
|
||||
|
||||
## Basic Usage
|
||||
|
||||
Passing a prompt non-interactively triggers headless mode. The most common way is the `-p` flag (short for `--single`); `--prompt-json` and `--prompt-file` also trigger it:
|
||||
|
||||
```bash
|
||||
grok -p "Your prompt here"
|
||||
```
|
||||
|
||||
Grok processes the prompt, runs any necessary tools, and prints the result to stdout. The process exits when the response is complete.
|
||||
|
||||
---
|
||||
|
||||
## Command-Line Options
|
||||
|
||||
| Flag | Description |
|
||||
| ----------------------- | ----------------------------------------------------- |
|
||||
| `-p, --single <PROMPT>` | The prompt to send (or use `--prompt-json` / `--prompt-file`) |
|
||||
| `-m, --model <MODEL>` | Model to use (e.g., `grok-build`) |
|
||||
| `-s, --session-id <ID>` | Create a **new** session with this **UUID** (errors if invalid UUID or already in use under the target session directory; does not resume — use `-r`/`-c`) |
|
||||
| `--fork-session` | With `-r`/`-c`, fork into a new session ID instead of appending to the original |
|
||||
| `-r, --resume <ID>` | Resume an existing session (errors if not found) |
|
||||
| `-c, --continue` | Continue the most recent session in current directory |
|
||||
| `--cwd <PATH>` | Set working directory |
|
||||
| `--output-format <FMT>` | Output format: `plain`, `json`, `streaming-json` |
|
||||
| `--yolo` | Auto-approve all tool executions |
|
||||
| `--rules <TEXT>` | Custom rules for the system prompt |
|
||||
| `--tools <TOOLS>` | Allowlist of built-in tools (comma-separated). MCP meta-tools remain available unless denied. Headless only. |
|
||||
| `--disallowed-tools <TOOLS>` | Denylist of built-in tools to remove (comma-separated). Supports `Agent` entries. Headless only. |
|
||||
| `--max-turns <N>` | Maximum number of agentic turns before stopping. Headless only. |
|
||||
| `--reasoning-effort` / `--effort <LEVEL>` | Reasoning effort for reasoning models. Canonical levels: `none`, `minimal`, `low`, `medium`, `high`, `xhigh`, `max` (alias of `xhigh`). Also accepts per-model menu option ids (e.g. `deep` → mapped wire value), same as `/effort`. Works in TUI and headless. |
|
||||
| `--permission-mode <MODE>` | Permission mode. `bypassPermissions` enables always-approve via this flag (see [22-permissions-and-safety.md](22-permissions-and-safety.md)); for deny-by-default use `defaultMode` in `.claude/settings.json`. |
|
||||
| `--allow <RULE>` | Permission allow rule with glob patterns (repeatable). Works in TUI and headless. |
|
||||
| `--deny <RULE>` | Permission deny rule with glob patterns (repeatable). Works in TUI and headless. |
|
||||
| `--prompt-json <JSON>` | Prompt as JSON content blocks |
|
||||
| `--prompt-file <PATH>` | Prompt from a file |
|
||||
| `--verbatim` | Send prompt exactly as given |
|
||||
| `--no-auto-update` | Disable update checks for this session |
|
||||
| `--sandbox <PROFILE>` | Sandbox profile for filesystem/network access |
|
||||
|
||||
> **Note:** `--tools`, `--disallowed-tools`, `--max-turns`, and `--agents` are headless-only flags. If used in the interactive TUI, a warning is printed and the flag is ignored. `--reasoning-effort`/`--effort`, `--permission-mode`, `--allow`, and `--deny` work in both modes. For more flags (agents, verification, worktrees), see [Additional Headless Flags](#additional-headless-flags).
|
||||
|
||||
### Tool Filtering
|
||||
|
||||
Use `--tools` to restrict the agent to an explicit set of tools (allowlist), or `--disallowed-tools` to remove specific tools from the default set (denylist). Both accept comma-separated tool names.
|
||||
|
||||
Tool names are internal tool IDs (e.g. the shell tool is `run_terminal_cmd`, not `bash`).
|
||||
|
||||
```bash
|
||||
# Only allow read-only tools
|
||||
grok -p "Explain this codebase" --tools "read_file,grep,list_dir"
|
||||
|
||||
# Remove web access and file editing
|
||||
grok -p "Review this code" --disallowed-tools "web_search,web_fetch,search_replace"
|
||||
|
||||
# Remove shell access
|
||||
grok -p "Review this code" --disallowed-tools "run_terminal_cmd"
|
||||
```
|
||||
|
||||
`--disallowed-tools` also supports special `Agent` entries to control subagent spawning:
|
||||
|
||||
| Entry | Effect |
|
||||
| ---------------------- | --------------------------------------- |
|
||||
| `Agent` | Block all subagent spawning |
|
||||
| `Agent(explore)` | Block the `explore` subagent type only |
|
||||
| `Agent(explore, plan)` | Block multiple specific types |
|
||||
|
||||
```bash
|
||||
# Prevent the agent from spawning any subagents
|
||||
grok -p "Fix this bug" --disallowed-tools "Agent"
|
||||
|
||||
# Block only the explore subagent
|
||||
grok -p "Refactor this module" --disallowed-tools "Agent(explore)"
|
||||
```
|
||||
|
||||
`--tools` preserves the selected agent profile's injection policy: stock profiles inject enabled optional tools before applying the allowlist, while curated profiles remain strict. The final toolset retains requested tools plus always-on MCP meta-tools. When both flags are present, `--disallowed-tools` wins.
|
||||
|
||||
### Permission Rules (`--allow` / `--deny`)
|
||||
|
||||
Permission rules control whether specific tool invocations are auto-approved, denied, or require user confirmation. Unlike `--disallowed-tools` (which removes tools entirely), permission rules leave tools available but gate their execution.
|
||||
|
||||
Rules use `ToolPrefix(glob_pattern)` syntax:
|
||||
|
||||
| Prefix | What it controls |
|
||||
| ------------- | ---------------------------------- |
|
||||
| `Bash(...)` | Shell command execution |
|
||||
| `Edit(...)` | File editing (path glob) |
|
||||
| `Write(...)` | File writing (path glob) |
|
||||
| `Read(...)` | File reading (path glob) |
|
||||
| `Grep(...)` | Search operations (path glob) |
|
||||
| `WebFetch(...)` | URL fetching (glob or `domain:host`) |
|
||||
| `MCPTool(...)` | MCP tool invocations |
|
||||
|
||||
For path rules (`Read`, `Edit`, `Write`, `Grep`), `*` is a single-level wildcard and `**` is recursive. For `Bash` rules, `*` matches any characters including spaces. A bare prefix without parentheses matches all invocations of that type, and `Bash(cmd:*)` is equivalent to prefix matching on `cmd`. See [22-permissions-and-safety.md](22-permissions-and-safety.md#rule-matching-reference) for the full matching semantics.
|
||||
|
||||
```bash
|
||||
# Deny shell commands matching "rm*"
|
||||
grok -p "Clean up this project" --deny "Bash(rm*)"
|
||||
|
||||
# Allow npm commands, deny sudo
|
||||
grok -p "Set up the project" --allow "Bash(npm*)" --deny "Bash(sudo*)"
|
||||
|
||||
# Allow all bash commands (auto-approve without prompting)
|
||||
grok -p "Build the project" --allow "Bash"
|
||||
```
|
||||
|
||||
`--allow` and `--deny` can be repeated. Deny rules take precedence over allow rules.
|
||||
|
||||
---
|
||||
|
||||
## Output Formats
|
||||
|
||||
Headless mode supports three output formats, selected with `--output-format`.
|
||||
|
||||
### plain (default)
|
||||
|
||||
Human-readable text, suitable for direct display or piping:
|
||||
|
||||
```
|
||||
Here's a summary of the codebase...
|
||||
```
|
||||
|
||||
### json
|
||||
|
||||
A single JSON object emitted after the response completes: response text,
|
||||
stop reason, session ID, request ID (plus `thought` when reasoning is present).
|
||||
When the prompt reached the model, the same object also carries spend fields
|
||||
(`usage`, `num_turns`, `modelUsage`, cost).
|
||||
|
||||
```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
|
||||
}
|
||||
```
|
||||
|
||||
Usage notes:
|
||||
|
||||
- `usage` sums tokens for the prompt, including subagents that finished
|
||||
before turn end (also under their own `modelUsage` keys). Compaction and
|
||||
other side-model calls are excluded.
|
||||
- **Token field policy (headless result / `end` / error spend):**
|
||||
- `usage.input_tokens` and `modelUsage.*.inputTokens` are **uncached only**.
|
||||
- `cache_read_input_tokens` / `cacheReadInputTokens` are cache hits.
|
||||
- `total_tokens` is full input + output (includes cache):
|
||||
`total_tokens = input_tokens + cache_read_input_tokens + output_tokens`.
|
||||
- ACP `_meta.usage.inputTokens` (PromptUsage) is still the **full** prompt
|
||||
sum; only the headless projector subtracts cache. Prefer headless fields
|
||||
for spend automation.
|
||||
- `num_turns` counts main-agent model rounds recorded on the prompt ledger
|
||||
(tool-loop rounds that reported usage). Subagent sampler calls do not
|
||||
increase it. Per-model call counts (including subagents) stay on
|
||||
`modelUsage.*.modelCalls`. This is the same counter family as `--max-turns`,
|
||||
not a guarantee of exact equality when rounds lack usage or hit gates.
|
||||
- `total_cost_usd` appears only when the server reported a **complete** cost.
|
||||
Absence means unreported or incomplete, never free. Cost is stamped for
|
||||
API-key traffic today; pool/OAuth paths often omit it until the server
|
||||
stamps cost. When some calls lacked cost, `cost_is_partial` is true and
|
||||
**all** cost floats are omitted (`total_cost_usd` and every
|
||||
`modelUsage.*.costUSD`) so consumers cannot sum model rows into a fake
|
||||
complete bill.
|
||||
- `total_cost_usd_ticks` is the same value in exact integer ticks
|
||||
(1 USD = 10^10 ticks) and appears under the same conditions. Use it for
|
||||
billing reconciliation: summing per-invocation ticks matches the server's
|
||||
usage export exactly, which float dollars cannot guarantee.
|
||||
- When subagent usage could not be applied, nested subagent usage was incomplete,
|
||||
or the success-path drain timed out (up to 120s on the turn task),
|
||||
`usage_is_incomplete` is true and cost floats are omitted the same way
|
||||
(token totals may under-count subagents). Cancel snapshots without that long
|
||||
drain and marks incomplete while subagents are still live. Incomplete with
|
||||
no recorded tokens emits only `usage_is_incomplete` (no zero `usage` object).
|
||||
- A prompt that never reached the model omits the spend fields.
|
||||
|
||||
The `sessionId` field is useful for resuming the conversation later.
|
||||
|
||||
On failure, Grok emits an error object (process exit non-zero). Prompt-level
|
||||
failures may also include frozen spend fields when usage was recorded:
|
||||
|
||||
```json
|
||||
{"type":"error","message":"Couldn't start session: ..."}
|
||||
```
|
||||
|
||||
### streaming-json
|
||||
|
||||
Newline-delimited JSON events emitted in real time. Each line is a self-contained JSON object with a `type` field:
|
||||
|
||||
```json
|
||||
{"type":"text","data":"Here's"}
|
||||
{"type":"text","data":" a summary"}
|
||||
{"type":"thought","data":"Analyzing the directory structure..."}
|
||||
{"type":"end","stopReason":"EndTurn","sessionId":"abc123","requestId":"xyz789","usage":{...},"num_turns":7,"modelUsage":{...}}
|
||||
```
|
||||
|
||||
Event types:
|
||||
|
||||
| Type | Description |
|
||||
| ---------- | -------------------------------------------------------------- |
|
||||
| `text` | A chunk of the agent's response text |
|
||||
| `thought` | Internal reasoning (thinking tokens) |
|
||||
| `end` | Final event with metadata and spend fields when available |
|
||||
| `error` | An error occurred (carries `message`, and spend fields if any) |
|
||||
|
||||
`end` is always the last event. Spend fields on `end` match the json object
|
||||
shape (snake_case uncached `input_tokens`, safe cost floats).
|
||||
|
||||
Grok may also emit `max_turns_reached` and `auto_compact_*` events; treat the list as non-exhaustive and switch on `type`.
|
||||
|
||||
---
|
||||
|
||||
## Session Management in Headless Mode
|
||||
|
||||
By default, each `grok -p` invocation creates a fresh session. To maintain context across calls, use session flags.
|
||||
|
||||
### Named Sessions (`-s`)
|
||||
|
||||
To carry context across headless calls, use `-r/--resume` or `-c/--continue`. Use `-s/--session-id` only for a **new** session with a **UUID** (errors if not a UUID or already in use under the target directory). Older hidden `-s` upsert/resume behavior is gone — use `-r`/`-c` to continue. With `-r`/`-c`, `-s` requires `--fork-session`:
|
||||
|
||||
```bash
|
||||
# Start a headless session and capture its ID
|
||||
grok -p "Review the changes in this PR" --output-format json | jq -r '.sessionId'
|
||||
|
||||
# Continue in the same session
|
||||
grok -p "Now check for security issues" --resume "<id>"
|
||||
|
||||
# Optional: create with a client-chosen UUID (must not already exist)
|
||||
grok -p "hello" --session-id "$(uuidgen | tr '[:upper:]' '[:lower:]')" --output-format json
|
||||
```
|
||||
|
||||
> **Note:** `-s/--session-id` creates a new session only (valid UUID; errors if already in use). Use `-r` to resume.
|
||||
|
||||
### Resume (`-r`)
|
||||
|
||||
The `-r/--resume` flag resumes a specific session by ID. It errors if the session does not exist:
|
||||
|
||||
```bash
|
||||
# Get the session ID from a previous JSON response
|
||||
grok -p "Remember: the secret number is 42" --output-format json
|
||||
# Output includes "sessionId": "abc123"
|
||||
|
||||
# Resume that exact session
|
||||
grok -p "What's the secret number?" --resume abc123
|
||||
```
|
||||
|
||||
### Continue (`-c`)
|
||||
|
||||
The `-c/--continue` flag continues the most recent session in the current working directory:
|
||||
|
||||
```bash
|
||||
grok -p "Continue where we left off" -c
|
||||
```
|
||||
|
||||
### Extracting Session IDs
|
||||
|
||||
Use `--output-format json` and parse the `sessionId` field:
|
||||
|
||||
```bash
|
||||
grok -p "Hello" --output-format json | jq -r '.sessionId'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Piping Input and Output
|
||||
|
||||
Headless mode works naturally with Unix pipes and redirection.
|
||||
|
||||
### Standard Output
|
||||
|
||||
```bash
|
||||
# Pipe output to a file
|
||||
grok -p "Generate a README" > README.md
|
||||
|
||||
# Parse JSON output with jq
|
||||
grok -p "List files" --output-format json | jq -r '.text'
|
||||
```
|
||||
|
||||
### Standard Input
|
||||
|
||||
Headless mode does not read piped stdin into the prompt. Pass external content through command substitution or `--prompt-file`:
|
||||
|
||||
```bash
|
||||
# Include git diff as context via command substitution
|
||||
grok -p "Write a concise commit message for these changes:
|
||||
|
||||
$(git diff --staged)"
|
||||
|
||||
# Or read the prompt from a file
|
||||
grok --prompt-file ./prompt.txt
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## CI/CD Integration Examples
|
||||
|
||||
### Automated Code Review
|
||||
|
||||
```bash
|
||||
grok -p "Review changes for bugs and security issues." \
|
||||
--output-format json --yolo | jq -r '.text' > review.md
|
||||
```
|
||||
|
||||
### Pre-Commit Hook
|
||||
|
||||
```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 Processing
|
||||
|
||||
```bash
|
||||
for file in src/*.js; do
|
||||
grok -p "Migrate $file from CommonJS to ES modules." --yolo
|
||||
done
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Scripting Patterns
|
||||
|
||||
### Python Wrapper
|
||||
|
||||
Grok's headless mode can be wrapped as an OpenAI-compatible chat completion API:
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
import json
|
||||
import os
|
||||
|
||||
class GrokChat:
|
||||
"""Simple OpenAI-compatible wrapper using headless mode."""
|
||||
|
||||
def __init__(self, cwd="."):
|
||||
self.cwd = cwd
|
||||
self.env = {**os.environ}
|
||||
|
||||
def _build_cmd(self, prompt, model, stream):
|
||||
return ["grok", "-p", prompt, "-m", model, "--cwd", self.cwd,
|
||||
"--output-format", "streaming-json" if stream else "json",
|
||||
"--yolo"]
|
||||
|
||||
async def create(self, messages, model="grok-build", stream=False):
|
||||
prompt = messages[-1]["content"] if len(messages) == 1 else "\n".join(
|
||||
f"{m['role']}: {m['content']}" for m in messages
|
||||
)
|
||||
cmd = self._build_cmd(prompt, model, stream)
|
||||
|
||||
if stream:
|
||||
return self._stream(cmd)
|
||||
|
||||
proc = await asyncio.create_subprocess_exec(
|
||||
*cmd, env=self.env, stdout=asyncio.subprocess.PIPE
|
||||
)
|
||||
stdout, _ = await proc.communicate()
|
||||
data = json.loads(stdout.decode()) if stdout else {"text": ""}
|
||||
return {
|
||||
"choices": [{
|
||||
"message": {"role": "assistant", "content": data.get("text", "")},
|
||||
"finish_reason": "stop"
|
||||
}]
|
||||
}
|
||||
|
||||
async def _stream(self, cmd):
|
||||
proc = await asyncio.create_subprocess_exec(
|
||||
*cmd, env=self.env, stdout=asyncio.subprocess.PIPE
|
||||
)
|
||||
async for line in proc.stdout:
|
||||
if not line.strip():
|
||||
continue
|
||||
event = json.loads(line)
|
||||
if event.get("type") == "text":
|
||||
yield {"choices": [{"delta": {"content": event["data"]}}]}
|
||||
elif event.get("type") == "end":
|
||||
yield {"choices": [{"delta": {}, "finish_reason": "stop"}]}
|
||||
|
||||
|
||||
async def main():
|
||||
client = GrokChat(cwd=".")
|
||||
response = await client.create(
|
||||
[{"role": "user", "content": "What files are here?"}]
|
||||
)
|
||||
print(response["choices"][0]["message"]["content"])
|
||||
|
||||
asyncio.run(main())
|
||||
```
|
||||
|
||||
### Shell Script
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# Run a code review and exit with failure if issues are found
|
||||
|
||||
RESULT=$(grok -p "Review this PR for bugs. Output JSON with 'issues' array." \
|
||||
--output-format json --yolo | jq -r '.text')
|
||||
|
||||
ISSUE_COUNT=$(echo "$RESULT" | jq '.issues | length' 2>/dev/null || echo "0")
|
||||
|
||||
if [ "$ISSUE_COUNT" -gt 0 ]; then
|
||||
echo "Found $ISSUE_COUNT issues"
|
||||
echo "$RESULT" | jq '.issues[]'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "No issues found"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Fully Automated Runs with --yolo
|
||||
|
||||
The `--yolo` flag enables always-approve mode (the same mode as `--permission-mode bypassPermissions` and `--always-approve`), auto-approving tool executions (file writes, command execution, etc.) without prompting for confirmation. Explicit `deny` rules and `PreToolUse` hooks still apply, and administrators can disable the mode via `requirements.toml` (see [22-permissions-and-safety.md](22-permissions-and-safety.md)). This is required for unattended automation:
|
||||
|
||||
```bash
|
||||
# Format all files without asking
|
||||
grok -p "Format all files" --yolo
|
||||
|
||||
# Run tests and fix failures
|
||||
grok -p "Run the tests and fix any failures" --cwd ~/projects/my-app --yolo
|
||||
```
|
||||
|
||||
**Use `--yolo` with care.** It grants the agent full autonomy to modify files and run commands. Only use it in trusted environments or with well-scoped prompts.
|
||||
|
||||
---
|
||||
|
||||
## Environment Variables for Headless
|
||||
|
||||
Key environment variables that affect headless mode:
|
||||
|
||||
| Variable | Description |
|
||||
| ------------------------------- | ------------------------------------------------------------- |
|
||||
| `XAI_API_KEY` | API key for authentication (required when no browser login) |
|
||||
| `GROK_HOME` | Override config directory (default: `~/.grok`) |
|
||||
| `GROK_LOG_FILE` | Path to a log file (used verbatim as the path; works in headless and TUI, honors `RUST_LOG`) |
|
||||
| `RUST_LOG` | Log level filter (e.g. `debug`). Headless logs to stderr. |
|
||||
|
||||
For CI environments without browser access, set `XAI_API_KEY` with an API key from [console.x.ai](https://console.x.ai):
|
||||
|
||||
```bash
|
||||
export XAI_API_KEY="xai-..."
|
||||
grok -p "Run the test suite" --yolo
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Exit Codes
|
||||
|
||||
| Code | Meaning |
|
||||
| ---- | ------------------------------------ |
|
||||
| `0` | Success -- prompt completed normally |
|
||||
| `1` | Error -- authentication failure, network error, or runtime error |
|
||||
| `130` | Interrupted by SIGINT (Ctrl+C) |
|
||||
| `143` | Terminated by SIGTERM |
|
||||
|
||||
---
|
||||
|
||||
## Authentication for Headless Environments
|
||||
|
||||
For headless use, authenticate with one of:
|
||||
|
||||
- **`XAI_API_KEY`** — simplest for CI. See [Environment Variables](#environment-variables-for-headless) above.
|
||||
- **`grok login --device-auth`** (or `--device-code`) — no browser needed on the target machine.
|
||||
See [Authentication > Device Code Flow](02-authentication.md#device-code-flow).
|
||||
- **`grok login`** — browser-based OAuth2 on machines with a GUI.
|
||||
|
||||
If you've previously logged in, cached credentials are used automatically.
|
||||
|
||||
---
|
||||
|
||||
## Tips
|
||||
|
||||
- Headless mode starts a **fresh session by default**. Use `-r/--resume` or `-c/--continue` to maintain context across calls.
|
||||
- The `--output-format json` response always includes a `sessionId` you can use with `--resume` for follow-up calls.
|
||||
- Combine `--yolo` with `--rules` to set guardrails: `grok -p "..." --yolo --rules "Never delete files"`.
|
||||
- For debugging, raise the log level and capture stderr: `RUST_LOG=debug grok -p "..." 2> debug.log`.
|
||||
|
||||
---
|
||||
|
||||
## Project Root Discovery
|
||||
|
||||
When Grok starts, it discovers the project root by walking upward from `--cwd`
|
||||
(or the current directory) until it finds a `.git` directory.
|
||||
|
||||
Note: If `--cwd` is nested inside a large repository (such as a monorepo),
|
||||
Grok discovers that repository as the project root and scopes its discovery (AGENTS.md, skills, git history) to it, which can make
|
||||
startup slow. Point `--cwd` at the specific subproject you want to work in to keep
|
||||
the scope small.
|
||||
|
||||
---
|
||||
|
||||
## File Locations
|
||||
|
||||
Grok stores data in `~/.grok` (override with `GROK_HOME`; see [Environment Variables for Headless](#environment-variables-for-headless)):
|
||||
|
||||
| Path | Contents |
|
||||
| ------------------------ | ------------------------------------- |
|
||||
| `config.toml` | User configuration |
|
||||
| `auth.json` | Cached OAuth2/API credentials |
|
||||
| `version.json` | Version cache for update checks |
|
||||
| `sessions/` | Session transcripts (SQLite) |
|
||||
| `memory/` | Cross-session memory store |
|
||||
| `logs/` | Internal log files (for example `unified.jsonl`) |
|
||||
| `logs/mcp/` | MCP server logs |
|
||||
| `skills/` | User skill definitions |
|
||||
| `personas/` | User-scoped agent personas |
|
||||
| `crash/` | Crash reports |
|
||||
| `trace-exports/` | Session trace exports |
|
||||
| `worktrees/` | Git worktree metadata |
|
||||
|
||||
### Read-Only `~/.grok`
|
||||
|
||||
For containers or CI, mount `~/.grok` read-only:
|
||||
|
||||
- Pre-populate `auth.json` or use `XAI_API_KEY`
|
||||
- Session persistence fails silently (ephemeral)
|
||||
- Update checks log a warning and skip
|
||||
|
||||
```bash
|
||||
export XAI_API_KEY="xai-..."
|
||||
export GROK_DISABLE_AUTOUPDATER=1
|
||||
grok -p "..." --no-auto-update
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Update Check Suppression
|
||||
|
||||
| Method | Scope |
|
||||
| ------------------------------- | --------- |
|
||||
| `--no-auto-update` | Session |
|
||||
| `GROK_DISABLE_AUTOUPDATER=1` | Process |
|
||||
| Non-TTY stderr (auto-detected) | Automatic |
|
||||
| `[cli] auto_update = false` | Persistent|
|
||||
|
||||
Update messages go to **stderr**. Stdout stays clean for `--output-format json`. See also [Environment Variables for Headless](#environment-variables-for-headless).
|
||||
|
||||
---
|
||||
|
||||
## Additional Headless Flags
|
||||
|
||||
These flags supplement the [Command-Line Options](#command-line-options) table above. Flags already listed there (`--prompt-json`, `--prompt-file`, `--verbatim`, `--sandbox`, `--no-auto-update`) are not repeated here.
|
||||
|
||||
| Flag | Description |
|
||||
| ----------------------------- | ------------------------------------------------- |
|
||||
| `--agent <NAME>` | Agent name or definition file path |
|
||||
| `--agents <JSON>` | Inline subagent definitions as JSON |
|
||||
| `--system-prompt-override` | Override the agent's system prompt |
|
||||
| `--check` / `--self-verify` | Append verification loop (headless only) |
|
||||
| `--best-of-n <N>` | Run task N ways, pick 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 |
|
||||
| `--no-alt-screen` | Run inline (no alternate screen) |
|
||||
| `--worktree [NAME]` | Start session in a new git worktree |
|
||||
| `--ref <REF>` / `--worktree-ref <REF>` | Branch/tag/commit to base the worktree on (with `--worktree`) |
|
||||
|
||||
---
|
||||
|
||||
## Interrupted Headless Runs
|
||||
|
||||
On SIGINT/SIGTERM:
|
||||
|
||||
- Session state saved up to the last completed tool call
|
||||
- File modifications by tools are **not rolled back**
|
||||
- Exit code is **130** for SIGINT (`128 + 2`) and **143** for SIGTERM (`128 + 15`); CI pipelines can distinguish these from a normal error (exit code `1`)
|
||||
- Resume: `grok -p "continue" --resume "<id>"` or `grok -p "continue" --continue`
|
||||
|
||||
See [Session Management in Headless Mode](#session-management-in-headless-mode) for details on named sessions and the `-s`/`-r`/`-c` flags.
|
||||
282
crates/codegen/xai-grok-pager/docs/user-guide/15-agent-mode.md
Normal file
282
crates/codegen/xai-grok-pager/docs/user-guide/15-agent-mode.md
Normal file
|
|
@ -0,0 +1,282 @@
|
|||
# Agent Mode (ACP) and IDE Integration
|
||||
|
||||
Agent mode runs Grok as an ACP (Agent Client Protocol) server for integration with IDEs, editors, and custom tooling. Unlike single-prompt mode (`grok -p`, which prints one response and exits), agent mode keeps a persistent process running and communicates through structured JSON-RPC messages.
|
||||
|
||||
---
|
||||
|
||||
## What is ACP?
|
||||
|
||||
The [Agent Client Protocol (ACP)](https://agentclientprotocol.com) is a standard for AI agent communication. It defines how clients (IDEs, editors, custom apps) interact with AI agents through a structured JSON-RPC protocol. ACP provides:
|
||||
|
||||
- **Session management** -- create, load, and resume conversations
|
||||
- **Prompt submission** -- send user messages and receive streamed responses
|
||||
- **Tool visibility** -- see what tools the agent is using in real time
|
||||
- **Thought streams** -- observe the agent's reasoning process
|
||||
- **Permission handling** -- approve or deny tool executions interactively
|
||||
|
||||
---
|
||||
|
||||
## stdio transport
|
||||
|
||||
stdio is the primary integration mode. The agent exchanges JSON-RPC messages over stdin and stdout:
|
||||
|
||||
```bash
|
||||
grok agent stdio
|
||||
```
|
||||
|
||||
Clients that use this mode include:
|
||||
|
||||
- IDE extensions (for example, Zed, Neovim, and Emacs)
|
||||
- Custom automation tools
|
||||
- ACP client libraries
|
||||
|
||||
### Options
|
||||
|
||||
These options belong to the `grok agent` command and apply to every mode. Pass them before the mode name, for example `grok agent --model grok-build stdio`. The `stdio` subcommand itself takes no options.
|
||||
|
||||
| Flag | Description |
|
||||
| -------------------------- | ---------------------------------------------------------------- |
|
||||
| `-m, --model <MODEL>` | Set the model ID (for example, `grok-build`). |
|
||||
| `--always-approve` | Auto-approve every tool execution. (Alias: `--yolo`.) |
|
||||
| `--reauth` | Run authentication before starting the agent. |
|
||||
| `--agent-profile <PATH>` | Load an agent profile from a file. |
|
||||
|
||||
---
|
||||
|
||||
## Server mode
|
||||
|
||||
Run the agent as a WebSocket server for remote clients:
|
||||
|
||||
```bash
|
||||
grok agent serve --bind 127.0.0.1:2419 --secret <token>
|
||||
```
|
||||
|
||||
Clients connect over WebSocket and authenticate with the secret token. If you omit `--secret`, the agent generates a token and prints it at startup; you can also supply one through the `GROK_AGENT_SECRET` environment variable. The agent persists across reconnections, so a client can disconnect and later resume in-flight work.
|
||||
|
||||
---
|
||||
|
||||
## WebSocket relay
|
||||
|
||||
To reach the agent over the internet instead of the local network, run a WebSocket relay server and have the agent connect to it:
|
||||
|
||||
```bash
|
||||
grok agent headless --grok-ws-url wss://your-relay.example.com/ws
|
||||
```
|
||||
|
||||
The agent connects out to your relay, and your web clients connect to the same relay. This is useful for building web UIs where browsers cannot spawn local processes.
|
||||
|
||||
---
|
||||
|
||||
## ACP protocol basics
|
||||
|
||||
Communication follows the JSON-RPC 2.0 format. A typical session lifecycle:
|
||||
|
||||
1. **Initialize** -- client sends `initialize` with capabilities
|
||||
2. **Create session** -- client sends `session/new` with working directory
|
||||
3. **Send prompts** -- client sends `session/prompt` with user messages
|
||||
4. **Receive updates** -- agent sends `session/update` notifications with streamed content
|
||||
5. **Handle permissions** -- agent may request tool execution approval
|
||||
|
||||
### Architecture
|
||||
|
||||
```
|
||||
+------------------------------------------+
|
||||
| ACP Client |
|
||||
| (IDE, Editor, Custom Application) |
|
||||
+-------------------+----------------------+
|
||||
| JSON-RPC over stdio
|
||||
+-------------------v----------------------+
|
||||
| grok agent stdio |
|
||||
| |
|
||||
| +---------+ +---------+ +---------+ |
|
||||
| | Session | | Tools | | MCP | |
|
||||
| | Manager | | Registry| | Servers | |
|
||||
| +---------+ +---------+ +---------+ |
|
||||
+------------------------------------------+
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Streaming updates
|
||||
|
||||
ACP streams structured events. Each `session/update` notification carries a `sessionUpdate` field that identifies the update type:
|
||||
|
||||
| `sessionUpdate` value | Description |
|
||||
| --------------------- | ----------------------------------------------------- |
|
||||
| `agent_message_chunk` | A chunk of the agent's response text. |
|
||||
| `agent_thought_chunk` | A chunk of the agent's internal reasoning. |
|
||||
| `tool_call` | A new tool invocation (title, kind, status, input). |
|
||||
| `tool_call_update` | A status or result update for an in-flight tool call. |
|
||||
| `plan` | The agent's execution plan. |
|
||||
|
||||
Each update names its type, so a client can render distinct panels for reasoning, tool calls, and response text.
|
||||
|
||||
---
|
||||
|
||||
## Extension methods
|
||||
|
||||
Beyond the base ACP protocol, Grok defines extension methods under the `x.ai/` prefix for SpaceXAI-specific functionality. These cover:
|
||||
|
||||
| Category | Prefix | Examples |
|
||||
| -------------------------- | -------------------- | ------------------------------------------------ |
|
||||
| **Filesystem** | `x.ai/fs/*` | `list`, `exists`, `read_file`, `write_file` |
|
||||
| **Git** | `x.ai/git/*` | `status`, `stage`, `commit`, `diffs`, `discard` |
|
||||
| **Git Worktree** | `x.ai/git/worktree/*`| `create`, `remove`, `apply`, `list`, `gc` |
|
||||
| **Search** | `x.ai/search/*` | `fuzzy/open`, `fuzzy/change`, `content` |
|
||||
| **Terminal** | `x.ai/terminal/*` | `create`, `kill`, `output`, `wait_for_exit` |
|
||||
| **Session Management** | `x.ai/session/*` | `fork`, `resolve_local_for_worktree_resume` |
|
||||
| **Conversation & History** | `x.ai/*` | `prompt_history`, `rewind/*`, `compact_conversation` |
|
||||
| **Authentication** | `x.ai/auth/*` | `get_url`, `submit_code` |
|
||||
| **Feedback & Telemetry** | `x.ai/*` | `feedback`, `telemetry/*` |
|
||||
|
||||
The tables here show representative methods in each category. The `x.ai/*` set is SpaceXAI-specific and may expand across releases, so treat it as non-exhaustive and discover the available methods from the agent's `initialize` response.
|
||||
|
||||
### Notifications (agent to client)
|
||||
|
||||
The agent sends push notifications to clients for real-time updates:
|
||||
|
||||
| Notification | Description |
|
||||
| -------------------------- | ------------------------------------ |
|
||||
| `x.ai/search/fuzzy/status` | Fuzzy search results update |
|
||||
| `x.ai/git/worktree/status` | Worktree creation progress |
|
||||
| `x.ai/fs_notify` | Filesystem change notification |
|
||||
| `x.ai/fs/index` | Full file index update |
|
||||
| `x.ai/fs/index/delta` | Incremental file index update |
|
||||
| `x.ai/session_notification`| Session-specific updates (diff review, retry state, auto-compact) |
|
||||
| `x.ai/session/update` | Session update (tool calls, content) |
|
||||
|
||||
---
|
||||
|
||||
## Session `_meta` options
|
||||
|
||||
The `session/new` request accepts these optional `_meta` fields:
|
||||
|
||||
| Field | Description |
|
||||
| ---------------------- | ---------------------------------------------- |
|
||||
| `rules` | Extra rules appended to the system prompt. |
|
||||
| `systemPromptOverride` | A replacement system prompt. |
|
||||
| `agentProfile` | An agent profile, as a name or a JSON object. |
|
||||
|
||||
---
|
||||
|
||||
## ACP SDKs
|
||||
|
||||
Official SDK libraries are available for multiple languages:
|
||||
|
||||
| Language | Package |
|
||||
| ---------- | ---------------------------------------------------------------------------------------- |
|
||||
| TypeScript | [`@agentclientprotocol/sdk`](https://www.npmjs.com/package/@agentclientprotocol/sdk) |
|
||||
| Rust | [`agent-client-protocol`](https://crates.io/crates/agent-client-protocol) |
|
||||
| Python | [`agent-client-protocol-python`](https://github.com/PsiACE/agent-client-protocol-python) |
|
||||
| Go | [`acp-go-sdk`](https://github.com/coder/acp-go-sdk) |
|
||||
| Kotlin | [`acp`](https://github.com/agentclientprotocol/kotlin-sdk) |
|
||||
|
||||
---
|
||||
|
||||
## Compatible clients
|
||||
|
||||
| Client | Status |
|
||||
| -------------------------------------------------------- | ----------- |
|
||||
| [Zed](https://zed.dev/docs/ai/external-agents) | Supported |
|
||||
| [Neovim](https://neovim.io) (CodeCompanion, avante.nvim) | Supported |
|
||||
| [Emacs](https://github.com/xenodium/agent-shell) | Supported |
|
||||
| [marimo notebook](https://github.com/marimo-team/marimo) | Supported |
|
||||
| JetBrains | Coming soon |
|
||||
|
||||
---
|
||||
|
||||
## Integration example: a TypeScript ACP client
|
||||
|
||||
```typescript
|
||||
import { spawn, ChildProcess } from "child_process";
|
||||
import * as readline from "readline";
|
||||
|
||||
class GrokACPChat {
|
||||
private proc!: ChildProcess;
|
||||
private sessionId!: string;
|
||||
private rl!: readline.Interface;
|
||||
|
||||
constructor(private cwd = ".") {}
|
||||
|
||||
async init() {
|
||||
this.proc = spawn("grok", ["agent", "stdio"]);
|
||||
this.rl = readline.createInterface({ input: this.proc.stdout! });
|
||||
|
||||
// Initialize
|
||||
await this.request("initialize", {
|
||||
protocolVersion: 1,
|
||||
clientCapabilities: {
|
||||
fs: { readTextFile: true, writeTextFile: true },
|
||||
terminal: true,
|
||||
},
|
||||
});
|
||||
|
||||
// Create session
|
||||
const { sessionId } = await this.request("session/new", {
|
||||
cwd: this.cwd,
|
||||
mcpServers: [],
|
||||
});
|
||||
this.sessionId = sessionId;
|
||||
return this;
|
||||
}
|
||||
|
||||
private async request(method: string, params: any): Promise<any> {
|
||||
return new Promise((resolve) => {
|
||||
const msg = JSON.stringify({ jsonrpc: "2.0", id: 1, method, params });
|
||||
this.proc.stdin!.write(msg + "\n");
|
||||
|
||||
this.rl.once("line", (line) => {
|
||||
resolve(JSON.parse(line).result || {});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async *streamPrompt(text: string) {
|
||||
const msg = JSON.stringify({
|
||||
jsonrpc: "2.0",
|
||||
id: 1,
|
||||
method: "session/prompt",
|
||||
params: {
|
||||
sessionId: this.sessionId,
|
||||
prompt: [{ type: "text", text }],
|
||||
},
|
||||
});
|
||||
this.proc.stdin!.write(msg + "\n");
|
||||
|
||||
for await (const line of this.rl) {
|
||||
const data = JSON.parse(line);
|
||||
|
||||
if (data.method === "session/update") {
|
||||
const update = data.params.update;
|
||||
yield update; // { sessionUpdate, content, title, ... }
|
||||
} else if (data.result) {
|
||||
break; // Final response
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Usage
|
||||
const client = await new GrokACPChat(".").init();
|
||||
|
||||
for await (const update of client.streamPrompt("List the files in this project")) {
|
||||
switch (update.sessionUpdate) {
|
||||
case "agent_message_chunk":
|
||||
process.stdout.write(update.content?.text || "");
|
||||
break;
|
||||
case "agent_thought_chunk":
|
||||
console.log(`\n[Thinking: ${update.content?.text}]`);
|
||||
break;
|
||||
case "tool_call":
|
||||
console.log(`\n[Tool: ${update.title}]`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Resources
|
||||
|
||||
- [ACP Specification](https://agentclientprotocol.com/protocol/prompt-turn)
|
||||
- [Protocol Introduction](https://agentclientprotocol.com/overview/introduction)
|
||||
306
crates/codegen/xai-grok-pager/docs/user-guide/16-subagents.md
Normal file
306
crates/codegen/xai-grok-pager/docs/user-guide/16-subagents.md
Normal file
|
|
@ -0,0 +1,306 @@
|
|||
# Subagents and Personas
|
||||
|
||||
Subagents are independent child sessions that handle tasks in parallel. Each subagent has its own context window, so the main agent can delegate work (research, implementation, testing, and code review) without consuming its own context. A subagent reports a summary back to the parent when it finishes.
|
||||
|
||||
Subagents are enabled by default.
|
||||
|
||||
---
|
||||
|
||||
## Agents vs Personas
|
||||
|
||||
Agents and personas both customize behavior, but they operate at different levels:
|
||||
|
||||
| | **Agents** | **Personas** |
|
||||
|---|---|---|
|
||||
| **What they configure** | The whole session: model, tools, prompt mode, system prompt | A behavioral overlay added to a subagent's prompt |
|
||||
| **Scope** | Primary session or subagent | Subagents only |
|
||||
| **How you set them** | At startup, or with agent definitions (`.md` files in `.grok/agents/` or `~/.grok/agents/`) | In `config.toml` (`[subagents.personas]`) or `.toml` files under `.grok/personas/`; applied during subagent resolution |
|
||||
| **What they control** | Model, tool availability, prompt body, skills | Tone, output format, task focus, and input/output contracts |
|
||||
| **Who edits them** | You -- create, delete, or toggle them in the agents modal or by editing files | You -- define custom personas in config or files; bundled personas are read-only |
|
||||
| **Examples** | `grok-build`, `explore`, `plan` | `researcher`, `concise` |
|
||||
|
||||
An agent defines the session itself. A persona shapes how a subagent behaves within a session. A subagent always runs as an agent type (for example, `general-purpose`), and resolution can layer a persona on top.
|
||||
|
||||
Manage both in the agents modal. Open it with `/config-agents` (alias `/agents`), or open the Personas tab directly with `/personas`. The modal has two tabs: **Agents** and **Personas**.
|
||||
|
||||
---
|
||||
|
||||
## Disabling Subagents
|
||||
|
||||
Disable subagents with an environment variable or the config file:
|
||||
|
||||
```bash
|
||||
export GROK_SUBAGENTS=0 # Environment variable
|
||||
```
|
||||
|
||||
```toml
|
||||
# ~/.grok/config.toml
|
||||
[subagents]
|
||||
enabled = false
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## How Subagents Work
|
||||
|
||||
When the main agent identifies work to delegate, it calls the `spawn_subagent` tool to start a child session. The child runs with:
|
||||
|
||||
- Its own context window, independent of the parent
|
||||
- A toolset determined by its agent type and optional capability mode
|
||||
- Optional persona instructions applied during resolution
|
||||
|
||||
The parent receives the child's output -- usually a summary -- when the child finishes.
|
||||
|
||||
---
|
||||
|
||||
## Built-in Agent Types
|
||||
|
||||
The `spawn_subagent` tool accepts a `subagent_type` parameter that selects the child's role:
|
||||
|
||||
| Type | Description |
|
||||
| ----------------- | ---------------------------------------------------- |
|
||||
| `general-purpose` | Default type. Full-capability agent for any task. |
|
||||
| `explore` | Research agent. Searches, reads, greps, and runs shell commands, but does not edit files. Use it for codebase investigation. |
|
||||
| `plan` | Planning agent. Explores the codebase and produces a structured implementation plan; does not edit files. |
|
||||
|
||||
Project- or user-defined agents can add new types or shadow these built-ins by name.
|
||||
|
||||
---
|
||||
|
||||
## Personas
|
||||
|
||||
A persona is a named behavioral overlay. Its instructions are injected into the subagent's conversation as a `<system-reminder>`, which shapes tone, output format, and task focus without changing the subagent's agent type, model, or tools.
|
||||
|
||||
Define personas in `config.toml` or in `.toml` files:
|
||||
|
||||
```toml
|
||||
[subagents.personas.researcher]
|
||||
instructions = "You are a thorough researcher. Always cite specific file paths."
|
||||
description = "Deep investigator."
|
||||
```
|
||||
|
||||
Grok Build discovers file-based personas from these locations, in priority order:
|
||||
|
||||
- `.grok/personas/*.toml` (project)
|
||||
- `~/.grok/personas/*.toml` (user)
|
||||
- The bundled personas directory (lowest priority)
|
||||
|
||||
Each file defines one persona, and the file name (without the extension) becomes the persona name. Inline `config.toml` personas take precedence over files. Only `.toml` files are discovered.
|
||||
|
||||
Manage personas in the Personas tab of the agents modal (`/personas`). Bundled personas are read-only; personas you define are editable.
|
||||
|
||||
> **Note:** Grok Build applies personas through subagent resolution and roles, not through a `spawn_subagent` parameter. The main agent does not pass a persona name when it spawns a child.
|
||||
|
||||
### Persona Fields
|
||||
|
||||
| Field | Description |
|
||||
| ------------------- | ------------------------------------------------------------------- |
|
||||
| `instructions` | Inline instruction text applied as the persona layer. |
|
||||
| `instructions_file` | Path to an instruction file, loaded at spawn time and merged after `instructions`. |
|
||||
| `description` | Short summary shown in the persona catalog. Falls back to the first paragraph of `instructions`. |
|
||||
| `inputs` / `outputs`| Declared input and output contract (see below). |
|
||||
| `model` | Model override applied when the persona is used. |
|
||||
| `reasoning_effort` | Reasoning effort applied when the persona is used. |
|
||||
| `default_isolation` | Default isolation mode (`none` or `worktree`). |
|
||||
|
||||
### Input/Output Contracts
|
||||
|
||||
A persona can declare the inputs it expects and the outputs it produces. The parent agent reads these to know what context to supply and what artifacts to expect. This lets you chain personas, so one persona's output file becomes the next persona's input:
|
||||
|
||||
```toml
|
||||
[[subagents.personas.reviewer.inputs]]
|
||||
name = "review_file"
|
||||
io_type = "file"
|
||||
required = true
|
||||
description = "Path to the code under review"
|
||||
|
||||
[[subagents.personas.reviewer.outputs]]
|
||||
name = "summary_file"
|
||||
io_type = "file"
|
||||
required = false
|
||||
description = "Path to write review notes"
|
||||
```
|
||||
|
||||
Each field has a `name`, an `io_type` (defaults to `file`), a `required` flag, and a `description`.
|
||||
|
||||
### Persona Resolution
|
||||
|
||||
When a persona applies, Grok Build resolves the effective model and reasoning effort in this order, highest priority first:
|
||||
|
||||
1. Explicit spawn-time override
|
||||
2. Role default
|
||||
3. Persona default
|
||||
4. Parent session
|
||||
|
||||
Isolation follows the same order for the first three steps but defaults to `none` (no worktree) rather than inheriting from the parent session.
|
||||
|
||||
If a persona is requested but cannot be resolved -- it is not found, has no instructions, or its `instructions_file` is unreadable -- the spawn fails.
|
||||
|
||||
---
|
||||
|
||||
## Spawning Subagents
|
||||
|
||||
The main agent calls the `spawn_subagent` tool. Its parameters:
|
||||
|
||||
| Parameter | Description |
|
||||
| ----------------- | ---------------------------------------------------------------- |
|
||||
| `prompt` | The full task prompt for the subagent. |
|
||||
| `description` | A short label for the task (3-5 words). |
|
||||
| `subagent_type` | The agent type to launch. Defaults to `general-purpose`. |
|
||||
| `background` | Run the subagent in the background and return immediately with a subagent ID. Defaults to `false`. |
|
||||
| `capability_mode` | Restrict the subagent's tools: `read-only`, `read-write`, `execute`, or `all`. |
|
||||
| `isolation` | `none` (shared workspace, the default) or `worktree` (isolated git worktree). |
|
||||
| `resume_from` | Continue a completed subagent's conversation. Pass its subagent ID. |
|
||||
| `cwd` | Working directory for the subagent. Mutually exclusive with `isolation: worktree`; ignored when `resume_from` is set (the resumed child inherits its source's directory). |
|
||||
|
||||
When you run a subagent in the background, retrieve its result later with `get_command_or_subagent_output`.
|
||||
|
||||
---
|
||||
|
||||
## Capability Modes
|
||||
|
||||
A capability mode is an optional, coarse filter on a subagent's tools:
|
||||
|
||||
| Mode | Read | Write | Execute | Description |
|
||||
| ------------ | ---- | ----- | ------- | -------------------------------------------- |
|
||||
| `read-only` | Yes | No | No | Read, search, and inspect (also web search and LSP); no file edits or shell. |
|
||||
| `read-write` | Yes | Yes | No | Read, plus create, edit, delete, and move files. No shell. |
|
||||
| `execute` | Yes | No | Yes | Read, plus run shell commands and background tasks. No file edits. |
|
||||
| `all` | Yes | Yes | Yes | Unrestricted tool access. |
|
||||
|
||||
If you omit `capability_mode`, the subagent uses its agent type's toolset. The built-in `explore` and `plan` types read, search, and run shell commands but cannot edit files; `general-purpose` ships the full toolset.
|
||||
|
||||
---
|
||||
|
||||
## Context Inheritance
|
||||
|
||||
### resume_from
|
||||
|
||||
The `resume_from` parameter lets a new subagent continue where a completed subagent left off, which is useful for multi-stage workflows:
|
||||
|
||||
1. Spawn a research subagent to investigate a problem.
|
||||
2. Spawn a second subagent with `resume_from` set to the first subagent's ID, so it picks up with the full research context.
|
||||
|
||||
The new subagent inherits the source's transcript, tool state, and model; its system prompt and tools are re-rendered from the current agent definition. The source must be completed (not running), belong to the current session, and use the same agent type.
|
||||
|
||||
---
|
||||
|
||||
## Isolation: Worktree Mode
|
||||
|
||||
For tasks that modify files, run a subagent in an isolated git worktree with `isolation: worktree`. This keeps the child's edits from conflicting with the parent's:
|
||||
|
||||
- The subagent works in its own copy of the working tree.
|
||||
- Its changes stay isolated from the parent until you merge them.
|
||||
- The subagent's result includes the worktree path.
|
||||
|
||||
Grok Build manages worktrees through the `x.ai/git/worktree/*` extension methods, including an apply operation that merges changes back into the main working directory.
|
||||
|
||||
---
|
||||
|
||||
## Configuration
|
||||
|
||||
### Per-Type Toggles and Model Overrides
|
||||
|
||||
Disable specific agent types, or route them to a different model:
|
||||
|
||||
```toml
|
||||
[subagents.toggle]
|
||||
explore = true # default -- omit to keep enabled
|
||||
plan = false # disable the plan subagent
|
||||
|
||||
[subagents.models]
|
||||
explore = "grok-build" # route explore to a specific model
|
||||
```
|
||||
|
||||
Per-type model overrides apply for any parent. Without an override, a subagent inherits the parent's model.
|
||||
|
||||
### Custom Roles and Personas
|
||||
|
||||
Define custom roles with their own capability and model defaults:
|
||||
|
||||
```toml
|
||||
[subagents.roles.researcher]
|
||||
description = "Deep research agent"
|
||||
default_capability_mode = "read-only"
|
||||
model = "grok-build"
|
||||
prompt_file = ".grok/prompts/researcher.md"
|
||||
```
|
||||
|
||||
Define custom personas with behavioral instructions:
|
||||
|
||||
```toml
|
||||
[subagents.personas.concise]
|
||||
instructions = "Be concise. No filler words."
|
||||
# instructions_file = ".grok/personas/concise.md" # or load from a file
|
||||
```
|
||||
|
||||
Grok Build also discovers roles from `.grok/roles/*.toml` and personas from `.grok/personas/*.toml`. Inline `config.toml` definitions take precedence over files.
|
||||
|
||||
---
|
||||
|
||||
## The Tasks Pane (TUI)
|
||||
|
||||
Grok Build shows running and finished work in side panes on the agent screen:
|
||||
|
||||
- Press `Ctrl+B` to toggle the tasks pane, which lists active and completed subagents and background commands with their status.
|
||||
- Press `Ctrl+T` to toggle the separate todo pane.
|
||||
|
||||
To view the available agent types and personas, open the command palette with `Ctrl+P` and choose **Manage Agents** (`/config-agents`).
|
||||
|
||||
Subagents appear at the top of the tasks pane in their own collapsible "Subagents" group.
|
||||
|
||||
---
|
||||
|
||||
## Viewing Subagents in the TUI
|
||||
|
||||
Subagents appear in several places in the interactive TUI:
|
||||
|
||||
### Scrollback (parent conversation history)
|
||||
|
||||
When a subagent is spawned, a compact lifecycle block is added to the *parent's* scrollback:
|
||||
|
||||
- `Subagent running: "do the thing" (Implementer · grok-3) — Thinking`
|
||||
- Or for background subagents: `Subagent started: "..."`
|
||||
|
||||
While running, the block shows a live activity suffix (e.g. "Running: cargo test", "Compacting", "Retrying (2/3)") pulled from the child's turn tracker. The bullet animates (or is colored) according to state.
|
||||
|
||||
Press **Enter** (or Ctrl-F) on the block to open the subagent's full transcript.
|
||||
|
||||
For blocking subagents the single entry updates its bullet color when the child finishes. For background ones, a follow-up `Subagent completed/failed/cancelled in Xs: "..."` block is appended.
|
||||
|
||||
### Tasks pane (Ctrl+B)
|
||||
|
||||
As noted above — grouped under "Subagents", with spinners, elapsed times, and quick access to kill or inspect.
|
||||
|
||||
### Fullscreen framed view (the child transcript)
|
||||
|
||||
When you open a subagent (from a scrollback block or the tasks pane), the parent view is replaced by a bordered frame containing the child's full transcript:
|
||||
|
||||
- Title bar inside the frame: status icon (spinner / ✓ / ✗), label + bold description + model, optional "resumed"/"forked" badge, live activity · elapsed time, and [✗] close button.
|
||||
- The child's own scrollback, thinking, tool calls, and (limited) prompt area render inside the frame.
|
||||
- Subagent views are largely observational — you generally cannot send new top-level prompts directly to them the way you can a parent session.
|
||||
|
||||
Use `q`, `Esc`, or click the close button to pop back to the parent view. The parent's scrollback continues to show the subagent's status.
|
||||
|
||||
---
|
||||
|
||||
## Depth Limits
|
||||
|
||||
Only the top-level session spawns subagents. A subagent cannot spawn its own subagents: the maximum nesting depth is one. If a subagent calls `spawn_subagent`, the call fails with a depth-limit error. This keeps the agent tree flat and prevents runaway spawning.
|
||||
|
||||
---
|
||||
|
||||
## When to Use Subagents
|
||||
|
||||
**Good use cases:**
|
||||
|
||||
- Researching a codebase while the parent continues other work
|
||||
- Running tests in parallel while the parent implements changes
|
||||
- Reviewing generated changes before you commit them
|
||||
- Delegating independent tasks that do not depend on each other
|
||||
|
||||
**When not to use:**
|
||||
|
||||
- Simple tasks that the parent can handle directly
|
||||
- Tasks that require tight back-and-forth with the user, since a subagent runs autonomously and isn't suited to interactive exchanges
|
||||
- Tasks where the context setup cost exceeds the parallelism benefit
|
||||
300
crates/codegen/xai-grok-pager/docs/user-guide/17-sessions.md
Normal file
300
crates/codegen/xai-grok-pager/docs/user-guide/17-sessions.md
Normal file
|
|
@ -0,0 +1,300 @@
|
|||
# Session Management
|
||||
|
||||
Grok saves every conversation to disk automatically. Whether you work in the TUI, in headless mode, or over agent stdio, Grok records the exchange as a session. You can resume, rewind, or compact it. This document describes how to manage sessions.
|
||||
|
||||
---
|
||||
|
||||
## What Sessions Are
|
||||
|
||||
A session is a persistent conversation with full history. It includes:
|
||||
|
||||
- All user prompts and agent responses
|
||||
- Tool calls and their results
|
||||
- TODO/task list state
|
||||
- File snapshots for rewind
|
||||
- Token usage and turn counts
|
||||
- Subagent sessions (when enabled)
|
||||
|
||||
Sessions are identified by a unique session ID (a UUIDv7 when Grok generates it; a client may supply its own ID with `-s`) and stored on disk under `~/.grok/sessions/`. Set `GROK_HOME` to override the base directory; when it is unset, Grok uses `~/.grok`.
|
||||
|
||||
---
|
||||
|
||||
## Storage Layout
|
||||
|
||||
Grok stores each session in its own directory, grouped by working directory. It URL-encodes the working directory to name the group. When the encoded name exceeds 255 bytes, it instead uses a slug plus a hash and records the original path in a `.cwd` file inside the group.
|
||||
|
||||
```
|
||||
~/.grok/sessions/<encoded-cwd>/<session-id>/
|
||||
summary.json # metadata: summary/title, timestamps, model ID, message counts
|
||||
updates.jsonl # ACP session update stream (conversation + tool calls)
|
||||
chat_history.jsonl # raw chat messages sent to the model
|
||||
plan.json # TODO/task list state
|
||||
rewind_points.jsonl # file snapshots for /rewind undo
|
||||
signals.json # session signals (token usage, tool/turn counters)
|
||||
feedback.jsonl # user feedback and ratings
|
||||
compaction_checkpoints/ # saved state from compaction (manual or auto)
|
||||
subagents/ # per-subagent metadata (meta.json); the child sessions live in the normal sessions tree
|
||||
```
|
||||
|
||||
`summary.json` is the index entry. It records the session summary and generated title, the model ID, the creation and update timestamps, the message counts, and a parent session reference for forked or restored sessions. `updates.jsonl` is the authoritative conversation log that drives `/resume` and session restore.
|
||||
|
||||
---
|
||||
|
||||
## Starting and Ending Sessions
|
||||
|
||||
### New Session
|
||||
|
||||
The TUI creates a new session each time you launch. To explicitly start fresh mid-session:
|
||||
|
||||
```
|
||||
/new
|
||||
```
|
||||
|
||||
This clears the current context and begins a new conversation. Alias: `/clear`.
|
||||
|
||||
### Exit
|
||||
|
||||
End the session and quit Grok:
|
||||
|
||||
```
|
||||
/quit
|
||||
```
|
||||
|
||||
Alias: `/exit`. To leave the current session but stay in Grok, use `/home` to return to the welcome screen.
|
||||
|
||||
---
|
||||
|
||||
## Resuming Sessions
|
||||
|
||||
### From the TUI
|
||||
|
||||
Use the `/resume` command to browse and resume previous sessions:
|
||||
|
||||
```
|
||||
/resume
|
||||
```
|
||||
|
||||
This opens a session picker that lists recent sessions for the current workspace. Select a session to resume it. The command takes no arguments.
|
||||
|
||||
Typing in the picker filters the list by title and also searches your conversation content as you type; content matches appear under an "Extended search results" heading. Press `Ctrl+/` to search immediately without the brief pause.
|
||||
|
||||
To switch between, rename, or close the sessions that are currently active (the parent session and any forks), use `/dashboard` (or its alias `/sessions`) instead.
|
||||
|
||||
### From the Command Line
|
||||
|
||||
Resume a specific session by ID:
|
||||
|
||||
```bash
|
||||
grok --resume <session-id>
|
||||
```
|
||||
|
||||
Run `grok --resume` without an ID to resume the most recent session for the current directory.
|
||||
|
||||
### From the Welcome Screen
|
||||
|
||||
When you launch `grok`, the welcome screen lists recent sessions for the current directory. Select one to resume it.
|
||||
|
||||
---
|
||||
|
||||
## Forking and Renaming Sessions
|
||||
|
||||
### Fork
|
||||
|
||||
Branch the current session into a peer agent that starts from a copy of the conversation:
|
||||
|
||||
```
|
||||
/fork [--worktree|--no-worktree] [directive]
|
||||
```
|
||||
|
||||
Pass an optional `directive` to set the new session's first prompt. Use `--worktree` or `--no-worktree` to choose whether the fork runs in a new git worktree; omit both to be asked each time. The `--at <turn>` flag is not supported in this version.
|
||||
|
||||
### Rename
|
||||
|
||||
Rename the current session's title:
|
||||
|
||||
```
|
||||
/rename <title>
|
||||
```
|
||||
|
||||
Alias: `/title`.
|
||||
|
||||
---
|
||||
|
||||
## The /rewind Command
|
||||
|
||||
`/rewind` undoes recent changes by restoring files to their state at an earlier point in the conversation. Use it to recover from mistakes.
|
||||
|
||||
```
|
||||
/rewind
|
||||
```
|
||||
|
||||
When you run `/rewind` (or press **Esc Esc** within 800ms while idle with an empty prompt and conversation messages), Grok:
|
||||
|
||||
1. Shows a list of rewind points (one per user prompt)
|
||||
2. Lets you select which point to rewind to
|
||||
3. Restores all files to their state at that point
|
||||
4. Truncates the conversation history to that point
|
||||
|
||||
File snapshots are recorded at each prompt, so you can go back to any previous state.
|
||||
|
||||
**Important:** `/rewind` modifies files on disk. The changes it reverts are lost unless you have them in git.
|
||||
|
||||
---
|
||||
|
||||
## The /compact Command
|
||||
|
||||
`/compact` compresses the conversation history to save context window space. Use it in long sessions where early messages are no longer relevant.
|
||||
|
||||
```
|
||||
/compact
|
||||
/compact [context]
|
||||
```
|
||||
|
||||
The optional `context` argument lets you provide additional instructions about what to preserve during compaction.
|
||||
|
||||
### Auto-Compact
|
||||
|
||||
Grok automatically compacts the conversation when the context window approaches its limit. You will see a notification when auto-compact triggers. The `context_window` setting on your model configuration controls when this threshold is reached.
|
||||
|
||||
---
|
||||
|
||||
## The /session-info Command
|
||||
|
||||
View details about the current session:
|
||||
|
||||
```
|
||||
/session-info
|
||||
```
|
||||
|
||||
This shows:
|
||||
|
||||
- Session title (when set)
|
||||
- Shell version
|
||||
- Session ID
|
||||
- Working directory
|
||||
- Model (with a model hash for coding models)
|
||||
- API backend and sandbox profile (when set)
|
||||
- Context window usage (used and total tokens, with the percentage used)
|
||||
|
||||
---
|
||||
|
||||
## Headless Session Management
|
||||
|
||||
In headless mode, you manage sessions through command-line flags:
|
||||
|
||||
```bash
|
||||
# New session each time (default)
|
||||
grok -p "Hello"
|
||||
|
||||
# Resume an existing session by ID (errors if it does not exist)
|
||||
grok -p "Continue where we left off" -r <session-id>
|
||||
|
||||
# Continue the most recent session in the current directory
|
||||
grok -p "What were we doing?" -c
|
||||
```
|
||||
|
||||
In headless mode, resume an existing session with `-r`/`--resume`, which errors if the session does not exist, or continue the most recent session in the current directory with `-c`/`--continue`. Pass the session ID from JSON output (see below) to `-r`.
|
||||
|
||||
Use `-s`/`--session-id` only to **create** a new session with a **UUID** (errors if the value is not a UUID, or if that ID already has a session under the target session directory). It does **not** resume an existing session — that was the old hidden upsert behavior; use `-r`/`-c` instead. Combine `-s` with `-r`/`-c` only when also passing `--fork-session` (forks history into a new ID; optional `-s` names the child UUID). This matches Claude Code’s anti-overwrite model (client preflight under the write cwd; sequential use is reliable, concurrent same-ID is best-effort).
|
||||
|
||||
To read the session ID back, request JSON output:
|
||||
|
||||
```bash
|
||||
grok -p "Hello" --output-format json | jq -r '.sessionId'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Agent stdio Session Management
|
||||
|
||||
When building with ACP, sessions are managed via protocol methods:
|
||||
|
||||
```typescript
|
||||
// Create new session
|
||||
const { sessionId } = await connection.request("session/new", {
|
||||
cwd: "/path/to/project",
|
||||
mcpServers: [],
|
||||
});
|
||||
|
||||
// Load existing session
|
||||
await connection.request("session/load", {
|
||||
sessionId: "existing-session-id",
|
||||
cwd: "/path/to/project",
|
||||
mcpServers: [],
|
||||
});
|
||||
```
|
||||
|
||||
The agent persists all session updates automatically. Clients can reconnect and load previous sessions by ID.
|
||||
|
||||
---
|
||||
|
||||
## The grok sessions Subcommand
|
||||
|
||||
List or search sessions from the command line. `grok sessions` requires a subcommand:
|
||||
|
||||
```bash
|
||||
# List recent sessions for the current directory
|
||||
grok sessions list
|
||||
|
||||
# Limit the number of results (default 20)
|
||||
grok sessions list --limit 50
|
||||
|
||||
# Search sessions by keyword (matches titles and prompts)
|
||||
grok sessions search "rate limit"
|
||||
```
|
||||
|
||||
`grok sessions list` shows sessions for the current working directory, grouped by worktree label. Each row lists the session ID, the creation and update dates, the source status, and the summary. `grok sessions search` combines a local SQLite index with remote results.
|
||||
|
||||
---
|
||||
|
||||
## Worktree Sessions
|
||||
|
||||
When working with subagents or session forks, Grok can create isolated git worktrees per session. Each worktree gets its own copy of the working directory, so file changes in one session do not affect another.
|
||||
|
||||
Worktree sessions are managed internally through the `x.ai/git/worktree/*` extension methods. Key operations:
|
||||
|
||||
- **Create**: Create a new worktree for an isolated session
|
||||
- **Apply**: Merge worktree changes back into the main working directory
|
||||
- **Remove**: Clean up a worktree when the session is done
|
||||
|
||||
Resume a session in a fresh worktree with `grok -w -r <session-id>`.
|
||||
|
||||
---
|
||||
|
||||
## Session Storage Details
|
||||
|
||||
### Persistence Format
|
||||
|
||||
Grok stores the conversation as newline-delimited JSON (JSONL). Each line in `updates.jsonl` is a self-contained ACP session update event. This format supports:
|
||||
|
||||
- Incremental writes (append-only during a session)
|
||||
- Efficient streaming reads (for session restore)
|
||||
- Easy debugging (each line is valid JSON)
|
||||
|
||||
The smaller state files -- `summary.json`, `plan.json`, and `signals.json` -- are plain JSON rather than JSONL. JSONL is the source of truth for session content; `grok sessions search` additionally maintains a local SQLite FTS5 index over session titles and prompts for fast keyword search.
|
||||
|
||||
### Session Metadata
|
||||
|
||||
`summary.json` records, among other fields:
|
||||
|
||||
- `info` -- the session ID and working directory
|
||||
- `session_summary` and `generated_title` -- the session summary and its model-generated title
|
||||
- `created_at` and `updated_at` -- creation and last-update timestamps
|
||||
- `num_messages` and `num_chat_messages` -- update and chat-message counts
|
||||
- `current_model_id` -- the model in use
|
||||
- `parent_session_id` -- the source session for a fork or restore
|
||||
- `agent_name` -- the agent definition active when the session was last saved
|
||||
|
||||
### Disk Usage
|
||||
|
||||
Rewind point snapshots (copies of modified files) are the largest contributor to disk usage in sessions that modify many files. Use `/compact` to reduce history size.
|
||||
|
||||
---
|
||||
|
||||
## Tips
|
||||
|
||||
- Use `/new` to start fresh when your current context is no longer relevant.
|
||||
- Use `/compact` proactively in long sessions to keep the context window effective.
|
||||
- Use `/rewind` to undo mistakes; it restores actual file snapshots instead of relying on the agent to reconstruct earlier state.
|
||||
- In headless mode, capture the `sessionId` from JSON output and pass it to `-r` to build multi-step automations that maintain context.
|
||||
- Check `/session-info` to see how much of your context window has been used.
|
||||
244
crates/codegen/xai-grok-pager/docs/user-guide/18-sandbox.md
Normal file
244
crates/codegen/xai-grok-pager/docs/user-guide/18-sandbox.md
Normal file
|
|
@ -0,0 +1,244 @@
|
|||
# Sandbox Mode
|
||||
|
||||
Sandbox mode restricts what the agent process and its spawned commands can access on your filesystem and network using OS-level kernel primitives (Landlock on Linux, Seatbelt on macOS). The kernel enforces these limits for the process lifetime.
|
||||
|
||||
Sandbox mode is off by default.
|
||||
|
||||
---
|
||||
|
||||
## Quick Start
|
||||
|
||||
```bash
|
||||
# Run with workspace sandbox (read everywhere, write to CWD + temp dirs + ~/.grok/)
|
||||
grok --sandbox workspace
|
||||
|
||||
# Read-only mode (read everywhere, write only to ~/.grok/ + temp dirs)
|
||||
grok --sandbox read-only
|
||||
|
||||
# Most restrictive profile (read CWD + system paths, write CWD + temp dirs + ~/.grok/, no child network)
|
||||
grok --sandbox strict
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Built-in Profiles
|
||||
|
||||
| Profile | FS Read | FS Write | Child Network | Use Case |
|
||||
| --------------------- | ------------------ | ---------------------------------------------- | ------------- | --------------------------------- |
|
||||
| `off` (default) | Unrestricted | Unrestricted | Unrestricted | No sandbox |
|
||||
| `workspace` | Everywhere | CWD + `~/.grok/` + `/tmp` + `/var/tmp` | Allowed | Normal development |
|
||||
| `devbox` | Everywhere | All top-level dirs except `/data` | Allowed | Disposable dev VMs |
|
||||
| `read-only` | Everywhere | `~/.grok/` + `/tmp` + `/var/tmp` | Blocked¹ | Exploration, code review |
|
||||
| `strict` | CWD + system paths | CWD + `~/.grok/` + `/tmp` + `/var/tmp` | Blocked¹ | Untrusted code |
|
||||
|
||||
¹ Child-network blocking is enforced on **Linux only** (via seccomp). On macOS it is a no-op — these profiles do not restrict child-process network there.
|
||||
|
||||
To block specific files (e.g. `.env` or credential paths) on top of a profile, define a [custom profile](#custom-profiles) with a `deny` list — it is kernel-enforced (read + write/rename) and supports glob patterns like `**/*.pem`.
|
||||
|
||||
### Profile Details
|
||||
|
||||
**workspace** -- The recommended profile for everyday development. The agent can read any file on the system (for understanding dependencies, system libraries, etc.) but can only write to the current working directory, `~/.grok/`, and temp directories (`/tmp`, `/var/tmp`, plus the macOS temp dirs). Network access is allowed for tools like `web_search` and MCP servers.
|
||||
|
||||
**devbox** -- A reserved built-in profile for disposable development VMs. The agent can read everywhere and write to every top-level directory except `/data` and the virtual filesystems (`/proc`, `/sys`, `/dev`), including the home directory. Network access is allowed. `--sandbox devbox` runs the built-in profile, which shadows any `[profiles.devbox]` you define in `sandbox.toml`.
|
||||
|
||||
**read-only** -- Use when you want the agent to analyze code without modifying your project files. The agent can read everything but can only write to `~/.grok/` (needed for session persistence) and temp directories. Child-process network access is blocked on Linux (no-op on macOS).
|
||||
|
||||
**strict** -- The most restrictive profile, for reviewing untrusted code. The agent can only read files within the current working directory and essential system paths. Writes are limited to CWD, `~/.grok/`, and temp directories. Child-process network access is blocked on Linux (no-op on macOS).
|
||||
|
||||
---
|
||||
|
||||
## Custom Profiles
|
||||
|
||||
Create custom sandbox profiles in `~/.grok/sandbox.toml` (global) or `.grok/sandbox.toml` (per-project):
|
||||
|
||||
```toml
|
||||
[profiles.project]
|
||||
# Start from a built-in profile, then add overrides
|
||||
extends = "workspace"
|
||||
restrict_network = true
|
||||
|
||||
# Paths the agent can read but NOT write/delete
|
||||
read_only = ["/data"]
|
||||
|
||||
# Additional writable paths
|
||||
read_write = ["/tmp/scratch"]
|
||||
|
||||
# Paths or globs to kernel-deny (read + write/rename, enforced; see notes below)
|
||||
deny = ["/data/shared-secrets", "**/.env", "**/*.pem"]
|
||||
```
|
||||
|
||||
Use the custom profile:
|
||||
|
||||
```bash
|
||||
grok --sandbox project
|
||||
```
|
||||
|
||||
A custom profile can't reuse a built-in name. `--sandbox devbox` always runs the built-in `devbox` profile, shadowing any `[profiles.devbox]` you define.
|
||||
|
||||
When the global and per-project files define the same custom profile name, the user-level definition takes precedence and the project definition is ignored. If those two definitions differ, Grok warns about the conflict at startup — on the welcome screen in the TUI, and on stderr for headless runs. Identical duplicate definitions do not produce a warning.
|
||||
|
||||
### Custom Profile Fields
|
||||
|
||||
| Field | Type | Description |
|
||||
| ------------------ | -------- | ---------------------------------------------------- |
|
||||
| `extends` | String | Base built-in profile to inherit from (`workspace`, `devbox`, `read-only`, `strict`). Defaults to `workspace` when omitted |
|
||||
| `restrict_network` | Boolean | Block network access for child processes |
|
||||
| `read_only` | String[] | Additional read-only paths |
|
||||
| `read_write` | String[] | Additional read-write paths |
|
||||
| `deny` | String[] | Paths or globs to kernel-deny (read + write/rename; see notes). An entry with `*`, `?`, or `[` is a glob |
|
||||
|
||||
> **Note on `deny`:** A non-empty `deny` list is **kernel-enforced**. Denied paths
|
||||
> are **read-denied and write/rename-denied** via Seatbelt on macOS and a bwrap
|
||||
> bind-over on Linux, so a denied path can neither be read (via `bash`, `grep`, or
|
||||
> subagents) nor relocated out of the deny set and read elsewhere (the
|
||||
> `mv secret x && cat x` bypass is closed). On **Linux**, read-deny requires
|
||||
> `bubblewrap`: if it is missing (or any single deny path can't be bound), Grok
|
||||
> refuses to start rather than run with denied paths exposed (`devbox`, which only
|
||||
> write-denies `/data`, still falls back to Landlock). Writes to paths **not** in
|
||||
> `deny` are controlled by what you grant in `read_write`.
|
||||
|
||||
> **Globs in `deny`:** An entry is a **glob** if it contains `*`, `?`, or `[`.
|
||||
> Those characters **always** mean glob — to deny a literal file whose name
|
||||
> contains them, name a parent directory instead. The supported, gitignore-style
|
||||
> subset is:
|
||||
>
|
||||
> - `*` — any run of characters within one path segment (stops at `/`)
|
||||
> - `?` — exactly one character within a segment
|
||||
> - `**` — spans directories (as a whole path segment, e.g. `**/`, `a/**`); `**/`
|
||||
> also matches zero directories, so `**/.env` matches `.env` and `sub/.env`
|
||||
> - `[abc]` / `[a-z]` — character classes; a leading `!` **or** `^` negates
|
||||
> (`[!a]` and `[^a]` both mean "not `a`")
|
||||
>
|
||||
> Brace alternation (`{a,b}`), backslash-escapes, and the unusual class forms
|
||||
> `[]…]` (literal `]` first) and POSIX `[[:…:]]` are **not** supported, so the two
|
||||
> platforms can never interpret a glob differently. A glob using an unsupported
|
||||
> metacharacter, or one that is malformed, makes Grok **refuse to start** (fail
|
||||
> closed) on **both** platforms — write `*.pem` and `*.key` as separate entries
|
||||
> rather than `*.{pem,key}`.
|
||||
>
|
||||
> Relative globs are anchored at the workspace; absolute globs (e.g.
|
||||
> `/home/**/.ssh`) at their literal prefix. Non-glob entries keep exact-path
|
||||
> matching. Enforcement otherwise differs by platform:
|
||||
>
|
||||
> - **macOS is airtight:** each glob becomes a Seatbelt regex applied at runtime,
|
||||
> so matching files are denied **even if created after Grok starts**.
|
||||
> - **Linux is best-effort:** a mount namespace can't glob at runtime, so each
|
||||
> glob is expanded to the files that **exist at launch** and those are bound
|
||||
> over. Files created **later** that match a glob are **not** covered — name
|
||||
> exact paths for anything that must be airtight on Linux. A glob that matches
|
||||
> too many files, or whose tree is too deep/broad to walk, makes Grok **refuse
|
||||
> to start** rather than under-enforce.
|
||||
|
||||
---
|
||||
|
||||
## How It Works
|
||||
|
||||
The sandbox is applied to the **entire grok process** at startup using kernel primitives -- not per-command wrapping. This means all tool operations are covered:
|
||||
|
||||
- `read_file`, `search_replace`, `list_dir` -- restricted by Landlock/Seatbelt in-process
|
||||
- `bash` commands, `grep` (rg) -- child processes inherit FS restrictions automatically
|
||||
- Network -- on Linux, child processes can be blocked via seccomp; on macOS this is a no-op
|
||||
|
||||
The sandbox is **irreversible** once applied. The agent cannot relax restrictions at runtime.
|
||||
|
||||
---
|
||||
|
||||
## Resuming Sessions
|
||||
|
||||
The profile a session was started with is saved with the session and is **fixed
|
||||
for the life of the session**. When you resume it (`grok --resume <id>`,
|
||||
`grok --continue`, or `grok -r`), Grok restores that same profile automatically —
|
||||
so a session started with `--sandbox workspace` won't silently come back under a
|
||||
stricter default and break commands that previously worked.
|
||||
|
||||
Resuming will **not** change a session's sandbox:
|
||||
|
||||
- Omitting `--sandbox` on resume uses the session's saved profile.
|
||||
- Passing `--sandbox <profile>` that **matches** the saved profile is allowed.
|
||||
- Passing `--sandbox <profile>` that **differs** from the saved profile is
|
||||
**refused with an error** — changing a resumed session's sandbox is a safety
|
||||
footgun (it could widen access the session was meant to be confined to, or
|
||||
break a session that relied on broader access). Start a new session to use a
|
||||
different profile.
|
||||
|
||||
Profile resolution order for a **new** session:
|
||||
|
||||
1. An explicit `--sandbox <profile>` flag or `GROK_SANDBOX` environment variable
|
||||
2. The `[sandbox] profile` in your config
|
||||
3. `off` (no sandbox)
|
||||
|
||||
---
|
||||
|
||||
## Platform Support
|
||||
|
||||
| Platform | Mechanism | Minimum Version |
|
||||
| -------- | --------- | ---------------------- |
|
||||
| Linux | Landlock | Kernel 5.13 or later |
|
||||
| macOS | Seatbelt | macOS (all versions) |
|
||||
|
||||
If the sandbox cannot be applied (e.g., unsupported kernel, missing entitlements), Grok logs a warning and continues without enforcement. The exception is an explicitly-requested **custom profile**: on **both macOS and Linux**, if it cannot be applied (unknown profile, malformed `sandbox.toml`, or — on Linux — `bubblewrap` unavailable for a non-empty `deny`), Grok refuses to start rather than run with its denied paths exposed.
|
||||
|
||||
---
|
||||
|
||||
## Network Restrictions
|
||||
|
||||
On Linux, profiles with `restrict_network` block network access in **child processes** (bash commands, scripts) via seccomp. On macOS, network blocking is a no-op. Built-in tools that make HTTP requests in-process (web search, LLM API calls) are never affected -- the agent needs network access to function.
|
||||
|
||||
In practice, on Linux this means:
|
||||
|
||||
- `web_search`, `web_fetch`, and the LLM API always have network access
|
||||
- `bash` commands like `curl`, `wget`, and `npm install` are blocked when `restrict_network` is enabled
|
||||
|
||||
---
|
||||
|
||||
## Event Logging
|
||||
|
||||
Sandbox events are logged to `~/.grok/sandbox-events.jsonl` for debugging. Events include:
|
||||
|
||||
- Profile applied (which profile, timestamp)
|
||||
- Violations (attempted access to denied paths)
|
||||
|
||||
---
|
||||
|
||||
## When to Use Sandbox Mode
|
||||
|
||||
**Use `workspace` when:**
|
||||
|
||||
- Working on your own projects and you want basic write protection
|
||||
- Running in shared environments where you want to limit the scope of changes
|
||||
|
||||
**Define a custom profile with a `deny` list when:**
|
||||
|
||||
- You need to block specific files (e.g. `.env` or credential paths) on top of a base profile
|
||||
- You need kernel enforcement that covers `bash`, `grep`, and subagents — not just the `read_file` tool
|
||||
|
||||
**Use `read-only` when:**
|
||||
|
||||
- Reviewing code you do not trust
|
||||
- Exploring a codebase without risk of accidental modification
|
||||
- Running code analysis or audits
|
||||
|
||||
**Use `strict` when:**
|
||||
|
||||
- Analyzing untrusted or third-party code
|
||||
- Running in security-sensitive environments
|
||||
- You want maximum isolation
|
||||
|
||||
**Skip sandbox when:**
|
||||
|
||||
- The agent needs to install dependencies (`npm install`, `pip install`)
|
||||
- The agent needs to modify files outside the working directory
|
||||
- You are working in a trusted environment and want maximum flexibility
|
||||
|
||||
---
|
||||
|
||||
## Trade-offs
|
||||
|
||||
| Aspect | Without Sandbox | With Sandbox |
|
||||
| ----------- | -------------------------- | ------------------------------- |
|
||||
| Safety | Agent has full system access | Agent restricted to profile rules |
|
||||
| Capability | Can do anything | Limited by profile |
|
||||
| Performance | No overhead | Negligible overhead |
|
||||
| Recovery | Must trust the agent | Kernel enforces boundaries |
|
||||
|
||||
The sandbox enforces limits at the OS level -- through Landlock or a mount namespace on Linux, and Seatbelt on macOS -- not a separate VM.
|
||||
160
crates/codegen/xai-grok-pager/docs/user-guide/19-plan-mode.md
Normal file
160
crates/codegen/xai-grok-pager/docs/user-guide/19-plan-mode.md
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
# Plan Mode
|
||||
|
||||
Plan mode is a structured planning phase: the agent explores the codebase and designs an implementation approach before writing any code. Use it for tasks with genuine ambiguity about the right approach, where getting your input before coding prevents significant rework.
|
||||
|
||||
---
|
||||
|
||||
## What Plan Mode Does
|
||||
|
||||
When plan mode is active, the agent:
|
||||
|
||||
1. Reads and searches the codebase to understand existing patterns and architecture
|
||||
2. Designs an implementation approach and writes it to the plan file
|
||||
3. May use `ask_user_question` to clarify specific questions
|
||||
4. Calls `exit_plan_mode` to present the plan for your approval
|
||||
|
||||
Plan mode is read-only except for the plan file: plan-file edits (`plan.md` in the session directory) are auto-approved, and edits to any other file are rejected outright — the tool call fails with a short message naming the plan file as the only editable path. This holds in every permission mode, including always-approve. Separating planning from implementation lets you review and correct the approach before any code is written.
|
||||
|
||||
---
|
||||
|
||||
## How to Enter Plan Mode
|
||||
|
||||
### Agent-Initiated Entry
|
||||
|
||||
The agent enters plan mode when it determines a task has genuine ambiguity. It calls the `enter_plan_mode` tool, which requires your approval before plan mode activates. If you decline, the agent stays in normal mode.
|
||||
|
||||
**Good triggers for plan mode:**
|
||||
|
||||
- "Add user authentication to the app" -- genuinely ambiguous (session vs JWT, token storage, middleware structure)
|
||||
- "Redesign the data pipeline" -- major restructuring where the wrong approach wastes significant effort
|
||||
- "Add caching to the API" -- multiple reasonable approaches (Redis vs in-memory vs file-based)
|
||||
- "Add real-time updates" -- architectural decision (WebSockets vs SSE vs polling)
|
||||
|
||||
**Not appropriate for plan mode:**
|
||||
|
||||
- "Add a delete button to the user profile" -- clear implementation path
|
||||
- "Fix the typo in the README" -- straightforward
|
||||
- "Update the error handling in the API" -- start working, ask specific questions if needed
|
||||
- "Can we work on the search feature?" -- user wants to get started, not plan
|
||||
|
||||
### User-Initiated Entry
|
||||
|
||||
You can enter plan mode yourself in two ways:
|
||||
|
||||
- **`/plan`** -- Enter plan mode. Plan mode activates when you send your next prompt. Run `/plan <description>` to enter plan mode and start a turn with that description in one step.
|
||||
- **Shift+Tab** -- Cycle the session mode: Normal, then Plan, then Always-approve, then back to Normal. From Normal, a single press lands on Plan.
|
||||
|
||||
After a plan exists, run **`/view-plan`** (aliases `/show-plan`, `/plan-view`) to reopen its saved preview.
|
||||
|
||||
---
|
||||
|
||||
## The Plan File
|
||||
|
||||
The plan is written to `plan.md` inside the session directory (`~/.grok/sessions/<cwd>/<session-id>/plan.md`, where `<cwd>` is an encoded directory name, not the literal path).
|
||||
|
||||
The plan file contains:
|
||||
|
||||
- A **Context** section explaining why the change is being made
|
||||
- The recommended approach (not every alternative)
|
||||
- The paths of critical files to modify
|
||||
- Existing functions and utilities to reuse, with their file paths
|
||||
- A verification section describing how to test the changes end to end
|
||||
|
||||
---
|
||||
|
||||
## Plan Approval
|
||||
|
||||
When the agent finishes planning, it calls the `exit_plan_mode` tool. The tool reads the plan file from disk, and the TUI opens a scrollable preview of the plan with an action bar along the bottom.
|
||||
|
||||
If the agent exits without writing a plan (empty or missing `plan.md`), the same approval surface still opens with a clear empty-state message so you can approve and start implementing, request changes (send the agent back to planning), or quit. In minimal mode the empty notice is committed into scrollback and the controls strip header reads **No plan written yet**.
|
||||
|
||||
### Reviewing the Plan
|
||||
|
||||
Scroll the plan with the arrow keys or `j`/`k`. The action bar shows these shortcuts:
|
||||
|
||||
| Shortcut | Action |
|
||||
| -------- | ---------------------------------------------------------------------------------------------------- |
|
||||
| `a` | Approve the plan and start building. With pending comments, this reads `approve w/ comments` and sends them alongside the approval. |
|
||||
| `s` | Request changes. Focus moves to the prompt so you can type revision notes; press `Enter` to send them. |
|
||||
| `c` | Comment on the selected line or line range. |
|
||||
| `q` | Quit plan -- abandon the plan without approving and turn plan mode off. |
|
||||
|
||||
Press `Tab` to move focus between the plan preview and the prompt.
|
||||
|
||||
### Providing Feedback
|
||||
|
||||
The approval view has three focus states:
|
||||
|
||||
- **Preview**: Scroll the plan and select lines to comment on.
|
||||
- **Commenting**: Add an inline comment to the selected line range (press `c`, or `Enter` on a line).
|
||||
- **Prompt**: Type freeform revision notes.
|
||||
|
||||
Press `Tab` to switch between the preview and the prompt. When you send feedback -- inline comments, freeform notes, or both -- the agent receives it and revises the plan. Plan mode stays active so you can iterate.
|
||||
|
||||
### Leaving the Approval View
|
||||
|
||||
Press `Esc` to return focus from the prompt to the plan preview. To dismiss the approval without approving or sending feedback, press `q` to quit the plan. Quitting abandons the proposed plan and turns plan mode off.
|
||||
|
||||
---
|
||||
|
||||
## Plan Mode Lifecycle
|
||||
|
||||
The plan mode state machine has four states:
|
||||
|
||||
| State | Description |
|
||||
| -------------- | -------------------------------------------------------------- |
|
||||
| `Inactive` | Normal operating mode. No plan mode constraints. |
|
||||
| `Pending` | Client toggled plan mode ON, but no prompt has been sent yet. |
|
||||
| `Active` | Plan mode is active. Plan-file edits are auto-approved; edits to other files are rejected. |
|
||||
| `ExitPending` | User toggled plan mode OFF while a turn is in-flight. |
|
||||
|
||||
Transitions:
|
||||
|
||||
```
|
||||
Inactive --> Active (enter_plan_mode tool called and approved -- skips Pending)
|
||||
Inactive --> Pending (you toggle plan mode on with /plan or Shift+Tab)
|
||||
Pending --> Active (your first prompt activates plan mode)
|
||||
Active --> Inactive (exit_plan_mode approved, or you toggle plan mode off when idle)
|
||||
Active --> ExitPending (you toggle plan mode off while a turn is in-flight)
|
||||
ExitPending --> Inactive (after the turn completes)
|
||||
```
|
||||
|
||||
Plan mode state is persisted to disk and survives process restarts. Transient states (`Pending`, `ExitPending`) are collapsed to `Inactive` on restart since they depend on in-flight interactions.
|
||||
|
||||
---
|
||||
|
||||
## Edits During Plan Mode
|
||||
|
||||
During active plan mode, edits to the plan file are auto-approved without prompting, so the agent can iterate on the plan freely. Edits to **any other file are rejected** before they run — the agent receives a short message naming the plan file as the only editable path.
|
||||
|
||||
This enforcement is independent of the permission mode:
|
||||
|
||||
- **Always-approve (yolo) stays armed underneath plan mode.** Non-edit tools (bash commands, reads, MCP tools) still auto-run, but file edits are blocked until you approve exiting plan mode. Once the plan is approved, always-approve resumes for implementation.
|
||||
- Bash commands are not inspected for file writes — plan mode blocks the edit tools, not shell redirection.
|
||||
- Subagents are not covered by the parent session's plan-mode edit gate. Each subagent starts with a fresh plan-mode tracker (`Inactive`), so a `general-purpose` (or other write-capable) subagent can edit files while the parent is still in plan mode — and it inherits the parent's permission mode (including always-approve). Read-only types such as `explore` remain limited by their own toolset.
|
||||
|
||||
The status flag shows `plan` while plan mode is active. If always-approve is enabled underneath, its flag reappears when plan mode exits.
|
||||
|
||||
---
|
||||
|
||||
## Plan Mode and Compaction
|
||||
|
||||
When `/compact` runs during an active plan mode session, the plan mode state is preserved. The compacted context includes a reminder that plan mode is active, so the agent continues planning after compaction.
|
||||
|
||||
---
|
||||
|
||||
## When Plan Mode is Appropriate
|
||||
|
||||
**Use plan mode for:**
|
||||
|
||||
- Tasks with significant architectural ambiguity (multiple reasonable approaches)
|
||||
- Unclear requirements that need exploration before implementation
|
||||
- High-impact restructuring where the wrong approach wastes significant effort
|
||||
|
||||
**Skip plan mode for:**
|
||||
|
||||
- Tasks with a clear implementation path
|
||||
- Bug fixes where the fix is obvious once you understand the bug
|
||||
- Adding features that follow existing conventions
|
||||
- Straightforward modifications (renaming, formatting, adding tests)
|
||||
- Research and exploration tasks (use subagents instead)
|
||||
|
|
@ -0,0 +1,236 @@
|
|||
# Background Tasks and Monitoring
|
||||
|
||||
Grok runs long-lived processes without blocking the conversation. This document covers background commands, the `/loop` command, the `monitor` tool, and the scheduler.
|
||||
|
||||
---
|
||||
|
||||
## Background Commands
|
||||
|
||||
Set `background: true` on the `run_terminal_command` tool to run a command in the background. It returns a task ID immediately; retrieve output with `get_command_or_subagent_output`.
|
||||
|
||||
### How It Works
|
||||
|
||||
1. The agent calls `run_terminal_command` with `background: true`.
|
||||
2. The command starts in the background.
|
||||
3. The agent receives a `task_id` for later reference.
|
||||
4. When the command completes, a notification appears in the conversation.
|
||||
|
||||
### Getting Output
|
||||
|
||||
Use the `get_command_or_subagent_output` tool to check on a background command or subagent:
|
||||
|
||||
- `get_command_or_subagent_output(task_id)` — current output and status without waiting
|
||||
- `get_command_or_subagent_output(task_id, timeout_ms=30000)` — wait up to the given milliseconds for completion
|
||||
|
||||
### Waiting for Multiple Tasks
|
||||
|
||||
Use `wait_commands_or_subagents` to block on several tasks at once:
|
||||
|
||||
- `task_ids` — the list of task IDs to wait for (maximum 20)
|
||||
- `mode` — `wait_any` returns when the first task completes; `wait_all` waits for every task
|
||||
- `timeout_ms` — the maximum time to wait, in milliseconds (default: 30 seconds)
|
||||
|
||||
The tool returns the status and output for every task you list.
|
||||
|
||||
### Killing Background Tasks
|
||||
|
||||
Use `kill_command_or_subagent(task_id)` to terminate a running background task or subagent. The tool sends SIGTERM, then SIGKILL, to shell processes, and sends Cancel and Shutdown to subagents. It reports success if the task was killed or had already exited.
|
||||
|
||||
### Common Use Cases
|
||||
|
||||
- **Dev servers**: Start a development server and continue coding
|
||||
- **Test suites**: Run tests in the background while working on fixes
|
||||
- **Build processes**: Start a build and check results later
|
||||
- **Long compilations**: Start a compile and continue with other tasks
|
||||
|
||||
---
|
||||
|
||||
## Send a Running Task to the Background
|
||||
|
||||
In the interactive TUI, press `Ctrl+G` to send the running foreground command to the background. Do this when:
|
||||
|
||||
- A command takes longer than expected.
|
||||
- You want to ask the agent something else while a command runs.
|
||||
- You realize a process is long-running after it has started.
|
||||
|
||||
The task keeps running, and you receive a notification when it completes.
|
||||
|
||||
---
|
||||
|
||||
## The /loop Command
|
||||
|
||||
`/loop` runs a prompt on a recurring interval. It is useful for polling tasks, periodic checks, and continuous monitoring.
|
||||
|
||||
### Syntax
|
||||
|
||||
```
|
||||
/loop [interval] <prompt>
|
||||
```
|
||||
|
||||
The interval format supports:
|
||||
|
||||
| Format | Example | Description |
|
||||
| ------ | ------- | ------------------ |
|
||||
| `Ns` | `60s` | Every N seconds (minimum 60) |
|
||||
| `Nm` | `5m` | Every N minutes |
|
||||
| `Nh` | `2h` | Every N hours |
|
||||
| `Nd` | `1d` | Every N days |
|
||||
|
||||
### Examples
|
||||
|
||||
```
|
||||
/loop 5m Check if the test suite passes and report any failures
|
||||
/loop 2h Summarize new commits since the last check
|
||||
/loop 60s Check if the dev server at localhost:3000 is responding
|
||||
```
|
||||
|
||||
### Behavior
|
||||
|
||||
- The prompt fires immediately on creation, then repeats at the specified interval
|
||||
- Each firing creates a new agent turn
|
||||
- Recurring tasks auto-expire after 7 days
|
||||
- Maximum 50 scheduled tasks can be active at once
|
||||
|
||||
---
|
||||
|
||||
## The monitor Tool
|
||||
|
||||
The `monitor` tool streams events from a long-running script. Each line of output becomes a notification in the conversation. The `monitor` tool is the streaming counterpart to `/loop`: use `/loop` for periodic checks, and use `monitor` for real-time event streams.
|
||||
|
||||
### How It Works
|
||||
|
||||
1. You provide a shell command (`command`) and a short `description` that appears in every notification.
|
||||
2. Grok merges the command's stdout and stderr into a single output file.
|
||||
3. Each new line in that file becomes a notification delivered to the conversation.
|
||||
4. The monitor runs until the command exits or you stop it.
|
||||
|
||||
### Script Guidelines
|
||||
|
||||
- **Always use `grep --line-buffered` in pipes.** Without it, pipe buffering delays events by minutes.
|
||||
- **Handle transient failures in poll loops** (`curl ... || true`). One failed request should not stop the monitor.
|
||||
- **Use selective filters.** Every line becomes a message, so never pipe raw logs.
|
||||
- **Set poll intervals to match the source.** Use 30 seconds or more for remote APIs to respect rate limits, and 0.5 to 1 second for local checks.
|
||||
- **Both stdout and stderr generate events.** Redirect output you don't want as events — for example, append `2>/dev/null` — or filter it out.
|
||||
|
||||
### Examples
|
||||
|
||||
```bash
|
||||
# Watch for errors in a log file
|
||||
tail -f /var/log/app.log | grep --line-buffered "ERROR"
|
||||
|
||||
# Monitor file changes in a directory
|
||||
inotifywait -m --format '%e %f' /watched/dir
|
||||
|
||||
# Poll GitHub for new PR comments
|
||||
last=$(date -u +%Y-%m-%dT%H:%M:%SZ)
|
||||
while true; do
|
||||
now=$(date -u +%Y-%m-%dT%H:%M:%SZ)
|
||||
gh api "repos/owner/repo/issues/123/comments?since=$last" \
|
||||
--jq '.[] | "\(.user.login): \(.body)"'
|
||||
last=$now; sleep 30
|
||||
done
|
||||
```
|
||||
|
||||
### Persistent Monitors
|
||||
|
||||
Set `persistent: true` for monitors that should run for the lifetime of the session:
|
||||
|
||||
- PR monitoring
|
||||
- Log tailing
|
||||
- CI status watching
|
||||
|
||||
Stop persistent monitors with `kill_command_or_subagent(task_id)`.
|
||||
|
||||
### Volume Control
|
||||
|
||||
If a monitor produces too many events, Grok stops it automatically. When this happens, restart the monitor with a tighter filter. Prefer `grep --line-buffered`, `awk`, or a wrapper script that emits only the events you care about.
|
||||
|
||||
---
|
||||
|
||||
## The Scheduler
|
||||
|
||||
The scheduler provides a lower-level API for creating recurring tasks. `/loop` is a convenience wrapper around the scheduler.
|
||||
|
||||
### scheduler_create
|
||||
|
||||
Create a scheduled task:
|
||||
|
||||
| Parameter | Description |
|
||||
| ---------------- | -------------------------------------------------------- |
|
||||
| `interval` | How often to run: `"5m"`, `"2h"`, `"1d"`, `"60s"` |
|
||||
| `prompt` | The prompt text to execute on each fire |
|
||||
| `fire_immediately`| Fire on creation in addition to the interval (default: `false`) |
|
||||
| `recurring` | Repeat (default: `true`) or fire once (`false`) |
|
||||
| `durable` | Persist across sessions (default: `false`) |
|
||||
|
||||
### scheduler_list
|
||||
|
||||
List all active scheduled tasks with their IDs, prompts, intervals, and next fire times.
|
||||
|
||||
### scheduler_delete
|
||||
|
||||
Cancel a scheduled task by ID. Returns success if the task was found and removed.
|
||||
|
||||
---
|
||||
|
||||
## The Tasks Pane
|
||||
|
||||
In the interactive TUI, press `Ctrl+B` to toggle the tasks pane. This pane lists, in a single view:
|
||||
|
||||
- Running subagents and their progress
|
||||
- Active background tasks and their status
|
||||
- Monitor and `/loop` tasks, each with a live line-count badge
|
||||
- The task ID for each entry
|
||||
|
||||
To toggle the prompt queue instead, press `Ctrl+;`.
|
||||
|
||||
---
|
||||
|
||||
## Use Cases and Patterns
|
||||
|
||||
### Dev Server + Coding
|
||||
|
||||
Start a dev server in the background and continue coding:
|
||||
|
||||
```
|
||||
Start the dev server with `npm run dev` in the background, then implement the login form.
|
||||
```
|
||||
|
||||
The agent runs the dev server with `background: true` and continues writing code. When the server starts, you see a notification.
|
||||
|
||||
### Continuous Test Monitoring
|
||||
|
||||
```
|
||||
/loop 5m Run the test suite and report any new failures since the last run
|
||||
```
|
||||
|
||||
Every 5 minutes, the agent runs tests and reports only new failures.
|
||||
|
||||
### Log Monitoring
|
||||
|
||||
Use `monitor` to watch for specific events:
|
||||
|
||||
```
|
||||
Monitor the application log for ERROR and WARN entries. Use:
|
||||
tail -f /var/log/app.log | grep --line-buffered -E "ERROR|WARN"
|
||||
```
|
||||
|
||||
Each error or warning appears as a notification in the conversation.
|
||||
|
||||
### CI Pipeline Watching
|
||||
|
||||
```
|
||||
/loop 2m Check the status of the GitHub Actions run for this PR. Report when it completes.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
- **Use `background` for one-shot long commands** (builds, test suites, server starts)
|
||||
- **Use `/loop` for periodic checks** (CI status, test runs, health checks)
|
||||
- **Use `monitor` for real-time event streams** (log tailing, file watching)
|
||||
- **Use `scheduler_create` with `recurring: false`** for delayed one-shot tasks
|
||||
- **Keep monitor filters tight** — prefer `grep --line-buffered` over raw log streams
|
||||
- **Do not use sleep loops** in normal commands to poll — use `get_command_or_subagent_output` with `timeout_ms` instead
|
||||
- **Set reasonable poll intervals** — 30s+ for remote APIs to avoid rate limits, shorter for local checks
|
||||
|
|
@ -0,0 +1,210 @@
|
|||
# Terminal Support and Troubleshooting
|
||||
|
||||
Grok Build runs as a full-screen TUI. To draw the interface, it relies on terminal escape sequences for color, clipboard, mouse, and full-screen control. Some terminals, multiplexers, and SSH sessions handle these sequences differently.
|
||||
|
||||
## Quick Fixes
|
||||
|
||||
### Truecolor / Washed-out or wrong colors
|
||||
|
||||
```bash
|
||||
# Add to ~/.zshrc or ~/.bashrc
|
||||
export COLORTERM=truecolor
|
||||
```
|
||||
|
||||
Inside tmux or over SSH, also add to your tmux config:
|
||||
|
||||
```tmux
|
||||
# ~/.tmux.conf or ~/.byobu/.tmux.conf
|
||||
set -g default-terminal "tmux-256color"
|
||||
set -as terminal-features ",*:RGB"
|
||||
```
|
||||
|
||||
### Recommended tmux settings (clipboard + passthrough)
|
||||
|
||||
```tmux
|
||||
set -g set-clipboard on
|
||||
set -g allow-passthrough on
|
||||
```
|
||||
|
||||
After editing, run:
|
||||
|
||||
```bash
|
||||
tmux source-file ~/.tmux.conf
|
||||
# or detach and reattach
|
||||
```
|
||||
|
||||
### Live diagnostics inside Grok
|
||||
|
||||
Run this slash command:
|
||||
|
||||
```
|
||||
/terminal-setup
|
||||
```
|
||||
|
||||
The command reports the terminal, multiplexer, **color level**, **available themes**, and clipboard routes Grok detected, then lists any issues and how to fix them. When color is below truecolor, it explains how to unlock the truecolor-only themes (TokyoNight, RosePineMoon, OscuraMidnight) — or notes that Terminal.app is inherently 256-color. The aliases `/terminal-check` and `/terminal-info` run the same command.
|
||||
|
||||
---
|
||||
|
||||
## Detected Terminals
|
||||
|
||||
Grok detects these terminal emulators from environment variables:
|
||||
|
||||
- **Apple Terminal** (Terminal.app)
|
||||
- **Ghostty**
|
||||
- **iTerm2**
|
||||
- **Warp**
|
||||
- **WezTerm**
|
||||
- **Kitty**
|
||||
- **Alacritty**
|
||||
- **Rio**
|
||||
- **foot** (Wayland-native, Linux)
|
||||
- **VS Code**, **Cursor**, **Windsurf**, and **Zed** integrated terminals
|
||||
- **JetBrains** IDE terminals (IntelliJ, PhpStorm, and others)
|
||||
- **Grok Desktop**
|
||||
- **VTE**-based terminals (GNOME Terminal, GNOME Console, Tilix)
|
||||
- **Windows Terminal**
|
||||
|
||||
Detection has these limitations:
|
||||
|
||||
- Inside tmux, the variables Grok needs to identify the terminal don't reach the pager.
|
||||
- Over SSH, many terminal variables aren't forwarded.
|
||||
- tmux's global environment (`tmux -g`) reflects the first client that attached to the server, not your current session.
|
||||
|
||||
---
|
||||
|
||||
## Common Problems and Fixes
|
||||
|
||||
### Problem: Colors look wrong or lack truecolor
|
||||
|
||||
**Cause**: `COLORTERM` not set or tmux not configured for 24-bit RGB.
|
||||
|
||||
**Fix**: Apply the two settings above, then restart Grok.
|
||||
|
||||
**Verify**: Run `/terminal-setup`. Expect `color truecolor` and `themes all`. If `color` is `256` or `basic`, the issues section has the unlock fix.
|
||||
|
||||
### Problem: Clipboard problems
|
||||
|
||||
Grok writes to the clipboard through up to three routes, which match the **Clipboard routes** section of `/terminal-setup`:
|
||||
|
||||
- **native** — Grok always writes to the native OS clipboard first.
|
||||
- **tmux buffer** — inside tmux, Grok also writes to the tmux paste buffer (`tmux load-buffer`).
|
||||
- **OSC 52** — Grok emits the OSC 52 escape sequence so the outer terminal updates its clipboard. Grok always emits OSC 52 inside tmux. Outside tmux, it emits OSC 52 on Linux, over SSH, or in a container without a display.
|
||||
|
||||
**Linux Wayland**: on compositors that support the data-control protocol (GNOME 48+, KDE, Sway, Hyprland — the `data-control` line in `/terminal-setup` shows `yes`) copies work even if the terminal loses focus mid-copy. On older compositors (GNOME 46/47), keep the terminal focused until the copy toast confirms, and install the `wl-clipboard` package (provides `wl-copy`) for the most reliable route — Grok shows a startup warning when this applies. If data-control misbehaves on your compositor, set `GROK_CLIPBOARD_NO_DATA_CONTROL=1` to stop Grok from speaking that protocol entirely — copies then go through the CLI tools (`wl-copy`/`xclip`).
|
||||
|
||||
**Linux X11 selections**: X11 **PRIMARY** and **CLIPBOARD** are separate. Selecting text usually fills PRIMARY; an explicit Copy action fills CLIPBOARD. In Grok:
|
||||
|
||||
- An unmodified middle click reads PRIMARY only when `DISPLAY` is non-empty. Pure X11 can fall back to the native arboard reader. XWayland must have `xclip` or `xsel` on `PATH`; Grok deliberately disables the arboard fallback there so it cannot substitute Wayland PRIMARY.
|
||||
- `Ctrl+V` reads CLIPBOARD only and never falls back to PRIMARY. To fill CLIPBOARD from a shell, run `printf %s "text" | xclip -selection clipboard`.
|
||||
- `Shift+Insert` remains the terminal-native selected-text paste. Native Wayland PRIMARY behavior is compositor/terminal-specific and is not inferred from `TERM` or an incoming mouse event.
|
||||
|
||||
**SSH and selected text**: a remote Grok process usually cannot read the local terminal's PRIMARY or CLIPBOARD selection. Use terminal-native `Shift+Insert`, or hold `Shift` while middle-clicking when your terminal uses that gesture to bypass mouse reporting. The terminal then sends the local selection through the PTY instead of asking the remote process to access it.
|
||||
|
||||
**Known limitation — Apple Terminal + SSH**:
|
||||
Apple Terminal ignores OSC 52, so copying from a Grok session over SSH can't reach your local clipboard. Use the workaround below.
|
||||
|
||||
**Temporary workaround**: Use `grok wrap ssh` instead of plain `ssh` (for example, `grok wrap ssh user@host`). It runs the command in a local PTY that intercepts OSC 52 sequences, including tmux-wrapped ones, and writes their contents to your local clipboard. The same command wraps anything else whose clipboard can't reach you — for example `grok wrap docker exec -it <container> bash` or `grok wrap kubectl exec -it <pod> -- bash`.
|
||||
|
||||
> **Warning**: `grok wrap` is **experimental** and may misbehave in some setups.
|
||||
|
||||
**iTerm2 setting**:
|
||||
iTerm2 requires explicit permission for OSC 52:
|
||||
|
||||
1. iTerm2 → **Settings** → **General** → **Selection**
|
||||
2. Enable **"Applications in terminal may access clipboard"**
|
||||
|
||||
This setting is off by default for security reasons. Without it, OSC 52 writes from Grok (or any TUI) will be ignored.
|
||||
|
||||
**Fix for other cases**:
|
||||
- `set -g set-clipboard on` in tmux config
|
||||
- For other terminals over SSH, switch to iTerm2, Ghostty, WezTerm, or Kitty for native OSC 52 support
|
||||
|
||||
### Problem: Fullscreen / alternate screen not activating (inline mode)
|
||||
|
||||
**Cause**: Zellij, tmux control mode (`tmux -CC`), or config set to `never`.
|
||||
|
||||
**Fix**:
|
||||
- In Zellij or control mode, Grok intentionally runs inline (no alt screen).
|
||||
- Set `[terminal] alt_screen = "always"` in `~/.grok/pager.toml` to force fullscreen.
|
||||
- Use the CLI flag `--no-alt-screen` to disable alt-screen mode entirely (useful for debugging or when the alternate screen causes issues in your terminal).
|
||||
|
||||
### Problem: Zellij keybindings interfere with Grok (Ctrl+g, Ctrl+o, etc.)
|
||||
|
||||
Zellij intercepts many Ctrl/Alt key combinations before they reach full-screen TUIs like Grok.
|
||||
|
||||
**Best fix** (Zellij 0.41+): Switch to the **"Unlock-First (non-colliding)"** preset:
|
||||
|
||||
1. Press `Ctrl+o` → `c` (open Configuration)
|
||||
2. Go to **"Change Mode Behavior"**
|
||||
3. Select **"Unlock-First (non-colliding)"**
|
||||
4. Press `Enter` (or `Ctrl+a` to save permanently)
|
||||
|
||||
After this, Zellij starts **locked**. Most keys pass through to Grok. Press `Ctrl+g` to temporarily unlock Zellij when you need its pane/session management.
|
||||
|
||||
Zellij recommends this approach for TUI users.
|
||||
|
||||
### Problem: `Ctrl+Enter` doesn't interject in WezTerm
|
||||
|
||||
**Cause**: WezTerm ships with the Kitty keyboard protocol disabled. Grok relies on it to tell `Ctrl+Enter` (interject) and `Shift+Enter` (send in multiline mode) apart from plain `Enter`. Most other terminals enable the protocol when Grok requests it.
|
||||
|
||||
For the same reason, in Apple Terminal, Grok binds `Ctrl+O` to interject.
|
||||
|
||||
**Fix**:
|
||||
|
||||
Add this after `config = wezterm.config_builder()` in `~/.config/wezterm/wezterm.lua`:
|
||||
|
||||
```lua
|
||||
config.enable_kitty_keyboard = true
|
||||
```
|
||||
|
||||
Reload (`Cmd+Shift+R` or restart WezTerm) and restart `grok`.
|
||||
|
||||
**Verify**: Run `/terminal-setup` inside Grok. While a turn is active, you see the interject hint, and `Ctrl+Enter` interjects.
|
||||
|
||||
**Quick workaround** (no global change):
|
||||
|
||||
```lua
|
||||
table.insert(config.keys, {
|
||||
key = "Enter",
|
||||
mods = "CTRL",
|
||||
action = wezterm.action.SendString("\x1b[13;5u"),
|
||||
})
|
||||
```
|
||||
|
||||
### Problem: `Shift+Enter` doesn't insert a newline in VS Code
|
||||
|
||||
**Cause**: VS Code's integrated terminal (and the Cursor / Windsurf / Zed
|
||||
forks) use xterm.js, which only partially implements the Kitty keyboard
|
||||
protocol — it mis-encodes shifted printable keys (`!@#$%^&*()` arrive as
|
||||
plain digits). Grok therefore never negotiates the protocol for these
|
||||
terminals. Without it, xterm.js sends a bare `CR` for `Shift+Enter`,
|
||||
byte-for-byte identical to plain `Enter`, so the chord can't be told apart
|
||||
and the prompt submits.
|
||||
|
||||
This also affects VS Code reached **over SSH** (e.g. into a devbox or
|
||||
container): `TERM_PROGRAM` isn't forwarded, so Grok sees an `Unknown`
|
||||
terminal and skips the protocol for the same reason.
|
||||
|
||||
**Fix**: Use **`Alt+Enter`** to insert a newline. xterm.js delivers it
|
||||
reliably as `ESC`+`CR` regardless of the keyboard protocol, and Grok's
|
||||
prompt hint bar advertises `Alt+Enter: newline` whenever it detects this
|
||||
situation. Run `/terminal-setup` to confirm — the `newline` row shows
|
||||
`Alt+Enter` when `Shift+Enter` is unavailable.
|
||||
|
||||
### Problem: Mouse scrolling stops working (native scrollbar takes over)
|
||||
|
||||
If Grok's mouse-driven scrolling stops responding and your terminal falls back to its native scrollbar, mouse reporting is off.
|
||||
|
||||
**Apple Terminal**: Go to **View > Allow Mouse Reporting** (keyboard shortcut `Cmd+R`) to re-enable it. A checkmark appears next to the option when active.
|
||||
|
||||
**iTerm2**: Open **Settings** (`Cmd+,`) → **Profiles** → **Terminal** → ensure **"Enable mouse reporting"** is checked. Alternatively, restart iTerm2.
|
||||
|
||||
### Problem: Byobu + GNU screen
|
||||
|
||||
Byobu on screen has best-effort support only. Prefer Byobu on tmux.
|
||||
|
||||
---
|
||||
|
||||
## Still Stuck?
|
||||
|
||||
Run `/feedback` to report it.
|
||||
|
|
@ -0,0 +1,478 @@
|
|||
# Permissions and Safety Controls
|
||||
|
||||
Grok can read files, search code, edit files, and run shell commands. The permission system controls what the agent is allowed to do. You can combine several independent layers: permission rules, permission modes, hooks, and the OS-level sandbox.
|
||||
|
||||
This guide explains how a tool call is authorized, how to configure permission rules from the CLI, native configuration, or Claude settings, and how to use `PreToolUse` hooks for allow lists that apply in every mode.
|
||||
|
||||
---
|
||||
|
||||
## How a Tool Call Is Authorized
|
||||
|
||||
When the model requests a tool, the following checks happen in order:
|
||||
|
||||
1. **`PreToolUse` hooks**. A hook can deny a tool call before any other check. A hook that allows a call does not skip the checks below; it only declines to deny. See [10-hooks.md](10-hooks.md).
|
||||
|
||||
2. **Permission rules** (from configuration files or `--allow`/`--deny` flags)
|
||||
- A matching `deny` rule rejects the call. `deny` wins over every other rule.
|
||||
- A matching `ask` rule prompts you, including for file reads, searches, and shell commands that would otherwise be auto-approved.
|
||||
- A matching `allow` rule approves the call.
|
||||
|
||||
3. **Remembered grants**. Per-command approvals you saved from earlier prompts apply here, scoped to the current project. An existing grant can satisfy an `ask` rule instead of re-prompting. Commands on the [dangerous list](#dangerous-commands) prompt again rather than using a remembered prefix. See [Interactive Approvals](#interactive-approvals-and-where-they-persist).
|
||||
|
||||
4. **Built-in auto-approvals**. Read-only tools and a fixed set of read-only shell commands run without prompting (see below).
|
||||
|
||||
5. **Prompt policy** (set by the [permission mode](#permission-modes)): prompt you, auto-approve, or auto-deny the call.
|
||||
|
||||
Always-approve mode (`bypassPermissions`) short-circuits this pipeline after step 2: `deny` rules, hooks, and `ask` rules that match a shell command's segments still apply, but remembered grants (including remembered "never allow" entries) are not consulted, and `ask` rules on non-shell tools do not prompt.
|
||||
|
||||
---
|
||||
|
||||
## Operations That Never Prompt by Default
|
||||
|
||||
The operations below are treated as read-only and run without prompting, in every mode including `dontAsk`, unless a matching `deny` rule or a hook blocks them. An `ask` rule forces a prompt for file reads, searches, and shell commands (see [How a Tool Call Is Authorized](#how-a-tool-call-is-authorized)).
|
||||
|
||||
### Read-Only Tools
|
||||
|
||||
- `read_file`
|
||||
- `list_dir`
|
||||
- `grep` (content search)
|
||||
- `web_search`
|
||||
- `todo_write`
|
||||
- `get_command_or_subagent_output` / `wait_commands_or_subagents` / `kill_command_or_subagent` (subagent control)
|
||||
- Invoking skills
|
||||
|
||||
### Read-Only Shell Commands
|
||||
|
||||
After splitting chained commands (on `&&`, `||`, `;`, and pipes), the following commands are recognized as read-only when they appear as the primary command. This list is word-boundary matched, so `ls` does not match `lsof` or `less`. (Your own `Bash(...)` rules match differently; see [Rule Matching Reference](#rule-matching-reference).)
|
||||
|
||||
**Filesystem (read-only 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 and inspection:**
|
||||
- `grep`, `rg` (not `rg --pre` / `rg --pre=…`, which spawn a preprocessor per file)
|
||||
|
||||
**Build and check (read-only):**
|
||||
- `cargo check`
|
||||
|
||||
**Kubernetes (read-only):**
|
||||
- `kubectl get`, `kubectl logs`, `kubectl describe`
|
||||
|
||||
> **Note:** `tee` is not on this list because it can write its input to arbitrary files.
|
||||
|
||||
These checks apply per segment. In a command like `ls && rm -rf /`, the `ls` segment is recognized as read-only, but the `rm` segment is not on the list. In `default` mode the `rm` segment prompts; under `dontAsk` it is denied.
|
||||
|
||||
---
|
||||
|
||||
## Permission Modes
|
||||
|
||||
The prompt policy is named by one of these modes:
|
||||
|
||||
| Mode | Behavior | Typical Use |
|
||||
|---------------------|--------------------------------------------------------------------------|---------------------------------|
|
||||
| `default` | Prompt for anything not pre-approved | Daily interactive use |
|
||||
| `dontAsk` | Deny anything without an explicit allow rule or built-in auto-approval | Headless, CI, high-security |
|
||||
| `bypassPermissions` | Auto-approve tool calls (`deny` rules, hooks, and shell `ask` rules still apply) | Trusted environments |
|
||||
| `acceptEdits` | Auto-approve file edits (`search_replace`, `write`, etc.) | "Accept edits" workflows |
|
||||
| `plan` | Accepted for compatibility; plan sessions are a separate feature (see [19-plan-mode.md](19-plan-mode.md)) | Structured planning sessions |
|
||||
|
||||
### Setting the Mode
|
||||
|
||||
The mode is set by `defaultMode` in `.claude/settings.json` (see [Claude Code Compatibility](#3-claude-code-compatibility-claudesettingsjson)). `dontAsk`, `acceptEdits`, and `bypassPermissions` change the prompt policy from there; `default` and `plan` keep standard prompting.
|
||||
|
||||
The `--permission-mode` CLI flag applies `bypassPermissions` (always-approve) and `default`; an explicit flag value always wins over a mode set in configuration. Passing `dontAsk`, `acceptEdits`, or `plan` to the flag is accepted but does not enable that policy; set those through `defaultMode` instead.
|
||||
|
||||
In headless runs (`-p`), a tool call that would prompt is cancelled and reported to the model instead of waiting for input. For deny-by-default in automation, set `defaultMode: "dontAsk"`.
|
||||
|
||||
### Disabling Always-Approve Mode
|
||||
|
||||
Administrators can turn always-approve (`bypassPermissions` / `--always-approve`) off so it cannot be enabled from the CLI, the TUI toggle, or the `/always-approve` command. Set the dedicated key in `requirements.toml`:
|
||||
|
||||
```toml
|
||||
[ui]
|
||||
disable_bypass_permissions_mode = true # default: false. true = locked off.
|
||||
```
|
||||
|
||||
Do not use `permission_mode` for this; it is a user-switchable default, not a lock. The legacy `[ui] yolo = false` key in `requirements.toml` also disables the mode, for backward compatibility; in `config.toml` the same key remains a togglable preference.
|
||||
|
||||
The user-level `~/.grok/requirements.toml` is under the user's control, so a developer can remove the lock by editing that file. For enforcement that users cannot override, deploy the setting in the root-owned system file `/etc/grok/requirements.toml`.
|
||||
|
||||
> **Note:** Grok honors the permission rules in Claude Code's `managed-settings.json`, but not its `disableBypassPermissionsMode` lock. To disable always-approve in Grok, use `requirements.toml` as shown above.
|
||||
|
||||
---
|
||||
|
||||
## Configuring Permissions
|
||||
|
||||
Grok reads permission rules from three compatible sources. Rules from all sources are merged into one set; a rule's effect depends on its action (`deny` > `ask` > `allow`), not on which file it came from.
|
||||
|
||||
### Where Permission Rules Live (Scopes)
|
||||
|
||||
Permission rules can be global (all projects), project-scoped (one repository), or personal to you within a project:
|
||||
|
||||
| Scope | File | Shared with teammates |
|
||||
|-------|------|-----------------------|
|
||||
| Global (all projects) | `~/.grok/config.toml` | No |
|
||||
| Project (committed) | `<project>/.grok/config.toml` | Yes (commit it) |
|
||||
| Project (personal) | `<project>/.claude/settings.local.json` | No (gitignore it) |
|
||||
| Interactive grants | Stored internally by Grok, per project | No |
|
||||
|
||||
Notes on scoping:
|
||||
|
||||
- Grok discovers a `.grok/config.toml` at every directory level from the repository root down to your working directory, so a subdirectory can add rules on top of the repo root's.
|
||||
- Rules from all scopes are merged into one rule set; `deny` > `ask` > `allow` applies across scopes, so a global `deny` cannot be overridden by a project `allow`.
|
||||
- Grok has no native `config.local.toml`. For personal, uncommitted rules in a project, use `.claude/settings.local.json`; Grok reads it directly (see [Claude Code Compatibility](#3-claude-code-compatibility-claudesettingsjson)).
|
||||
- Interactive "Always allow" decisions are stored outside the repository, scoped to the project (see [Interactive Approvals](#interactive-approvals-and-where-they-persist)).
|
||||
|
||||
To stop prompts for a specific command in one project, add a narrow allow rule to that project's `.grok/config.toml` (or `.claude/settings.json`):
|
||||
|
||||
```toml
|
||||
[permission]
|
||||
allow = ["Bash(cargo test *)", "Bash(npm run build)"]
|
||||
```
|
||||
|
||||
This approves only the listed commands. Always-approve mode, by contrast, approves all tool calls.
|
||||
|
||||
### 1. CLI Flags
|
||||
|
||||
```bash
|
||||
grok -p "Review the API changes" \
|
||||
--allow 'Bash(git *)' \
|
||||
--allow 'Bash(gh *)' \
|
||||
--allow 'Read' \
|
||||
--allow 'Grep' \
|
||||
--deny 'Bash(rm -rf *)'
|
||||
```
|
||||
|
||||
`--allow RULE` and `--deny RULE` can be repeated and are always enforced.
|
||||
|
||||
Rule syntax examples:
|
||||
- `Bash(git *)` — any command starting with `git `
|
||||
- `Bash(npm run build)` — exact command (or prefix)
|
||||
- `Bash(git commit:*)` — the `cmd:*` suffix form, equivalent to prefix matching on `git commit`
|
||||
- `Read(src/**)` — read access under `src/`
|
||||
- `Edit(**/*.rs)` — edit any Rust file
|
||||
- `Grep` — all grep operations
|
||||
- `MCPTool(my-server__*)` — MCP tools from a specific server
|
||||
|
||||
See [Rule Matching Reference](#rule-matching-reference) for the exact matching semantics, including how chained commands and wildcards are evaluated.
|
||||
|
||||
### 2. Native Configuration (`~/.grok/config.toml` and `.grok/config.toml`)
|
||||
|
||||
```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 *" }, # block a dangerous pattern
|
||||
{ action = "ask", tool = "edit" },
|
||||
]
|
||||
```
|
||||
|
||||
The structured `tool` field accepts the lowercase names `bash`, `read`, `edit`, `grep`, `mcp`, `webfetch`, and `websearch`, corresponding to the tool classes in [Tool Names](#tool-names).
|
||||
|
||||
Because `deny` always wins, you cannot combine these `allow` rules with a catch-all `deny` on `bash` to mean "only allow git/gh"; a `deny tool = "bash"` rule would block `git` and `gh` too. For deny-by-default, use `defaultMode: "dontAsk"` in `.claude/settings.json` or a `PreToolUse` hook (below).
|
||||
|
||||
Rules from the global `~/.grok/config.toml` and every project `.grok/config.toml` (from the repo root down to your working directory) are merged into one rule set, alongside any `.claude/settings.json` rules.
|
||||
|
||||
Managed configuration deployed by your organization also contributes `[permission]` rules: the system `/etc/grok/managed_config.toml`, and a user-level copy that Grok maintains automatically at `~/.grok/managed_config.toml`. Managed rules merge like rules from any other source, with two properties specific to managed `allow` rules: your own `deny` and `ask` rules win over a managed `allow` (severity ordering), and a catch-all managed `allow` is ignored when always-approve is locked off. For rules that users cannot edit away, use the root-owned system `/etc/grok/requirements.toml`.
|
||||
|
||||
Permission rules from every source are read once, when a session starts. Changes apply to the next session.
|
||||
|
||||
The native `[permission]` section also accepts the compact `allow` / `deny` / `ask` string-array form, using the same rule strings as the `--allow` / `--deny` flags and `.claude/settings.json`:
|
||||
|
||||
```toml
|
||||
[permission]
|
||||
deny = [
|
||||
"Read(/Users/you/private/**)",
|
||||
"Edit(/Users/you/private/**)",
|
||||
"Bash(rm -rf *)",
|
||||
]
|
||||
allow = [
|
||||
"Bash(git *)",
|
||||
"Bash(gh *)",
|
||||
]
|
||||
```
|
||||
|
||||
`deny` always wins over `allow` (evaluation is `deny` > `ask` > `allow`), regardless of order or source. To block reads of paths outside your project at the OS level as well, combine deny rules with the `strict` sandbox profile (see [18-sandbox.md](18-sandbox.md)).
|
||||
|
||||
### 3. Claude Code Compatibility (`.claude/settings.json`)
|
||||
|
||||
Grok reads `~/.claude/settings.json` and `~/.claude/settings.local.json`, plus the project-level `<project>/.claude/settings.json` and `settings.local.json` (walking up to the repo root). The native `.grok` source for permission rules is `config.toml`, described in the section above.
|
||||
|
||||
Example:
|
||||
|
||||
```json
|
||||
{
|
||||
"permissions": {
|
||||
"defaultMode": "dontAsk",
|
||||
"allow": [
|
||||
"Read",
|
||||
"Grep",
|
||||
"Bash(git *)",
|
||||
"Bash(gh *)"
|
||||
],
|
||||
"deny": [
|
||||
"Bash(rm -rf *)"
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Supported `defaultMode` values are `default`, `acceptEdits`, `bypassPermissions`, `dontAsk`, and `plan`. Grok reads `defaultMode` from its canonical location under `permissions`; a top-level `defaultMode` is also accepted when the nested key is absent.
|
||||
|
||||
`permissions.allow`, `permissions.deny`, and `permissions.ask` entries are translated into native rules and then matched with the semantics in the [Rule Matching Reference](#rule-matching-reference). Translation notes:
|
||||
|
||||
- Rules for MCP tools must use the `MCPTool(server__tool)` form; the `mcp__server__tool` form never matches (see [MCP Rules](#mcp-rules)).
|
||||
- Rules naming an unrecognized tool, and parameter rules such as `Agent(model:opus)`, are skipped with a warning rather than failing the load.
|
||||
- `permissions.additionalDirectories` is parsed but not supported.
|
||||
|
||||
You can import existing Claude settings interactively with **Ctrl+I** ("Import Claude settings").
|
||||
|
||||
---
|
||||
|
||||
## Rule Matching Reference
|
||||
|
||||
This section defines exactly how rules are matched.
|
||||
|
||||
### Bash Rules
|
||||
|
||||
A `Bash(...)` pattern matches a command in either of two ways:
|
||||
|
||||
- **Prefix**: the command starts with the pattern text, compared character for character. There is no word-boundary requirement, so `Bash(git)` matches `gitleaks` as well as `git status`. Include a trailing space and wildcard (`Bash(git *)`) to require the prefix to be a whole word.
|
||||
- **Glob**: the pattern matches the whole command as a glob. `*` can appear at any position and matches any characters, including spaces and slashes, so `Bash(git * main)` matches `git checkout main`. `?` and `[...]` are also supported.
|
||||
|
||||
Matching is case-sensitive. Leading whitespace in the command is trimmed before matching; nothing else is normalized.
|
||||
|
||||
A trailing `:*` suffix on a Bash rule is stripped to a plain prefix: `Bash(git commit:*)` becomes prefix `git commit`. Because prefixes have no word boundary, a `deny` written as `Bash(sed:*)` also blocks commands such as `sed-custom`.
|
||||
|
||||
**Chained commands.** Grok parses each command like a shell and splits it on `&&`, `||`, `;`, `|`, and newlines. The rule actions treat segments differently:
|
||||
|
||||
- `deny` and `ask` rules are checked against every segment, and against the whole string. One denied segment rejects the entire command.
|
||||
- `allow` rules are checked against the whole command string only. `Bash(git *)` therefore auto-approves `git status && rm -rf /`, because the full string starts with `git `. Pair narrow allow rules with `deny` rules for the patterns you want to block.
|
||||
|
||||
Commands that cannot be split into simple segments (subshells, command substitution `$(...)`, backticks, background `&`, control flow) prompt as a single unit when Bash restrictions are configured.
|
||||
|
||||
Segment-level checks (`deny` and `ask` rules, remembered grants, and the read-only command list) strip environment-variable prefixes such as `RUST_LOG=debug`, and peel a fixed set of process wrappers (`timeout`, `nice`, `ionice`, `chrt`, `stdbuf`, `env`) so that `deny` and `ask` rules match either the wrapped or the inner command. `deny` and `ask` rules are also checked inside inline scripts passed to `bash -c`. Other wrappers, including `sudo`, `xargs`, and `nohup`, are not peeled; write rules that include them explicitly. `allow` rules do not get this treatment: they match the command string as written, so a leading environment assignment or wrapper keeps an `allow` rule from matching and the command prompts instead.
|
||||
|
||||
### Dangerous Commands
|
||||
|
||||
A built-in list (`rm`, `chmod`, `chown`, `chgrp`, `chattr`, `pkill`, `kill`, `killall`, `git push`) prompts even when a segment is covered by a remembered command prefix or the read-only command list. An explicit `allow` rule in configuration does approve them, and always-approve mode auto-approves them like any other command; use `deny` rules to block them unconditionally. Review rules like `Bash(rm *)` carefully before adding them as allow rules.
|
||||
|
||||
### Read, Edit, and Grep Rules
|
||||
|
||||
Path patterns are globs matched against the path string the tool was called with:
|
||||
|
||||
- `*` and `?` do not cross `/`; `**` does. `Read(src/*)` matches `src/main.rs` but not `src/nested/mod.rs`; use `Read(src/**)` for the whole tree.
|
||||
- A bare filename matches only that exact string. Use `**/.env` to match `.env` at any depth.
|
||||
- There are no anchor prefixes: a leading `//` or `~/` in a pattern is treated as literal glob text. Write absolute-path patterns or `**/` patterns instead.
|
||||
- Paths are matched as given, without canonicalization. Whether a path is absolute or relative depends on how the tool was invoked, so patterns intended as boundaries should cover both forms (for example both `/repo/secrets/**` and `secrets/**`).
|
||||
- `Read` rules also govern `grep` searches; `Grep(...)` rules match only grep.
|
||||
|
||||
`Read` and `Edit` deny rules additionally apply to file paths that shell commands touch (for example `cat` or `sed` on a denied path), and that shell-level check resolves symlinks. The direct `read_file`/`search_replace` tool checks do not resolve symlinks. For OS-level enforcement that covers every process, combine deny rules with the sandbox ([18-sandbox.md](18-sandbox.md)).
|
||||
|
||||
### MCP Rules
|
||||
|
||||
`MCPTool(...)` patterns match the full Grok tool name in `server__tool` form, with glob support: `MCPTool(linear__*)` matches every tool from the `linear` server. Grok tool names carry no `mcp__` prefix, so a rule written as `mcp__server__tool` never matches an MCP call; write `MCPTool(server__tool)` instead.
|
||||
|
||||
### WebFetch Rules
|
||||
|
||||
- `WebFetch(domain:example.com)` matches that host and every subdomain (`api.example.com`), case-insensitively, ignoring a leading `www.`. Wildcards are not supported inside `domain:` patterns.
|
||||
- A pattern without the `domain:` prefix globs against the entire URL: `WebFetch(https://api.example.com/*)`.
|
||||
|
||||
### Tool Names
|
||||
|
||||
Recognized tool names: `Bash`, `Read` (and `NotebookRead`), `Edit` (and `Write`, `NotebookEdit`), `Grep` (and `Glob`), `MCPTool`, `WebFetch`, `WebSearch`. A bare `*` rule matches every tool. Globs are not supported in the tool-name position.
|
||||
|
||||
Rules naming an unrecognized tool (for example `Agent(model:opus)`) are skipped with a warning rather than failing the load.
|
||||
|
||||
### Evaluation Order
|
||||
|
||||
Rules from every source are merged into one set and evaluated by severity, not order: any matching `deny` rejects, otherwise any matching `ask` prompts, otherwise any matching `allow` approves. When no rule matches, the request falls through to the built-in auto-approvals and then the prompt policy, as described in [How a Tool Call Is Authorized](#how-a-tool-call-is-authorized).
|
||||
|
||||
---
|
||||
|
||||
## Interactive Approvals and Where They Persist
|
||||
|
||||
When a tool call requires approval, the permission prompt offers these choices:
|
||||
|
||||
- **Allow once**: approve this single invocation.
|
||||
- **Reject once**: reject it, optionally with a message back to the model.
|
||||
- **Enable always-approve mode**: approves all future tool calls, not just the one being prompted.
|
||||
- **Allow all edits this session**: shown for file edits. This grant is held in memory only and does not survive a restart.
|
||||
|
||||
### Per-Command "Always Allow"
|
||||
|
||||
A narrower set of options remembers just the specific command, MCP tool, or web-fetch domain being prompted, for example "Always allow `cargo test`". These rows are off by default. Enable them with:
|
||||
|
||||
```toml
|
||||
# ~/.grok/config.toml
|
||||
[ui]
|
||||
remember_tool_approvals = true
|
||||
```
|
||||
|
||||
With the gate enabled, prompts gain:
|
||||
|
||||
- **`Always allow: <command>`**, which persists an allow for the command prefix.
|
||||
- A matching "never allow" row, which persists a deny the same way.
|
||||
- Equivalent "always allow" rows for MCP tools and web-fetch domains.
|
||||
|
||||
The remembered prefix is limited to a short form of the command: read-only commands persist just their listed prefix (for example `git status`, not the full argument list), and other commands persist a short leading prefix. The prompt shows exactly what will be remembered before you confirm. Commands on the [dangerous list](#dangerous-commands) prompt again rather than using a remembered prefix.
|
||||
|
||||
### Persistence Is Per Project
|
||||
|
||||
Interactive grants are stored in Grok's own state directory under your home directory, scoped to the directory you launched Grok from. A grant made in one project never applies in another, grants are not written into the repository, and they are not meant to be hand-edited.
|
||||
|
||||
Interactive grants are personal, per-machine state. For an allowlist you can review in code review and share with teammates, use declarative rules in the project's `.grok/config.toml` instead.
|
||||
|
||||
---
|
||||
|
||||
## Restricting Bash to Specific Commands with a Hook
|
||||
|
||||
A `PreToolUse` hook can enforce an allow list on the `Bash` tool that applies in every permission mode. Hooks are evaluated before the permission system; a hook deny stops the call, and a hook allow falls through to the normal permission checks (so your `deny` rules still apply).
|
||||
|
||||
> **Note:** Hooks fail open. If a hook script crashes, times out, or is missing, the tool call proceeds as if the hook had allowed it, and the failure is reported in the UI. A hook used as a security boundary must handle its own errors, and must account for chained commands, as the example below does. See [10-hooks.md](10-hooks.md).
|
||||
|
||||
### Example: Allow 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
|
||||
# Allow only git and gh commands, including within chained commands.
|
||||
|
||||
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"
|
||||
|
||||
# Normalize '&&' and '||' to ';' so chains can be checked segment by
|
||||
# segment, then reject constructs this script cannot inspect.
|
||||
CMD=$(echo "$CMD" | sed 's/&&/;/g; s/||/;/g')
|
||||
case "$CMD" in
|
||||
*'$('*|*'`'*|*'&'*|*'>'*|*'<'*) deny "Substitution, background, and redirection are not permitted" ;;
|
||||
esac
|
||||
|
||||
# Split on the separators and require every segment to start with git or gh.
|
||||
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
|
||||
```
|
||||
|
||||
This hook denies every `Bash` command unless each chained segment starts with `git` or `gh`, and rejects command substitution, backgrounding, and redirection outright because it cannot verify what they execute. It works in every permission mode.
|
||||
|
||||
For hook installation, the JSON format, the trust model for project hooks, and other events, see [10-hooks.md](10-hooks.md), which also contains a complementary "block dangerous patterns" example.
|
||||
|
||||
---
|
||||
|
||||
## Example Configurations
|
||||
|
||||
### Headless git and gh Only (CI and Automation)
|
||||
|
||||
```bash
|
||||
grok -p "Implement the feature using only git and GitHub CLI" \
|
||||
--allow 'Read' \
|
||||
--allow 'Grep' \
|
||||
--allow 'Bash(git *)' \
|
||||
--allow 'Bash(gh *)'
|
||||
```
|
||||
|
||||
Install the `git-gh-only` hook above to deny every other `Bash` command. For deny-by-default on all tools, also set `{"permissions": {"defaultMode": "dontAsk"}}` in `.claude/settings.json`.
|
||||
|
||||
### 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(...)` allow rules for the commands you run most (`git`, `cargo test`, `rg`, and similar).
|
||||
|
||||
---
|
||||
|
||||
## Combining with the Sandbox
|
||||
|
||||
Permissions control what the model is allowed to request. The OS-level sandbox (see [18-sandbox.md](18-sandbox.md)) controls what the process can do even after a command is approved.
|
||||
|
||||
Recommended combination for untrusted code:
|
||||
|
||||
1. `dontAsk` plus narrow allow rules, or a restrictive hook
|
||||
2. `--sandbox strict` or a custom profile
|
||||
3. Project trust plus review of any `SessionStart` hooks
|
||||
|
||||
---
|
||||
|
||||
## Managing Permissions in the TUI
|
||||
|
||||
- Permission decisions appear in the transcript.
|
||||
- The `/always-approve` command toggles always-approve mode; other modes are set through `defaultMode` (see [Setting the Mode](#setting-the-mode)).
|
||||
- With `[ui] remember_tool_approvals = true`, permission prompts include per-command "Always allow" options that persist for the current project only. See [Interactive Approvals](#interactive-approvals-and-where-they-persist).
|
||||
- To manage hooks and plugins, run `/hooks` or `/plugins` (on most terminals, **Ctrl+L** also opens the Extensions modal; on VS Code, Cursor, Windsurf, and Zed, `Ctrl+L` is mid-turn interject instead). See [10-hooks.md](10-hooks.md).
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Prefer narrow patterns.** `Bash(git *)` grants less access than a bare `Bash` allow rule.
|
||||
2. **Combine layers.** `dontAsk`, narrow allow rules, a restrictive hook, and the sandbox each restrict independently.
|
||||
3. **Review project configuration from unfamiliar sources.** Project permission rules in `.grok/config.toml` and `.claude/settings.json`, including `allow` rules, apply without a separate trust prompt. Review them, and any project hooks, before working in an unfamiliar checkout (see the security notes in [10-hooks.md](10-hooks.md)).
|
||||
4. **Test your policy.** With `defaultMode: "dontAsk"` set (or your `PreToolUse` hook installed), run representative commands and confirm what is blocked.
|
||||
5. **Treat the read-only command list as a convenience, not a security boundary.**
|
||||
|
||||
---
|
||||
|
||||
## See Also
|
||||
|
||||
- [10-hooks.md](10-hooks.md) — Hook authoring guide
|
||||
- [14-headless-mode.md](14-headless-mode.md) — Headless flags, including permission-related ones
|
||||
- [18-sandbox.md](18-sandbox.md) — OS-level isolation profiles
|
||||
- [05-configuration.md](05-configuration.md) — Native `config.toml` structure
|
||||
351
crates/codegen/xai-grok-pager/docs/user-guide/23-dashboard.md
Normal file
351
crates/codegen/xai-grok-pager/docs/user-guide/23-dashboard.md
Normal file
|
|
@ -0,0 +1,351 @@
|
|||
# Agent Dashboard
|
||||
|
||||
The Agent Dashboard is a centralised, agent-native overview of every
|
||||
top-level session you have in flight — your local sessions and forks
|
||||
— grouped by state, with peek, attach, and dispatch from one screen.
|
||||
Subagents are not listed here: they run under their parent session,
|
||||
which already shows when work is in flight.
|
||||
|
||||
---
|
||||
|
||||
## Opening the dashboard
|
||||
|
||||
Three entry points, all opening the same view:
|
||||
|
||||
- **`grok dashboard`** — launches the TUI directly into the dashboard.
|
||||
- **`/dashboard`** (aliases **`/agents-dashboard`**, **`/sessions`**) — open
|
||||
from inside an active session.
|
||||
- **Ctrl+\\** — same as the slash command, two keystrokes. Configurable
|
||||
in `~/.grok/config.toml` under `[keybindings]` like every other shortcut.
|
||||
|
||||
---
|
||||
|
||||
## What you see
|
||||
|
||||
```
|
||||
Grok Build · Dashboard — 4 agents · 2 awaiting
|
||||
▌● reviewer · audit token flow Awaiting your input 2m
|
||||
● implementer · fix login bug Running: cargo test 12m
|
||||
⋅ refactor · feat/login Responding… 24m
|
||||
○ housekeeping idle 1h
|
||||
● implementer · add login tests 8 tools · 1.2k tok 14m
|
||||
╭─────────────────────────────────────────────────────────────────╮
|
||||
│ ❯ Dispatch a new agent │
|
||||
╰─ dispatch ──────────────────────────────────────────────────────╯
|
||||
↑/↓ select (peek) · Enter open · Ctrl+R rename · Ctrl+T pin · Ctrl+X stop · ? help · Esc new
|
||||
```
|
||||
|
||||
Each row is a top-level agent (subagents aren't shown — they run under
|
||||
their parent). Rows are sorted by state (Needs input → Working → Idle →
|
||||
Inactive → Completed → Failed) so same-state rows sit adjacent, or by
|
||||
working directory (toggle with `Ctrl+G`). **Inactive** holds roster-only
|
||||
sessions — idle/dormant sessions owned by other pager processes that
|
||||
haven't been loaded in this one — so **Idle** stays focused on the
|
||||
sessions you're actively cycling between. Because it's background noise,
|
||||
**Inactive starts collapsed** (expand it with `→` / click — see below).
|
||||
|
||||
To keep the **Idle** group scannable, only the most recent idle agents
|
||||
stay visible — the 8 freshest, plus any active within the last hour. The
|
||||
rest fold into a **"N more"** row (marked with a `+` / `-` toggle) at the
|
||||
bottom of the group; select it and press `Enter` / `→` (or click it) to
|
||||
reveal them all, and `←` to re-fold. The Idle header always shows the true total. Folding is
|
||||
suspended while a filter or search is active (so every match shows).
|
||||
|
||||
The state icon matches Grok Build's sibling views (
|
||||
`tasks_pane`):
|
||||
|
||||
- `⋅`/`:`/`⸬`/`⁙` — animated spinner for **Working** rows.
|
||||
- `●` — filled circle for **Needs input**, **Completed**, **Failed**,
|
||||
**Blocked**. Colour communicates the state (yellow / green / red /
|
||||
amber).
|
||||
- `○` — hollow circle for **Idle** and **Inactive** rows.
|
||||
|
||||
A row stays in **Working** while it has live background work even if its
|
||||
turn has finished — a running background task, a `monitor`, or an active
|
||||
scheduled `/loop`. The activity line says what's running (e.g.
|
||||
`watching · 1 monitor · 2 loops`), since each can wake the agent for a
|
||||
new turn.
|
||||
|
||||
There are no inline group headers — the sort order keeps same-state
|
||||
rows adjacent and the per-row dot+colour communicates which group
|
||||
each row belongs to (matching other session lists).
|
||||
|
||||
The dispatch input shares the same `PromptWidget` chrome as the
|
||||
agent view's prompt (rounded box, `❯` prefix, accent border, info
|
||||
line). Pressing `Ctrl+/` flips it into **search mode**: the `❯`
|
||||
prefix becomes a yellow `Search:` and whatever you type live-filters
|
||||
the row list instead of being dispatched.
|
||||
|
||||
---
|
||||
|
||||
## Keybindings
|
||||
|
||||
| Key | Action |
|
||||
| --- | --- |
|
||||
| `↑` / `↓`, `j` / `k` | Navigate rows AND section titles (selecting a row opens its peek panel) |
|
||||
| `→` / `←` (on a section title) | Expand / collapse the section (shows / hides its rows); `l` / `h` in vim mode |
|
||||
| `Enter` (on a section title) | Toggle the section collapsed / expanded |
|
||||
| `Enter` (empty reply) | Open the selected agent's conversation full-screen (details view) |
|
||||
| `Ctrl+S` | Send the peek reply AND open the agent (or dispatch + attach a new session) |
|
||||
| `Shift+Enter` / `Alt+Enter` | Insert a newline in the reply / dispatch input (multiline compose) |
|
||||
| `1`–`9` | Answer a pending permission / ask question (when the peek shows options) |
|
||||
| `Enter` (typed reply) | Send / queue the reply to the selected agent |
|
||||
| `/` | Types a literal `/` into the prompt |
|
||||
| `Ctrl+/` | Toggle search mode (live-filter the rows) |
|
||||
| `Ctrl+R` | Rename selected row |
|
||||
| `Ctrl+T` | Pin / unpin |
|
||||
| `Ctrl+G` | Toggle grouping (state ↔ directory) |
|
||||
| `Ctrl+X` | Stop / kill (two presses within 2s to close a session) |
|
||||
| `Shift+↑` / `Shift+↓` | Reorder pinned rows |
|
||||
| `Esc` | Step back one level: cancel search → close peek (clear reply draft, then unselect) → clear filter → **unfocus the dispatch input** (so `↑`/`↓`, `j`/`k` navigate the list) → unselect row (→ `[+ New Agent]`) → exit dashboard. Esc never clears your typed dispatch draft — use `Ctrl+U` / `Ctrl+C` for that |
|
||||
| `Ctrl+\` | Return to the dashboard from the details view, or exit dashboard |
|
||||
| `Ctrl+.` (alt: `?`) | Open the keyboard shortcuts cheatsheet. Footer advertises `?` when `Ctrl+.` cannot be delivered. Bare `?` opens help when list-focused or the draft is empty (otherwise types); `Ctrl+X` remains stop |
|
||||
|
||||
When grouping by state, each group has a **section title** (e.g. `Working`,
|
||||
`Idle`) with a `▸`/`▾` disclosure marker. Section titles are part of the
|
||||
up/down navigation: select one and press `→` to expand it (showing its rows)
|
||||
or `←` to collapse it — `l` / `h` do the same when vim mode is on.
|
||||
**Clicking** a section title toggles it, and **hovering**
|
||||
brightens its text. Collapse state is remembered while the dashboard stays open.
|
||||
The **Inactive** section starts collapsed by default each time the pager
|
||||
starts; expanding it sticks until you quit.
|
||||
|
||||
Opening a row shows the agent's conversation in the **details view**:
|
||||
a single top header row (the agent name on the left, `{i}/{n} [‹][›]
|
||||
[Dashboard]` cycle/close affordances on the right) sits above the conversation,
|
||||
which renders **full-width** — no bordered modal frame — so the prompt
|
||||
position and overall padding match the dashboard list view. All key
|
||||
presses route to the attached agent; `Esc` / `Ctrl+\\` (or the `[Dashboard]`
|
||||
affordance) return to the dashboard, the `[‹]` / `[›]` chips cycle to
|
||||
the previous / next agent, and the agent's shortcuts bar shows a
|
||||
`Ctrl+\\: back to dashboard` hint. Quick gotcha — `Esc` only returns to
|
||||
the dashboard; typing `/exit` inside the agent actually closes the
|
||||
underlying session (returning to the dashboard with a "Session closed"
|
||||
toast).
|
||||
|
||||
`Ctrl+X` in the details view is state-dependent. While a **turn is
|
||||
running** it cancels the turn — the same behaviour as `Ctrl+C`,
|
||||
including the keep-subagents prompt — and never touches the session
|
||||
itself, so mashing it to stop a turn can't close anything. In any
|
||||
other state — **idle**, a slash command in flight (commands can't
|
||||
be cancelled yet), or a cancel still pending — `Ctrl+X` arms a
|
||||
confirmation: the shortcuts bar flips to "press Ctrl+x again to
|
||||
close this session", and a second press within 2 seconds closes the
|
||||
session and returns you to the dashboard. Pressing any other key
|
||||
cancels the confirmation, and a turn that starts inside the window
|
||||
downgrades the confirmed press to a cancel instead of closing.
|
||||
(On terminals where `Ctrl+X` doubles as the shortcuts-cheatsheet
|
||||
binding, the cheatsheet stays reachable via `Ctrl+.` inside the
|
||||
details view.)
|
||||
|
||||
For the full behavioural specification (including the registry-lookup
|
||||
rules and the mouse-event intercept matrix) see plan
|
||||
[§3.10](../../plan/agent-dashboard.md) "Keybindings (v1)" — the user
|
||||
guide here is intentionally short and cross-references the plan as
|
||||
the source of truth.
|
||||
|
||||
All shortcuts are registered under `When::DashboardFocused` and can be
|
||||
rebound via `~/.grok/config.toml`.
|
||||
|
||||
---
|
||||
|
||||
## Dispatch input
|
||||
|
||||
The bottom textarea **always spawns a NEW session** — it is never a
|
||||
reply target. A selected row is the overview's navigation cursor, not a
|
||||
reply destination; to talk to an existing agent, open it (navigate +
|
||||
`Enter`, or click) and reply inside its own view.
|
||||
|
||||
Enter handler:
|
||||
|
||||
- Free text → creates a new top-level session, seeded with the prompt.
|
||||
Text is **never** reinterpreted as a filter — a prompt may start with
|
||||
`/`, `s:`, `a:`, or `#` and still dispatches verbatim (filtering is
|
||||
the explicit `Ctrl+/` search mode). A leading `/` runs a pager-global
|
||||
slash command.
|
||||
- Empty input → opens the selected row (`Attach`), or creates a new
|
||||
agent when the `[+ New Agent]` button is focused.
|
||||
|
||||
Press `Ctrl+S` after typing a prompt to dispatch AND attach
|
||||
(jump into the new session); plain `Enter` stays on the dashboard so
|
||||
you can dispatch several sessions in a row. `Shift+Enter` / `Alt+Enter`
|
||||
insert a newline for a multi-line prompt — the box **grows in height**
|
||||
as you add lines (up to a cap, after which it scrolls), so the whole
|
||||
draft stays visible.
|
||||
|
||||
The dispatch input accepts any non-empty prompt; an empty /
|
||||
whitespace-only prompt is ignored. Prompts above 64 KiB are rejected
|
||||
with a toast.
|
||||
|
||||
### Focus: input bar ↔ overview list (`Tab`)
|
||||
|
||||
The dashboard has two focus areas — the **dispatch input bar** (typing)
|
||||
and the **overview list** (navigating). `Tab` toggles between them; the
|
||||
inactive input dims its border and hides its caret.
|
||||
|
||||
On open, focus defaults to the **overview list** when at least one agent
|
||||
exists (so `↑`/`↓` / vim `j`/`k` navigate immediately). With **no**
|
||||
agents, focus stays on the **dispatch input** so you can type a first
|
||||
prompt right away. Either way, the `[+ New Agent]` button is the cursor
|
||||
target (no agent row is pre-selected).
|
||||
|
||||
- **Input focused**: type to compose a new-session prompt. `↑`/`↓`
|
||||
navigate the row list when the prompt is empty (a convenience),
|
||||
otherwise move the caret. `Esc` unfocuses the input → overview list
|
||||
(your typed draft is kept) so you can navigate straight away.
|
||||
- **Overview focused**: `↑`/`↓` — and, in **vim mode**, `j`/`k` — move
|
||||
between agent rows. `Enter` opens the highlighted agent (on
|
||||
`[+ New Agent]`, it sends a typed draft, else creates a new session).
|
||||
`Esc` **stays on the list** and steps back — clearing an active filter,
|
||||
then unselecting the row (→ `[+ New Agent]`), then exiting the
|
||||
dashboard. `Tab` or `i` (vim) — or any other printable key — return to
|
||||
the input.
|
||||
|
||||
---
|
||||
|
||||
## Peek panel
|
||||
|
||||
The peek panel is shown **by default whenever an agent row is
|
||||
selected** — it **replaces** the new-session dispatch box. With no row
|
||||
selected (the `[+ New Agent]` button focused, or after `Esc`), the
|
||||
dispatch box returns for starting a new session. So selecting a row is
|
||||
how you talk to an existing agent; deselecting is how you start a new
|
||||
one.
|
||||
|
||||
The panel shows, top to bottom, a header (the **last response type** —
|
||||
`Thinking` / `Thought` / `Response` / `Edit` / `Read` / `Bash` / … — on
|
||||
the left, **time** on the far right), the most recent response
|
||||
(**word-wrapped** to fit, up to ~3 rows), and a live `❯ reply` input. A
|
||||
`…` marker appears on the last row only when there's more than fits.
|
||||
|
||||
The selected agent's **model** and, when it's in always-approve (yolo)
|
||||
mode, an **`always-approve`** flag are shown on the panel's **bottom
|
||||
border** (bottom-right) — the same config-badge slot the new-session
|
||||
dispatch box uses. This holds in the question / approval modes too, so
|
||||
the model and approval mode are always in view while you answer. (The
|
||||
dashboard list rows no longer repeat the model or an always-approve badge,
|
||||
keeping the list compact.)
|
||||
|
||||
**`Shift+Tab` cycles the peeked agent's mode** (Normal → Plan →
|
||||
Always-approve → Normal) — the same cycle as Shift+Tab inside that agent's
|
||||
chat view, applied to the **live** agent (the badge updates to match).
|
||||
This differs from the new-session dispatch box, where Shift+Tab only
|
||||
stages the mode for the *next* agent.
|
||||
|
||||
Unlike the dispatch box (which only ever spawns new sessions), the
|
||||
peek's reply **talks to the selected agent**:
|
||||
|
||||
- **Type into `❯ reply`, then `Enter`** to send. An **idle** agent
|
||||
starts the turn immediately; a **busy** agent **queues** the message
|
||||
so it sends after the current turn finishes (the same queue/drain
|
||||
behaviour as the agent view's own prompt). `Ctrl+S` replies AND
|
||||
opens the agent's detail view; `Shift+Enter` / `Alt+Enter` insert a
|
||||
newline (multiline compose) and the reply **grows in height** to fit
|
||||
the draft (up to a cap, then it scrolls).
|
||||
- With an **empty** reply, `Enter` opens the agent.
|
||||
- **`↑`/`↓` move the caret within the reply** once it has content (so you
|
||||
can edit a multi-line draft). While the reply is **empty** (or
|
||||
unfocused via `Tab`), `↑`/`↓` instead **switch the selected agent** —
|
||||
the panel follows the selection cursor and refreshes live, and the
|
||||
switch clears any half-typed draft so a reply can't land on the wrong
|
||||
agent. (`Tab` to the row list to navigate agents while a draft is in
|
||||
the reply.)
|
||||
- **`Esc` unselects**: it first clears a typed reply, then deselects the
|
||||
row and focuses the `[+ New Agent]` button (bringing back the
|
||||
new-session input).
|
||||
- **`Tab`** toggles focus between the reply input and the row list: an
|
||||
unfocused reply dims its border and hides the caret; a printable key
|
||||
re-focuses it and starts composing.
|
||||
- The reply is a **full prompt editor** (the same component as the
|
||||
dispatch box and the agent prompt): pasting multi-line text folds
|
||||
into a `[Pasted: N lines]` chip with the same preview overlay and
|
||||
expand affordances as the agent prompt (`Enter` / double-click /
|
||||
paste-again), mouse click / drag place the caret and select text,
|
||||
and the usual editing chords work (word navigation, `Ctrl+A`/`Ctrl+E`,
|
||||
`Alt+Backspace`, `Ctrl+W`/`Ctrl+U`/`Ctrl+K`, undo, Shift+arrow
|
||||
selection, `Ctrl+Shift+V` inline paste).
|
||||
Typing **`@`** opens the file-context picker rooted at the **peeked
|
||||
agent's** working directory (so `@path` resolves against the agent
|
||||
you're replying to); its dropdown floats **above** the panel and
|
||||
`↑`/`↓`/`Tab`/`Enter`/`Esc` drive it while it's open.
|
||||
Dashboard chords (`Ctrl+X` stop, `Ctrl+T` pin, `Shift+↑/↓` reorder,
|
||||
…) still win over the editor while the panel is open.
|
||||
- When a **permission / ask-tool question** is pending, the `❯ reply`
|
||||
row is hidden and the options are listed instead: **`↑`/`↓` move the
|
||||
highlighted option** (marked with `▸`) and **`Enter` answers** it.
|
||||
**`1`–`9`** still answer an option directly. (While answering, the
|
||||
arrows pick options rather than switching agents.)
|
||||
- The **free-text row** accepts an inline typed answer (just like the
|
||||
chat panel): the permission **"No" / reject** option ("No, reject
|
||||
(type to add feedback)") and the ask-tool **"Other"** row ("Other
|
||||
(type your own answer)"). Type on it and `Enter` sends the rejection +
|
||||
message / the free-text answer.
|
||||
- This also covers the agent's **Ask tool** (`AskUserQuestion`): its
|
||||
options + the "Other" row show in the peek, answered the same way.
|
||||
**Multi-question** forms are walked one question at a time — a `(i/N)`
|
||||
marker shows progress and each answer advances to the next, submitting
|
||||
on the last. (Forms with a **multi-select** question are left to the
|
||||
agent's own view — open the agent to answer those.)
|
||||
|
||||
The panel only renders when the terminal is tall enough; on very short
|
||||
terminals the dispatch box shows even with a row selected.
|
||||
|
||||
---
|
||||
|
||||
## Search / filter (`Ctrl+/`)
|
||||
|
||||
Filtering lives behind an explicit **search mode** so normal typing
|
||||
always dispatches. Press `Ctrl+/` to toggle it: the prompt prefix
|
||||
flips from `❯` to a yellow `Search:` and every keystroke live-filters
|
||||
the row list.
|
||||
|
||||
Inside search mode:
|
||||
|
||||
- `Enter` — **confirm**: keep the filter applied and return to the
|
||||
dispatch prompt (rows stay filtered; `Esc` later clears them).
|
||||
- `Esc` or `Ctrl+/` — **cancel**: clear the filter and exit search.
|
||||
- `↑` / `↓` — navigate the filtered rows.
|
||||
|
||||
The query supports the same prefixes as before (they are only honoured
|
||||
*inside* search mode now):
|
||||
|
||||
- `a:<name>` — filter by agent label (case-insensitive substring,
|
||||
matches persona / role).
|
||||
- `s:<state>` — filter by row state. Accepts `working`, `idle`,
|
||||
`completed`, `failed`, `needs-input`, `blocked` and synonyms
|
||||
(`busy`/`running`/`done`/etc.).
|
||||
- `#<text>` — substring match on `#<text>` (matches the literal
|
||||
`#` in labels; reserved for future PR filtering).
|
||||
- anything else — plain substring match over label + working dir.
|
||||
|
||||
---
|
||||
|
||||
## Persistence
|
||||
|
||||
Per-user dashboard preferences live under `[dashboard]` in
|
||||
`~/.grok/config.toml`:
|
||||
|
||||
```toml
|
||||
[dashboard]
|
||||
enabled = true
|
||||
grouping = "state" # or "directory"
|
||||
pinned = ["top:<session_id>", "sub:<parent_session_id>:<child_session_id>"]
|
||||
reorder = ["top:<session_id>"]
|
||||
```
|
||||
|
||||
Pinned/reorder entries are keyed by **session id**, not by the
|
||||
per-process `AgentId(usize)`, so they survive restarts and don't
|
||||
attach to whatever agent happens to share the old slot number.
|
||||
|
||||
Set `GROK_AGENT_DASHBOARD=0` to force-disable the feature for a single
|
||||
pager invocation; the slash command and CLI subcommand will print a
|
||||
friendly toast.
|
||||
|
||||
---
|
||||
|
||||
## Phase 4 (out of scope for v1)
|
||||
|
||||
The current dashboard lists only agents owned by **this** pager
|
||||
process. The plan's Phase 4 ("supervisor / `grok --bg`") would list
|
||||
sessions that survive pager exit — that's a separate roadmap and not
|
||||
shipped yet.
|
||||
|
|
@ -0,0 +1,231 @@
|
|||
# Monitoring Usage (External OpenTelemetry)
|
||||
|
||||
> **Status: alpha.** The schema below is versioned (`grok_code.schema.version = v1`);
|
||||
> additive changes may occur without notice, renames/removals will bump the
|
||||
> version and be called out in the changelog.
|
||||
|
||||
Grok CLI can export usage **metrics** and **events** to your organization's
|
||||
own OpenTelemetry collector, so platform teams can monitor adoption, token
|
||||
consumption, tool-permission decisions, and errors across the fleet — without
|
||||
any data flowing through SpaceXAI.
|
||||
|
||||
The external stream is:
|
||||
|
||||
- **Off by default**, and requires a *double opt-in* (a master switch **and**
|
||||
an explicit exporter selection).
|
||||
- **Content-free by default**: no prompts, no code, no file paths (extension
|
||||
only), no tool arguments, no bash commands, and MCP/skill/plugin names
|
||||
collapsed to categories. Optional content gates re-enable some of these.
|
||||
- **Structurally separate** from SpaceXAI-internal telemetry: its exporters carry
|
||||
only the headers you configure, never SpaceXAI credentials.
|
||||
- **Independent of SpaceXAI data-retention opt-outs**: it works even when
|
||||
`telemetry` is disabled and for ZDR (zero-data-retention) teams — those
|
||||
settings govern SpaceXAI-side retention; the external stream is governed solely
|
||||
by your own OTEL configuration.
|
||||
|
||||
## Quick start
|
||||
|
||||
```bash
|
||||
export GROK_EXTERNAL_OTEL=1 # master switch
|
||||
export OTEL_METRICS_EXPORTER=otlp
|
||||
export OTEL_LOGS_EXPORTER=otlp
|
||||
export OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf # or grpc
|
||||
export OTEL_EXPORTER_OTLP_ENDPOINT=https://collector.corp.example:4318
|
||||
export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer <collector-token>"
|
||||
grok
|
||||
```
|
||||
|
||||
`GROK_EXTERNAL_OTEL=1` alone enables **nothing** — you must also select at
|
||||
least one exporter. Conversely, the `OTEL_*` vars alone enable nothing
|
||||
without the master switch.
|
||||
|
||||
## Environment variables
|
||||
|
||||
| Variable | Default | Meaning |
|
||||
|---|---|---|
|
||||
| `GROK_EXTERNAL_OTEL` | `0` | Master switch. Distinct from `GROK_TELEMETRY_ENABLED`, which controls SpaceXAI-internal product analytics — the two govern opposite-pointing data flows. |
|
||||
| `OTEL_METRICS_EXPORTER` | `none` | `otlp` \| `console` \| `none`. |
|
||||
| `OTEL_LOGS_EXPORTER` | `none` | `otlp` \| `console` \| `none`. Gates the event stream. |
|
||||
| `OTEL_EXPORTER_OTLP_PROTOCOL` | `http/protobuf` | `http/protobuf` \| `grpc`. |
|
||||
| `OTEL_EXPORTER_OTLP_ENDPOINT` | `http://localhost:4318` for HTTP, `http://localhost:4317` for gRPC | Base endpoint. For `http/protobuf`, `/v1/logs` and `/v1/metrics` are appended per the OTLP spec; for `grpc`, the collector endpoint is used as-is. |
|
||||
| `OTEL_EXPORTER_OTLP_LOGS_ENDPOINT` / `..._METRICS_ENDPOINT` | — | Signal-specific overrides, used verbatim. For gRPC these should normally be collector endpoints without `/v1/...` paths. |
|
||||
| `OTEL_EXPORTER_OTLP_HEADERS` (+ signal-specific variants) | — | Collector auth (`k=v,k2=v2`). The **only** headers the external exporters send, and the only supported collector-auth mechanism (no config-file headers key — tokens never live on disk). |
|
||||
| `OTEL_EXPORTER_OTLP_TIMEOUT` | `10000` (ms) | Export timeout. |
|
||||
| `OTEL_METRIC_EXPORT_INTERVAL` | `60000` (ms) | Metric export interval. |
|
||||
| `OTEL_BLRP_SCHEDULE_DELAY` (or alias `OTEL_LOGS_EXPORT_INTERVAL`) | `5000` (ms) | Log batch interval. |
|
||||
| `OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE` | `delta` | `delta` \| `cumulative`. |
|
||||
| `OTEL_METRICS_INCLUDE_SESSION_ID` | `1` | Attach `session.id` to metrics (cardinality opt-out). |
|
||||
| `OTEL_METRICS_INCLUDE_VERSION` | `0` | Attach `app.version` to metrics. |
|
||||
| `OTEL_LOG_USER_PROMPTS` | `0` | Content gate: prompt text on `grok_code.user_prompt` (60 KB cap, secret-scrubbed). |
|
||||
| `OTEL_LOG_TOOL_DETAILS` | `0` | Content gate: tool parameters (4 KB cap), full file paths, verbatim MCP/skill/plugin names. Bash command text is **never** exported in v1, even with this gate. |
|
||||
|
||||
`OTEL_RESOURCE_ATTRIBUTES` is deliberately ignored: the resource is built
|
||||
from a fixed, audited attribute set.
|
||||
|
||||
> **Migration note:** older releases could share `OTEL_EXPORTER_OTLP_*` with
|
||||
> the product's own analytics pipeline. That behavior is deprecated: when
|
||||
> `GROK_EXTERNAL_OTEL` is set, product analytics ignores those vars, and the
|
||||
> CLI refuses to activate the external stream in any configuration where
|
||||
> product analytics already consumed them — your collector only receives the
|
||||
> external stream you opted into.
|
||||
|
||||
## Config file
|
||||
|
||||
Org defaults live under the existing `[telemetry]` table in `config.toml`
|
||||
(env vars win). The keys are `otel_`-prefixed peers of the other
|
||||
`[telemetry]` settings:
|
||||
|
||||
```toml
|
||||
[telemetry]
|
||||
otel_enabled = true
|
||||
otel_metrics_exporter = "otlp"
|
||||
otel_logs_exporter = "otlp"
|
||||
otel_endpoint = "https://collector.corp.example:4318"
|
||||
otel_protocol = "http/protobuf" # or "grpc"
|
||||
otel_log_user_prompts = false # admins can pin these via requirements
|
||||
otel_log_tool_details = false
|
||||
```
|
||||
|
||||
The config keys are `otel_*` under `[telemetry]`; the **env vars keep their
|
||||
standard OTEL names** (`GROK_EXTERNAL_OTEL`, `OTEL_*`) for ecosystem
|
||||
interop, so the two layers use deliberately different namespaces. The
|
||||
`otel_protocol` config key maps to `OTEL_EXPORTER_OTLP_PROTOCOL`.
|
||||
|
||||
There is deliberately no `headers` key: supply collector auth via
|
||||
`OTEL_EXPORTER_OTLP_HEADERS` so tokens are never stored on disk.
|
||||
|
||||
Managed deployments can additionally enable org-wide telemetry by distributing
|
||||
the `[telemetry]` `otel_*` keys through `grok setup` managed config /
|
||||
requirements pins, or force-disable it fleet-wide with the same local config
|
||||
layers (`external_otel_disabled`, content-gate locks).
|
||||
|
||||
## Resource attributes
|
||||
|
||||
| Attribute | Value |
|
||||
|---|---|
|
||||
| `service.name` | `grok-cli` |
|
||||
| `service.version`, `client.version` | build/client versions |
|
||||
| `app.entrypoint` | `cli` \| `headless` \| `agent` |
|
||||
| `terminal.type` | terminal emulator brand |
|
||||
| `grok_code.schema.version` | `v1` |
|
||||
|
||||
Identity attributes (`user.id`, and `organization.id` / `team.id` /
|
||||
`deployment.id` when known) are attached per metric data point and per event
|
||||
once authentication completes. `prompt.id` (per-prompt UUID) appears on
|
||||
events only, never metrics.
|
||||
|
||||
## Metrics (meter scope `ai.xai.grok_code`)
|
||||
|
||||
| Metric | Unit | Attributes |
|
||||
|---|---|---|
|
||||
| `grok_code.session.count` | `{session}` | base attrs only |
|
||||
| `grok_code.token.usage` | `{token}` | `type` = `input` \| `output` \| `reasoning` \| `cache_read`; `model` |
|
||||
| `grok_code.turn.count` | `{turn}` | `outcome` = `completed` \| `cancelled` \| `error`; `model` |
|
||||
| `grok_code.tool.decision` | `{decision}` | `tool_name`, `decision` = `allow` \| `deny` \| `cancelled` \| `followup`, `access_kind`, `permission_mode` |
|
||||
| `grok_code.tool.usage` | `{call}` | `tool_name`, `outcome` |
|
||||
| `grok_code.error.count` | `{error}` | `error_category`, `model` |
|
||||
|
||||
There is no `cost.usage` metric: join `grok_code.token.usage` with your own
|
||||
price sheet. `lines_of_code.count` and `active_time.total` are planned for a
|
||||
later phase.
|
||||
|
||||
`tool_name` values: built-in tool names pass verbatim; MCP tools collapse to
|
||||
`mcp_tool` and other non-built-in tools to `custom_tool` unless
|
||||
`OTEL_LOG_TOOL_DETAILS=1`.
|
||||
|
||||
## Events (OTLP log records)
|
||||
|
||||
Every event carries `event.sequence`, `session.id`, `turn_number` (in-turn),
|
||||
`prompt.id`, plus the identity attributes. Gate legend: **details** =
|
||||
requires `OTEL_LOG_TOOL_DETAILS`, **prompts** = requires
|
||||
`OTEL_LOG_USER_PROMPTS`; everything else always exports while the stream is
|
||||
active.
|
||||
|
||||
| `event.name` | Attributes |
|
||||
|---|---|
|
||||
| `grok_code.session_start` | `model`, `permission_mode`, `mcp_server_count`, `plugin_count`, `skill_count`, `hook_count`, `memory_enabled`, `is_git_repo`, `client_identifier` |
|
||||
| `grok_code.session_end` | `duration_secs`, `turn_count`, `tool_call_count`, `compaction_count`, `model` |
|
||||
| `grok_code.user_prompt` | `prompt_length`, `model`, `screen_mode?` (`fullscreen` \| `inline` \| `minimal` \| `headless` \| `other`); `prompt` (**prompts**) |
|
||||
| `grok_code.turn_completed` | `outcome`, `duration_ms`, `tool_call_count`, `model`, `error_category?`, `cancellation_category?` |
|
||||
| `grok_code.api_request` | `model`, `duration_ms`, `stop_reason?`, `input_tokens`, `output_tokens`, `reasoning_tokens`, `cache_read_tokens` |
|
||||
| `grok_code.api_error` | `error_category`, `model`, `status_code?`, `duration_ms?` |
|
||||
| `grok_code.tool_result` | `tool_name`, `outcome`, `success`, `duration_ms`, `file_extension`; `tool_parameters`, `file_path` (**details**) |
|
||||
| `grok_code.tool_decision` | `tool_name`, `decision`, `access_kind`, `permission_mode`, `source` |
|
||||
| `grok_code.mcp_server_connection` | `status`, `transport_type`, `duration_ms`, `tool_count?`, `error_type?`; `mcp_server.name` (**details**; collapsed to `mcp_server` otherwise) |
|
||||
| `grok_code.permission_mode_changed` | `to_mode`, `trigger` |
|
||||
| `grok_code.skill_activated` | `skill_source`; `skill.name` (**details**) |
|
||||
| `grok_code.plugin_loaded` | `install_kind?`, `success`, `error_category?`; `plugin_name` (**details**) |
|
||||
| `grok_code.compaction` | `duration_ms`, `tokens_before`, `tokens_after`, `model?` |
|
||||
| `grok_code.subagent` | `phase` = `launched` \| `completed`, `subagent_type?`, `outcome?`, `duration_ms?` |
|
||||
| `grok_code.auth` | `auth_method` |
|
||||
| `grok_code.internal_error` | `error_type` (class only — no message, no location) |
|
||||
| `grok_code.model_switched` | `from_model`, `to_model`, `success`, `error_code?` |
|
||||
|
||||
## Privacy model
|
||||
|
||||
Three independent fail-closed mechanisms guard the wire format:
|
||||
|
||||
1. A **typed schema**: attribute keys are a closed enum; nothing outside it
|
||||
can be attached.
|
||||
2. **Emit-time redaction**: every string passes a secret-shape scrub and a
|
||||
home-directory scrub, with truncation (512→128 chars per value, 4 KB tool
|
||||
params, 60 KB prompt cap).
|
||||
3. **Export-time validators**: any record carrying a non-schema key, a
|
||||
closed-gate key, or an unscrubbed secret shape is dropped before leaving
|
||||
the process; metric exports with out-of-schema attribute keys are dropped
|
||||
entirely.
|
||||
|
||||
Never exported: bash command text, error message bodies, prompt text
|
||||
(without the gate), file paths (without the gate), `api_key.id`, machine
|
||||
fingerprints, email addresses, subscription tier.
|
||||
|
||||
## Example collector config
|
||||
|
||||
```yaml
|
||||
receivers:
|
||||
otlp:
|
||||
protocols:
|
||||
http:
|
||||
endpoint: 0.0.0.0:4318
|
||||
grpc:
|
||||
endpoint: 0.0.0.0:4317
|
||||
|
||||
processors:
|
||||
batch:
|
||||
|
||||
exporters:
|
||||
prometheus:
|
||||
endpoint: 0.0.0.0:9464
|
||||
|
||||
service:
|
||||
pipelines:
|
||||
metrics:
|
||||
receivers: [otlp]
|
||||
processors: [batch]
|
||||
exporters: [prometheus]
|
||||
logs:
|
||||
receivers: [otlp]
|
||||
processors: [batch]
|
||||
exporters: [] # point at your log backend (loki, elasticsearch, …)
|
||||
```
|
||||
|
||||
Example queries (PromQL, with the Prometheus exporter above):
|
||||
|
||||
```promql
|
||||
# Tokens by model and type across the org, 1h rate
|
||||
sum by (model, type) (rate(grok_code_token_usage_total[1h]))
|
||||
|
||||
# Sessions per team per day
|
||||
sum by (team_id) (increase(grok_code_session_count_total[1d]))
|
||||
|
||||
# Tool-permission denial ratio
|
||||
sum(rate(grok_code_tool_decision_total{decision="deny"}[1h]))
|
||||
/ sum(rate(grok_code_tool_decision_total[1h]))
|
||||
```
|
||||
|
||||
## Debugging
|
||||
|
||||
Set `OTEL_LOGS_EXPORTER=console` / `OTEL_METRICS_EXPORTER=console` to print
|
||||
redacted records to **stderr** (suppressed in `agent`/`headless` entrypoints
|
||||
to keep captured logs clean). Export errors never surface in the TUI; check
|
||||
the debug log.
|
||||
52
crates/codegen/xai-grok-pager/docs/user-guide/README.md
Normal file
52
crates/codegen/xai-grok-pager/docs/user-guide/README.md
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
# Grok Build User Guide
|
||||
|
||||
Learn how to install, configure, and extend Grok Build, the terminal-based AI coding assistant from SpaceXAI.
|
||||
|
||||
---
|
||||
|
||||
## Tier 1: Essential User Docs
|
||||
|
||||
Start here. These guides cover what you need on your first day.
|
||||
|
||||
| # | Document | Description |
|
||||
|---|----------|-------------|
|
||||
| 1 | [Getting Started](01-getting-started.md) | Installation, first launch, authentication, basic interaction, and key concepts |
|
||||
| 2 | [Authentication](02-authentication.md) | Browser login, API keys, OIDC/SSO, external auth providers, and device-code flow |
|
||||
| 3 | [Keyboard Shortcuts](03-keyboard-shortcuts.md) | Reference for every key binding and mouse action in the TUI |
|
||||
| 4 | [Slash Commands](04-slash-commands.md) | Every `/` command for sessions, models, memory, hooks, and plugins |
|
||||
| 5 | [Configuration](05-configuration.md) | `config.toml`, `pager.toml`, environment variables, and file locations |
|
||||
|
||||
---
|
||||
|
||||
## Tier 2: Core Feature Docs
|
||||
|
||||
Customize and extend Grok Build.
|
||||
|
||||
| # | Document | Description |
|
||||
|---|----------|-------------|
|
||||
| 6 | [Theming and Appearance](06-theming.md) | Themes, the `/theme` command, `pager.toml`, and color-support detection |
|
||||
| 7 | [MCP Servers](07-mcp-servers.md) | External tool integrations through the Model Context Protocol |
|
||||
| 8 | [Skills](08-skills.md) | Reusable prompt packages in the SKILL.md format |
|
||||
| 9 | [Plugins](09-plugins.md) | Bundle and share skills, commands, agents, hooks, and MCP servers; install from marketplace sources |
|
||||
| 10 | [Hooks](10-hooks.md) | Lifecycle scripts and HTTP callbacks for pre- and post-tool-use events |
|
||||
| 11 | [Custom Models](11-custom-models.md) | Bring-your-own-key, Ollama, and OpenAI-compatible endpoints |
|
||||
| 12 | [Project Rules (AGENTS.md)](12-project-rules.md) | Per-directory AGENTS.md instructions and their precedence |
|
||||
| 13 | [Memory](13-memory.md) | Cross-session knowledge persistence with `/flush`, `/dream`, and hybrid search |
|
||||
|
||||
---
|
||||
|
||||
## Tier 3: Advanced Usage Docs
|
||||
|
||||
Automate, script, and integrate Grok Build with other systems.
|
||||
|
||||
| # | Document | Description |
|
||||
|---|----------|-------------|
|
||||
| 14 | [Headless Mode and Scripting](14-headless-mode.md) | `grok -p`, output formats, CI/CD integration, and piping |
|
||||
| 15 | [Agent Mode and IDE Integration](15-agent-mode.md) | ACP stdio transport, WebSocket relay, and SDK integration |
|
||||
| 16 | [Subagents and Personas](16-subagents.md) | Parallel child sessions, agent types, personas, and capability modes |
|
||||
| 17 | [Session Management](17-sessions.md) | Save, load, resume, rewind, compact, and the session persistence format |
|
||||
| 18 | [Sandbox Mode](18-sandbox.md) | OS-level filesystem and network isolation profiles |
|
||||
| 19 | [Plan Mode](19-plan-mode.md) | Structured planning, plan-file edits, and approval before coding |
|
||||
| 20 | [Background Tasks and Monitoring](20-background-tasks.md) | `background: true`, `/loop`, `monitor`, and `Ctrl+G` to demote |
|
||||
| 21 | [Terminal Support and Troubleshooting](21-terminal-support.md) | tmux, SSH, truecolor, clipboard, and OSC 52 |
|
||||
| 22 | [Permissions and Safety Controls](22-permissions-and-safety.md) | `dontAsk` mode, auto-approved tools, the safe-bash list, and restrictive PreToolUse hooks (such as git/gh-only) |
|
||||
Loading…
Reference in a new issue