Skip to main content

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​

FlagShortDescription
--query <text>-qThe prompt to execute. Triggers headless mode.
--output <format>-oOutput format: text (default), json, or stream-json.
--model <name>-mOverride 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>-pfReplace 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>-rResume a previous conversation by session ID.
--prompt <text>-pAlias 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).

Large outputs

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:

ModeRuns without askingA call that would need approval
defaultEvery 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).
acceptEditsThe above, plus file creates and editsShell commands that are not read-only stop the run.
yoloEverythingNever happens.
ralphEverything, and the run restarts with the same prompt when it completesNever 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
caution

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 gets Stopped: followed by the full reason sentence, then Refused call: <tool> <args>.
  • json: "success": false, "error" is the reason, the call is in tool_calls with "status": "denied", and "exit_code": 2.
  • stream-json: a permission_denied event with the call's id, name, args and reason, then complete with exit_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​

GroupDescription
ReadRead files and images
SearchSearch within files and the web
EditCreate, modify, and delete files
BashExecute shell commands
ImageGenerate 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​

CodeMeaning
0Success — query completed, answer returned
1Failure — auth error, model error, or agent error
2Stopped — 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-tools instead of --yolo when 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 '.'