Headless Mode
AdaL's headless mode lets you run queries programmatically without the interactive terminal UI. Pass a prompt, get a result — as plain text, structured JSON, or a real-time NDJSON stream. It uses the exact same agent, tools, and capabilities as the interactive CLI.
Use headless mode to integrate AdaL into shell scripts, CI/CD pipelines, editor extensions, or any automation workflow.
Prerequisites
Headless mode requires authentication. Run adal interactively once — your credentials are cached and reused automatically by headless mode.
# Log in interactively first
adal
# Then headless works
adal -q "explain this codebase"
Quick Start
# Simple query — prints the final answer to stdout
adal -q "what does main.py do?"
# Pipe input — reads from stdin when not a TTY
cat bug_report.txt | adal
# JSON output — structured response with metadata
adal -q "list all TODO comments" -o json
# Stream events in real-time as NDJSON
adal -q "refactor the auth module" -o stream-json
# Auto-approve all tool calls (no confirmation prompts)
adal -q "fix the failing test" --yolo
# Use a specific model
adal -q "review this PR" -m claude-sonnet-4-20250514
# Resume a previous conversation
adal -q "now add tests for that" -r <session-id>
CLI Flags
| Flag | Short | Description |
|---|---|---|
--query <text> | -q | The prompt to execute. Triggers headless mode. |
--output <format> | -o | Output format: text (default), json, or stream-json. |
--model <name> | -m | Override the default model (e.g., claude-sonnet-4-20250514). |
--permission-mode <mode> | — | What may run without approval: default, acceptEdits, yolo, or ralph. See Tool Approval. |
--yolo | — | Same as --permission-mode yolo: every tool call runs. |
--prompt-file <path> | -pf | Replace the agent's role prompt with the contents of this file. |
--enabled-default-tools <csv> | — | Positive set of core tool groups/names to enable — ONLY these tools are available (comma-separated). Cannot combine with --disabled-default-tools. See Built-in Tools. |
--disabled-default-tools <csv> | — | Disable core tool groups/names entirely — invisible to the agent and unexecutable (comma-separated). See Built-in Tools. |
--resume <id> | -r | Resume a previous conversation by session ID. |
--prompt <text> | -p | Alias of --query. |
Stdin / Pipe Support
AdaL detects when input is piped (non-TTY stdin) and automatically reads it as the query:
# Pipe a file as the query
cat bug_report.txt | adal
# Pipe command output
git diff | adal -o json
# Combine with flags
echo "explain this error" | adal --yolo -o json
When piped, AdaL reads the entire stdin buffer and uses it as the prompt. The final answer goes to stdout, so you can chain it with other commands.
Output Formats
text (default)
Prints only the final answer to stdout. Ideal for piping into other commands.
adal -q "summarize README.md" | pbcopy
This project is a REST API for managing user accounts...
json
Returns a structured JSON object with the answer and metadata. Useful for programmatic consumption.
adal -q "what's the test coverage?" -o json
{
"success": true,
"answer": "Current test coverage is 87%. The uncovered files are...",
"error": null,
"tool_calls": [
{
"id": "3f9c...",
"name": "bash",
"args": { "command": "npm test -- --coverage" },
"status": "completed",
"output": "Statements : 87.2% ( 1043/1196 )\n..."
}
],
"model": "claude-sonnet-4-20250514",
"session_id": "a1b2c3d4-...",
"exit_code": 0
}
tool_calls lists every tool the agent ran, in start order, with the arguments it was called with, its final status (completed / failed / cancelled, or running if the run ended first) and its output: what the tool returned (a shell command's stdout and stderr, a file's contents, a search's matches).
output is what the interactive UI shows for the call. Past a per-tool size threshold (30,000 characters for bash, 400,000 by default) it is a head-and-tail preview rather than the full text. bash saves the full output to a file under the session directory and the preview ends with Full output saved to: <path>, so a consumer that needs everything can read that file.
On failure:
{
"success": false,
"answer": null,
"error": "Authentication failed",
"model": null,
"session_id": "a1b2c3d4-...",
"exit_code": 1
}
stream-json
Streams events as NDJSON (one JSON object per line) in real-time. Best for building integrations that need live progress updates.
adal -q "fix the lint errors" -o stream-json
{"type":"tool_call","id":"3f9c...","name":"bash","args":{"command":"npx eslint src/"}}
{"type":"tool_result","id":"3f9c...","name":"bash","status":"completed","output":"src/app.ts\n 12:5 error 'x' is assigned a value but never used\n..."}
{"type":"answer","content":"Fixed 3 lint errors in src/app.ts..."}
{"type":"complete","exit_code":0,"session_id":"a1b2c3d4-...","model":"claude-sonnet-4-20250514"}
The complete event reference — every event type, field semantics, ordering guarantees, and how exit codes map to events — is on the Event Stream Reference page.
Tool Approval
In interactive mode, AdaL pauses and asks you to confirm before running tools that modify files or execute commands. A headless run has nobody to ask, so you decide up front with --permission-mode:
| Mode | Runs without asking | A call that would need approval |
|---|---|---|
default | Every tool except file edits and non-project shell commands: reads, search, web, browser, media, MCP and custom tools; read-only shell commands (ls, git status, grep, ...); project commands (test, build and lint runners, git add/commit/switch, mkdir/cp/mv inside the workspace) | Stops the run. Exit code 2; the refused call and the reason are reported (see below). |
acceptEdits | The above, plus file creates and edits | Shell commands that are not read-only stop the run. |
yolo | Everything | Never happens. |
ralph | Everything, and the run restarts with the same prompt when it completes | Never happens. |
The default is default: a headless run never writes or runs commands unless you said it may. --yolo is the same as --permission-mode yolo.
adal -q "run the tests and report" # default: tests, builds, commits run; a file edit or rm stops the run
adal -q "fix the typos" --permission-mode acceptEdits # edits run, shell commands stop the run
adal -q "refactor the database layer" --yolo # everything runs
yolo (and ralph) let the agent execute any tool — file writes, deletions, shell commands — without asking. Use them in trusted environments (ephemeral CI containers, sandboxed workspaces).
When a call is refused
The run stops at the first call that needs approval, and every output format says so:
text: nothing on stdout; stderr getsStopped:followed by the full reason sentence, thenRefused call: <tool> <args>.json:"success": false,"error"is the reason, the call is intool_callswith"status": "denied", and"exit_code": 2.stream-json: apermission_deniedevent with the call'sid,name,argsandreason, thencompletewithexit_code: 2.
The reason names the tool and says how to allow it. A script can branch on the exit code:
adal -q "$TASK" -o json > out.json
case $? in
0) echo "done" ;;
2) echo "needs approval — re-run with --permission-mode acceptEdits or --yolo" ;;
*) echo "failed" ;;
esac
Allowed Tools
Control which tools are available using visibility flags:
# Only allow read operations and search (positive set)
adal -q "analyze the codebase" --enabled-default-tools "Read,Search" --yolo
# Allow reads, edits, but not shell commands
adal -q "fix the typos" --enabled-default-tools "Read,Search,Edit" --yolo
Tool Groups
| Group | Description |
|---|---|
Read | Read files and images |
Search | Search within files and the web |
Edit | Create, modify, and delete files |
Bash | Execute shell commands |
Image | Generate images |
If a tool isn't in the allowed list and isn't safe by default, the agent skips it and proceeds without that action.
Multi-Turn Conversations
Use --resume (-r) to continue a previous session:
# First query
adal -q "explain the auth flow" -o json
# Response includes session_id: "abc-123"
# Follow-up using the same session
adal -q "now add rate limiting to it" -r abc-123
The resumed session retains full conversation history, so the agent has context from previous turns.
Headless Subcommands
Beyond -q queries, AdaL exposes subcommands that work entirely in headless mode without launching the interactive UI.
Worktree Management
Create isolated git worktrees for parallel headless tasks — each worktree gets its own branch and working directory, so the agent can work without disturbing your main tree.
# Worktrees are managed by the agent, or with git directly
git worktree add .agents/worktrees/fix-auth-bug -b worktree-fix-auth-bug
git worktree list
git worktree remove .agents/worktrees/fix-auth-bug
Plugin Management
Install, list, and manage plugins and skills without the interactive UI:
# List installed plugins
adal plugin list
# Install a plugin from the marketplace
adal plugin install <plugin-name>
# Reload skills after manual changes
adal plugin reload-skills
Exit Codes
| Code | Meaning |
|---|---|
0 | Success — query completed, answer returned |
1 | Failure — auth error, model error, or agent error |
2 | Stopped — a tool call needed approval that nobody could give (see Tool Approval) |
Agent Integration Examples
SRE: Automated Incident Response
#!/bin/bash
investigate_incident() {
local description="$1"
local severity="${2:-medium}"
adal -q "Incident: $description (Severity: $severity). \
Diagnose the issue, assess impact, and provide immediate action items." \
--yolo -o json
}
# Usage
investigate_incident "Payment API returning 500 errors" "high"
GitHub Actions: Automated Code Review
- name: Run AdaL code review
run: |
adal -q "Review the changes in this PR for bugs and security issues. \
Focus on error handling and input validation." \
--enabled-default-tools "Read,Search" \
--yolo \
-o json > review.json
CI/CD: Auto-fix Lint Errors
#!/bin/bash
result=$(adal -q "fix all ESLint errors in src/" --yolo -o json)
if echo "$result" | jq -e '.success' > /dev/null; then
git add -A && git commit -m "fix: auto-fix lint errors"
fi
Multi-Turn: Guided Refactoring
# Start the conversation
session_id=$(adal -q "Analyze the auth module for tech debt" -o json | jq -r '.session_id')
# Follow up with context from the first turn
adal -q "Now refactor the worst offender you found" -r "$session_id" --yolo
adal -q "Add tests for the refactored code" -r "$session_id" --yolo
Pipeline: Stream Progress to a Dashboard
adal -q "migrate the database schema" --yolo -o stream-json | while IFS= read -r line; do
type=$(echo "$line" | jq -r '.type')
case "$type" in
tool_call) echo "🔧 $(echo "$line" | jq -r '.name')" ;;
error) echo "❌ $(echo "$line" | jq -r '.message')" ;;
answer) echo "✅ Done" ;;
esac
done
Pipe: Analyze Logs and Diffs
# Pipe logs directly to AdaL for analysis
tail -100 /var/log/app.log | adal --yolo -o text
# Pipe a git diff for review
git diff HEAD~1 | adal -o json
Parallel Tasks with Worktrees
# Run two agent tasks in parallel on separate checkouts
git worktree add .agents/worktrees/task-a -b worktree-task-a
git worktree add .agents/worktrees/task-b -b worktree-task-b
(cd .agents/worktrees/task-a && adal -q "implement feature A" --yolo) &
(cd .agents/worktrees/task-b && adal -q "implement feature B" --yolo) &
wait
git worktree remove .agents/worktrees/task-a
git worktree remove .agents/worktrees/task-b
Best Practices
-
Use JSON output for programmatic parsing:
result=$(adal -q "generate code" -o json)
answer=$(echo "$result" | jq -r '.answer') -
Handle errors gracefully — check exit codes and parse failure responses:
if ! result=$(adal -q "$prompt" -o json 2>error.log); then
echo "Error occurred:" >&2
cat error.log >&2
exit 1
fi -
Add timeouts for long-running operations:
timeout 300 adal -q "$complex_prompt" --yolo || echo "Timed out after 5 minutes" -
Use
--enabled-default-toolsinstead of--yolowhen possible — grant only the tools the task needs. -
Use worktrees for parallel tasks to avoid file conflicts between concurrent agents.
-
Respect rate limits when making multiple requests — add delays between calls in batch scripts.
Troubleshooting
"Not authenticated"
Your cached credentials are missing or expired. Run adal interactively to complete the login flow — credentials are then cached and reused for subsequent headless runs.
Tool calls are being skipped
If the agent skips tools without --yolo, there's no UI to approve them. Add the appropriate flag:
# Allow everything (auto-approve all tools)
adal -q "fix it" --yolo
# Or restrict to specific tools
adal -q "fix it" --enabled-default-tools "Read,Search,Edit" --yolo
Model override not working
Verify the model name is valid. Use the full model identifier:
adal -q "hello" -m claude-sonnet-4-20250514
Streaming stops unexpectedly
Check that your shell isn't buffering output. For real-time NDJSON, ensure line-buffered reading:
adal -q "task" -o stream-json | stdbuf -oL jq '.'