Grok Buildβs headless mode lets you run the agent non-interactively with grok -p "prompt". Combine it with --output-format streaming-json and you get structured output thatβs easy to parse in CI/CD pipelines.
This guide covers practical automation patterns: generating tests, creating PRs, running code reviews, and integrating with GitHub Actions.
Headless mode basics
# Basic headless execution
grok -p "add unit tests for src/auth.ts"
# With structured output
grok -p "add unit tests for src/auth.ts" --output-format streaming-json
# Specify mode
grok -p "review this PR for security issues" --mode plan --output-format json
# Plain text output
grok -p "explain what src/main.ts does" --output-format text
Output formats
| Format | Flag | Use case |
|---|---|---|
| Streaming JSON | --output-format streaming-json | Real-time progress in pipelines |
| JSON | --output-format json | Parse final result programmatically |
| Text | --output-format text | Human-readable logs |
Streaming JSON structure
Each line is a JSON object:
{"type": "status", "message": "Reading src/auth.ts..."}
{"type": "tool_call", "tool": "file_read", "path": "src/auth.ts"}
{"type": "tool_result", "tool": "file_read", "success": true}
{"type": "file_edit", "path": "src/auth.test.ts", "action": "create"}
{"type": "complete", "files_modified": ["src/auth.test.ts"], "tokens_used": 12450}
Final JSON structure
{
"success": true,
"files_modified": ["src/auth.test.ts"],
"files_created": ["src/auth.test.ts"],
"tokens_used": 12450,
"cost": 0.015,
"duration_ms": 8200
}
Authentication in CI
Set XAI_API_KEY as a pipeline secret. Never use browser OAuth in CI.
export XAI_API_KEY="${{ secrets.XAI_API_KEY }}"
GitHub Actions: Generate tests on PR
name: AI Test Generation
on:
pull_request:
types: [opened, synchronize]
jobs:
generate-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install Grok Build
run: curl -fsSL https://x.ai/cli/install.sh | bash
- name: Get changed files
id: changed
run: |
FILES=$(git diff --name-only origin/main...HEAD -- '*.ts' '*.tsx' | tr '\n' ' ')
echo "files=$FILES" >> $GITHUB_OUTPUT
- name: Generate tests
env:
XAI_API_KEY: ${{ secrets.XAI_API_KEY }}
run: |
grok -p "Generate unit tests for these files: ${{ steps.changed.outputs.files }}. Use vitest. Only create test files, don't modify source." \
--output-format json > result.json
- name: Commit generated tests
run: |
git config user.name "grok-build[bot]"
git config user.email "grok-build@bot"
git add "*.test.ts" "*.test.tsx"
git diff --cached --quiet || git commit -m "test: add AI-generated tests for changed files"
git push
GitHub Actions: Automated code review
name: AI Code Review
on:
pull_request:
types: [opened, synchronize]
jobs:
review:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install Grok Build
run: curl -fsSL https://x.ai/cli/install.sh | bash
- name: Get diff
run: git diff origin/main...HEAD > pr.diff
- name: Run AI review
env:
XAI_API_KEY: ${{ secrets.XAI_API_KEY }}
run: |
grok -p "Review this diff for bugs, security issues, and performance problems. Be concise. Output as markdown." \
--mode ask \
--output-format text > review.md
- name: Post review comment
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const review = fs.readFileSync('review.md', 'utf8');
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: `## AI Code Review\n\n${review}`
});
GitHub Actions: Auto-fix linting issues
name: AI Lint Fix
on:
push:
branches: [main]
jobs:
fix:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install dependencies
run: npm ci
- name: Run linter
id: lint
continue-on-error: true
run: npm run lint 2> lint-errors.txt
- name: Install Grok Build
if: steps.lint.outcome == 'failure'
run: curl -fsSL https://x.ai/cli/install.sh | bash
- name: Fix lint errors
if: steps.lint.outcome == 'failure'
env:
XAI_API_KEY: ${{ secrets.XAI_API_KEY }}
run: |
grok -p "Fix these lint errors. Only modify the minimum code needed: $(cat lint-errors.txt)" \
--output-format json
- name: Create PR with fixes
if: steps.lint.outcome == 'failure'
run: |
git checkout -b fix/lint-$(date +%s)
git add -A
git commit -m "fix: auto-fix lint errors"
git push -u origin HEAD
gh pr create --title "fix: auto-fix lint errors" --body "Automated lint fixes by Grok Build"
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Scripting patterns
Parse streaming JSON in bash
grok -p "refactor src/utils.ts" --output-format streaming-json | while IFS= read -r line; do
type=$(echo "$line" | jq -r '.type')
case "$type" in
"file_edit")
echo "Modified: $(echo "$line" | jq -r '.path')"
;;
"complete")
echo "Done. Tokens: $(echo "$line" | jq -r '.tokens_used')"
;;
esac
done
Parse final JSON in Node.js
const { execSync } = require('child_process');
const result = JSON.parse(
execSync('grok -p "add error handling to src/api.ts" --output-format json').toString()
);
if (result.success) {
console.log(`Modified ${result.files_modified.length} files`);
console.log(`Cost: $${result.cost}`);
} else {
process.exit(1);
}
Budget control in CI
# Run the task and capture output with cost info
grok -p "generate documentation" --output-format json > result.json
# Check cost after run
COST=$(cat result.json | jq '.cost')
if (( $(echo "$COST > 1.00" | bc -l) )); then
echo "WARNING: Run exceeded $1.00 budget"
fi
Note: Check
grok --helpfor the latest available flags as Grok Build is in early beta. Budget control flags like--max-costmay be added in future releases. For now, monitor costs using the/costslash command in interactive sessions or parse thecostfield from JSON output in CI.
Best practices for CI/CD
- Pin your prompts. Store prompts in files, not inline. Version control them.
- Monitor costs. Parse the
costfield from--output-format jsonoutput and set alerts for unexpected spending. Checkgrok --helpfor budget flags as they become available. - Use
--mode planfor reviews. Plan mode doesnβt modify files, making it safe for read-only review jobs. - Parse JSON output. Donβt rely on text parsing. Use
--output-format jsonand parse withjq. - Scope file access. Use specific file paths in prompts rather than βfix everything.β
- Test locally first. Run your headless command locally before adding it to CI.
Combining with hooks
If your project has .grok/hooks.json, hooks run in headless mode too:
{
"post-edit": "npm run lint --fix",
"pre-commit": "npm run test"
}
This means CI runs get the same validation as interactive sessions.
For the full command reference, see the Grok Build cheat sheet. For setup instructions, see the complete guide.
FAQ
Does headless mode support MCP servers?
Yes. MCP servers configured in ~/.grok/mcp.json or .grok/mcp.json start and connect in headless mode. This lets CI jobs access databases, GitHub APIs, or custom tools.
How do I limit costs in automated pipelines?
Monitor costs by parsing the cost field from --output-format json output after each run. Set up alerts in your CI pipeline if costs exceed a threshold. Check grok --help for the latest available flags. Budget control flags like --max-cost may be added in future releases as Grok Build is in early beta.
Can I run multiple headless instances in parallel?
Yes. Each grok -p invocation is independent. You can parallelize across files or tasks in your CI matrix. Each instance authenticates separately with the same API key.
Whatβs the timeout for headless mode?
Default timeout is 5 minutes. Override with --timeout 600 (in seconds) for longer tasks. CI runners may have their own timeouts that take precedence.
Does CLAUDE.md get loaded in headless mode?
Yes. If a CLAUDE.md exists in the working directory (or parent directories), Grok Build loads it in headless mode too. This ensures consistent behavior between interactive and automated runs.
Can I use headless mode with SuperGrok instead of an API key?
SuperGrok uses browser OAuth which doesnβt work in CI. For automated pipelines, you need an API key (XAI_API_KEY). You can have both: SuperGrok for interactive use, API key for CI.