Headless Mode
Run CodeBuddy Code programmatically without an interactive UI
Overview
Headless mode allows you to run CodeBuddy Code programmatically through command-line scripts and automation tools without any interactive UI.
Headless mode also supports scheduled task capabilities. In script, SDK, or server-side integration scenarios, you can use tools such as CronCreate, CronList, and CronDelete to create, view, and cancel scheduled tasks.
⚠️ Important Note:
-y(or--dangerously-skip-permissions) is a required parameter for non-interactive mode. When using the-p/--printparameter for non-interactive execution, this parameter must be added to perform operations that require authorization (file read/write, command execution, network requests, etc.), otherwise these operations will be blocked. With-yalone, HIGH/CRITICAL dangerous commands may still require confirmation; to suppress all prompts in an isolated sandbox, useexport CODEBUDDY_IS_SANDBOX=1 && codebuddy -p -y ...(high risk; process environment only). Use this only in trusted environments and for explicit task scenarios. See CLI Reference and Sandbox full pass (high risk) for details.
Basic Usage
The primary command-line interface for CodeBuddy Code is the codebuddy (or cbc) command. Use the --print (or -p) flag to run in non-interactive mode and print the final result:
bash
codebuddy -p "Stage my changes and write a set of commits for them" \
--allowedTools "Bash,Read" \
--permission-mode acceptEditsConfiguration Options
Headless mode leverages all available CLI options in CodeBuddy Code. Here are key options for automation and scripting:
| Flag | Description | Example |
|---|---|---|
--print, -p | Run in non-interactive mode | codebuddy -p "query" |
--output-format | Specify output format (text, json, stream-json) | codebuddy -p --output-format json |
--resume, -r | Resume a conversation by session ID | codebuddy --resume abc123 |
--continue, -c | Continue the most recent conversation | codebuddy --continue |
--verbose | Enable verbose logging | codebuddy --verbose |
--append-system-prompt | Append to system prompt (only works with --print) | codebuddy --append-system-prompt "Custom instructions" |
--allowedTools | List of allowed tools, space-separated or comma-separated string | codebuddy --allowedTools mcp__slack mcp__filesystemcodebuddy --allowedTools "Bash(npm install),mcp__filesystem" |
--disallowedTools | List of disallowed tools, space-separated or comma-separated string | codebuddy --disallowedTools mcp__splunk mcp__githubcodebuddy --disallowedTools "Bash(git commit),mcp__github" |
--settings | Load additional settings configuration from a JSON file or JSON string | codebuddy -p --settings '{"model":"gpt-5"}' "query" |
--setting-sources | Specify which settings sources to load (options: user, project, local) | codebuddy -p --setting-sources project,local "query" |
--mcp-config | Load MCP servers from a JSON file | codebuddy --mcp-config servers.json |
--permission-prompt-tool | MCP tool for handling permission prompts (only works with --print) | ❌ Not supported |
Note: The
--permission-prompt-toolfeature is currently not supported.
For a complete list of CLI options and features, please refer to the CLI Reference documentation.
Multi-turn Conversations
For multi-turn conversations, you can resume a conversation or continue from the most recent session:
bash
# Continue the most recent conversation
codebuddy --continue "Now refactor for better performance"
# Resume a specific conversation by session ID
codebuddy --resume 550e8400-e29b-41d4-a716-446655440000 "Update tests"
# Resume in non-interactive mode
codebuddy --resume 550e8400-e29b-41d4-a716-446655440000 "Fix all linting issues" -pOutput Formats
Text Output (Default)
bash
codebuddy -p "Explain the file src/components/Header.tsx"
# Output: This is a React component that displays...JSON Output
Returns structured data with metadata:
bash
codebuddy -p "How does the data layer work?" --output-format jsonResponse format:
json
{
...
}Stream JSON Output
Streams as each message is received:
bash
codebuddy -p "Build an application" --output-format stream-jsonEach conversation begins with an initial init system message, followed by a list of user and assistant messages, and ends with a final result system message containing statistics. Each message is emitted as a separate JSON object.
Background Task Events (Async)
When the model launches a background command (Bash / PowerShell), background workflow, or background Agent sub-task with run_in_background: true, the CLI emits a separate system event on the stream-json output stream for each task, carrying a unique task_id (used to distinguish concurrent tasks), with tool_use_id linking back to the tool_use that initiated the task:
- Task started →
system/subtype: "task_started" - Task progress (emitted per completed tool_use, sub-agent/workflow only) →
system/subtype: "task_progress"(withusage) - Task status change →
system/subtype: "task_updated"(withpatch) - Task completed/failed/stopped →
system/subtype: "task_notification"
jsonc
// Task started (pushed immediately when entering running state)
{"type":"system","subtype":"task_started","task_id":"agent-00f6","tool_use_id":"toolu_01","description":"bg agent","task_type":"Agent","uuid":"...","session_id":"..."}
// Progress (per completed tool_use, carries cumulative usage + last tool name; shell tasks do not emit)
{"type":"system","subtype":"task_progress","task_id":"agent-00f6","description":"bg agent","usage":{"total_tokens":320,"tool_uses":2,"duration_ms":157},"last_tool_name":"Bash","uuid":"...","session_id":"..."}
// Status change (patch carries changed fields; terminal state adds end_time)
{"type":"system","subtype":"task_updated","task_id":"agent-00f6","patch":{"status":"completed","end_time":1783945615966},"status":"completed","uuid":"...","session_id":"..."}
// Task completed (may arrive after the triggering turn's result; sub-agent carries usage)
{"type":"system","subtype":"task_notification","task_id":"agent-00f6","tool_use_id":"toolu_01","status":"completed","summary":"Background agent \"bg agent\" completed","output_file":"/.../bg-tasks/agent-00f6.stdout.log","usage":{"total_tokens":480,"tool_uses":2,"duration_ms":250},"session_id":"..."}Field descriptions:
| Field | Event | Description |
|---|---|---|
task_id | All | Unique ID for the background task, persists across started → progress → updated → notification; used to distinguish concurrent tasks and route to TaskOutput |
tool_use_id | Most (optional) | Links back to the model's tool_use |
description / task_type | started / progress | Task command description / tool type (Bash / PowerShell / Workflow / Agent) |
usage | progress (always present) / notification (present for sub-agent, omitted for shell) | { total_tokens, tool_uses, duration_ms } (aligns with CC's TaskUsage) |
last_tool_name | progress (optional) | Name of the most recently executed tool |
patch / status | updated | Changed fields in this update (at least status; terminal state adds end_time) |
status | notification | completed / failed / stopped (killed/cancelled normalized to stopped) |
summary | notification | Human-readable completion summary |
output_file / output_stderr_file | notification (optional) | On-disk output path for the background task (file mode); use this to read the full output |
Progress events (
task_progress) are event-driven (one emitted per completed tool_use), not polled on a timer; background shell tasks (Bash/PowerShell) do not emit progress—only sub-agent / workflow tasks do.Terminal state may arrive only via
task_updated: Some background tasks reach their terminal state solely throughtask_updated(patch.statusset to a terminal value) without a correspondingtask_notification. Consumers tracking "active tasks" should treat the terminal status (completed/failed/stopped/killed) from both events equally when cleaning up.
Important (stdio long-lived connection scenario): Background tasks may complete after the
resultof the turn that triggered them. When using--input-format stream-json --output-format stream-json(a long-lived connection with stdin kept open), the CLI proactively pushestask_notificationback to the same output stream after the task truly finishes—no need to send new input. Consumers should therefore keep reading the output stream and must not stop after receiving the firstresult, or they will miss background completion events. Pure-psingle-shot mode (process exits after the firstresult) does not support background tasks and returns an explicit error.
Disabling background tasks: In scenarios that do not support or need background tasks, set the environment variable
CODEBUDDY_CODE_DISABLE_BACKGROUND_TASKS=1to disable them—therun_in_backgroundparameter for Bash / PowerShell / Agent is hidden from the tool schema, and even if the model still sends it, it will be ignored/degraded to foreground execution, producing no background task events and no cross-turn push-back. The SDK'squery()single-shot usage automatically injects this variable (sincequery()stops at the firstresultand cannot receive cross-turn push-back events); continuously-reading SDK usage (JSunstable_v2_createSession/ PythonCodeBuddySDKClient) is unaffected.
Structured JSON Output
To get output that conforms to a specific schema, use --output-format json with --json-schema and a JSON Schema definition. The response includes metadata about the request (session ID, usage, etc.), with the structured output in the structured_output field.
This example extracts function names from auth.py and returns them as an array of strings:
bash
codebuddy -p "Extract the main function names from auth.py" \
--output-format json \
--json-schema '{"type":"object","properties":{"functions":{"type":"array","items":{"type":"string"}}},"required":["functions"]}'Tip: Use tools like jq to parse the response and extract specific fields:
bash# Extract text result codebuddy -p "Summarize this project" --output-format json | jq -r '.result' # Extract structured output codebuddy -p "Extract function names from auth.py" \ --output-format json \ --json-schema '{"type":"object","properties":{"functions":{"type":"array","items":{"type":"string"}}},"required":["functions"]}' \ | jq '.structured_output'
Input Formats
Text Input (Default)
bash
# Direct argument
codebuddy -p "Explain this code"
# From stdin
echo "Explain this code" | codebuddy -pStream JSON Input
A stream of messages provided via stdin, where each message represents a user turn. This allows for multi-turn conversations without restarting the codebuddy binary, and allows providing guidance to the model while it processes requests.
Each message is a JSON "user message" object following the same format as the output message schema. Messages are formatted using jsonl format, where each line of input is a complete JSON object. Stream JSON input requires -p and --output-format stream-json.
bash
echo '{"type":"user","message":{"role":"user","content":[{"type":"text","text":"Explain this code"}]}}' | \
codebuddy -p --output-format=stream-json --input-format=stream-json --verbose
# Single message (with image)
echo '{"type":"user","message":{"role":"user","content":[{"type":"text","text":"Text prompt, such as referring to the text in the following image"},{"type":"image","source":{"type":"base64","media_type":"image/png","data":"raw base64 (without protocol prefix)"}}]}}' \
| codebuddy -p --input-format stream-json --output-format stream-json
# Multi-turn conversation (multi-line JSON, continuously sent to the same process)
printf '%s\n' \
'{"type":"user","message":{"role":"user","content":[{"type":"text","text":"First question"}]}}' \
'{"type":"user","message":{"role":"user","content":[{"type":"text","text":"Second question"}]}}' \
| codebuddy -p --input-format stream-json --output-format stream-json --verboseClients can provide a conversationRequestId for each new user turn:
json
{"type":"user","message":{"role":"user","content":"解释这段代码"},"_meta":{"codebuddy.ai/conversationRequestId":"0198a1b2c3d47e5f8a9b0c1d2e3f4a5b"}}The value must be a lowercase, hyphenless, 32-character UUIDv7 hexadecimal string. If omitted, the CLI generates one. The CLI does not scan JSONL history for collisions; callers are responsible for ensuring uniqueness. Once accepted, the value appears in _requestId in the output for that turn.
Session Rewind
Rewind lets you restore "workspace files" and/or "conversation history" to the state before a specific historical user message was sent. This is useful when an automated Agent needs to retry after making an incorrect change, or when an IDE/upstream platform (such as CloudAgent) implements "undo to a specific message."
The naming and protocol align with Anthropic Claude Code v2.1.88, with extensions for upstream requirements.
Command-line flags (when starting a --print variant)
| Flag | Purpose | Constraints |
|---|---|---|
--rewind-files <user-message-id> | Restore workspace files to the snapshot before the specified user message was sent, then exit immediately | Requires --resume/-c; cannot be used with a prompt |
--resume-session-at <message id> | When resuming a session, retain only the history through the specified message (inclusive, using slice(0, index+1)), then continue the conversation | Requires --resume/-c |
--dry-run | When combined with --rewind-files, preview the file changes without modifying the disk | — |
bash
# Restore files to the state before a specific user message, then exit
codebuddy -p --resume <sessionId> --rewind-files <userMessageUuid>
# Preview only (do not modify the disk)
codebuddy -p --resume <sessionId> --rewind-files <userMessageUuid> --dry-run
# Resume the session with history truncated at a specific message, then send a new prompt
codebuddy -p --resume <sessionId> --resume-session-at <messageUuid> "Redo this step using a different approach"These three flags are hidden (they do not appear in --help) and are intended only for scripted calls from SDKs/upstream platforms. They take effect when starting any --print variant (single-shot, --output-format json, or long-lived --input-format stream-json).
Stream JSON control protocol (long-lived runtime)
In long-lived --input-format stream-json mode, you can trigger a rewind through a control_request without restarting the process. Two subtypes are supported:
| subtype | Rewind scope | Description |
|---|---|---|
rewind_files | Files only | Aligns with Claude Code; rewinds the disk without changing conversation history |
rewind | Determined by scope | cbc extension. scope can be Code (files only), Conversation (history only), or CodeAndConversation (default, files + history). A file rewind failure does not block history rewind (partial-success semantics) |
Supported scope values for rewind:
| scope | Rewind scope |
|---|---|
CodeAndConversation (default) | Files + conversation history together (the actual upstream CloudAgent usage) |
Conversation | Conversation history only; preserve files on disk |
Code | Files on disk only |
Request:
jsonc
// Rewind files only (aligns with Claude Code)
{"type":"control_request","request_id":"r1","request":{"subtype":"rewind_files","user_message_id":"<uuid>","dry_run":false}}
// Rewind files + conversation history (omitting scope defaults to CodeAndConversation)
{"type":"control_request","request_id":"r2","request":{"subtype":"rewind","user_message_id":"<uuid>","dry_run":false}}
// Rewind conversation history only (preserve files on disk)
{"type":"control_request","request_id":"r3","request":{"subtype":"rewind","user_message_id":"<uuid>","scope":"Conversation"}}Response (success):
jsonc
// rewind_files (strictly aligned with Claude's five-field schema)
{"type":"control_response","response":{"subtype":"success","request_id":"r1","response":{
"canRewind":true,"filesChanged":["src/a.ts"],"insertions":12,"deletions":5}}}
// rewind (adds historyRewound; includes fileRewindError on partial success)
{"type":"control_response","response":{"subtype":"success","request_id":"r2","response":{
"canRewind":true,"filesChanged":["src/a.ts"],"insertions":12,"deletions":5,"historyRewound":true}}}Response fields:
| Field | Type | Meaning |
|---|---|---|
canRewind | boolean | Whether rewind succeeded (for rewind, true when history rewind succeeds) |
error | string? | Failure reason (precondition validation failure / history rewind failure) |
filesChanged | string[]? | List of affected files |
insertions / deletions | number? | Line-level insertion/deletion counts |
historyRewound | boolean? | (rewind only) Whether conversation history was rewound |
fileRewindError | string? | (rewind only) Partial-success indicator: canRewind:true but file rewind failed while history rewind succeeded. The upstream caller can use this to ask the user to handle files manually while the conversation context has already returned to the correct point |
Key rewind semantics: a file rewind failure does not block history rewind. The handler first makes a best-effort attempt to rewind files (a failure is recorded only in fileRewindError and does not interrupt processing), then rewinds conversation history (which must succeed). Therefore, even if workspace files are in use or permissions are insufficient, the session context can still return to the correct point.
Obtaining user_message_id: In the stream-json output stream, snapshot.messageId in a file-history-snapshot message is the corresponding user message ID. You can also use the uuid of a type:"user" message directly from the output stream.
dry_run: true: Only calculates and returns a preview of filesChanged/insertions/deletions; it does not modify the disk or truncate history (historyRewound:false). This is useful for showing a confirmation dialog before performing the actual rewind.
Differences from Claude Code
| Capability | Claude Code | cbc |
|---|---|---|
rewind_files (file rewind) | ✅ | ✅ Fields and semantics strictly aligned |
--resume-session-at (history truncation) | ✅ CLI flag only | ✅ CLI flag |
Runtime rewind (files / history / both, selected by scope) | ❌ Not available | ✅ cbc extension |
| Runtime conversation-history-only rewind | ❌ Not available | ✅ rewind + scope:"Conversation" |
| File rewind failure does not block history rewind | ❌ | ✅ Partial success via fileRewindError |
Calling from an SDK
The TypeScript / Python SDKs do not require new dedicated methods—rewind is a control request sent from the SDK to the CLI, so it can pass through the SDK's existing generic control_request channel. The request and response fields are the same as described above.
TypeScript SDK:
ts
// query is an SDK Query instance; transport.sendControlRequest is the existing generic channel
const resp = await query.transport.sendControlRequest<{
canRewind: boolean;
filesChanged?: string[];
insertions?: number;
deletions?: number;
historyRewound?: boolean;
fileRewindError?: string;
error?: string;
}>({
subtype: 'rewind',
user_message_id: '<uuid>',
scope: 'CodeAndConversation', // Default when omitted; alternatives: 'Conversation' / 'Code'
});
if (resp.canRewind && resp.fileRewindError) {
// Partial success: history was rewound, but file rewind failed—ask the user to handle files manually
}Python SDK:
python
# query is an SDK Query instance; _send_control_request is the existing generic channel
resp = await query._send_control_request({
"subtype": "rewind",
"user_message_id": "<uuid>",
"scope": "Conversation", # Rewind conversation history only
})
# resp: {"canRewind": True, "historyRewound": True, ...}Type definitions (
ControlRewindRequest/ControlRewindResponse/ControlRewindFilesRequest/ControlRewindFilesResponse) are exported from cbc's control-signal protocol module, so TypeScript consumers can import them directly for type hints.
Agent Integration Examples
SRE Incident Response Bot
bash
#!/bin/bash
# Automated incident response agent
investigate_incident() {
local incident_description="$1"
local severity="${2:-medium}"
codebuddy -p "Incident: $incident_description (Severity: $severity)" \
--append-system-prompt "You are an SRE expert. Diagnose the issue, assess impact, and provide immediate action items." \
--output-format json \
--allowedTools "Bash,Read,WebSearch,mcp__datadog" \
--mcp-config monitoring-tools.json
}
# Usage
investigate_incident "Payment API returning 500 errors" "high"Automated Security Review
bash
# Security audit agent for PRs
audit_pr() {
local pr_number="$1"
gh pr diff "$pr_number" | codebuddy -p \
--append-system-prompt "You are a security engineer. Review this PR for vulnerabilities, unsafe patterns, and compliance issues." \
--output-format json \
--allowedTools "Read,Grep,WebSearch"
}
# Use and save to file
audit_pr 123 > security-report.jsonMulti-turn Legal Assistant
bash
# Legal document review with session persistence
session_id=$(codebuddy -p "Start legal review session" --output-format json | jq -r '.session_id')
# Review contract in multiple steps
codebuddy -p --resume "$session_id" "Review liability clauses in contract.pdf"
codebuddy -p --resume "$session_id" "Check compliance with GDPR requirements"
codebuddy -p --resume "$session_id" "Generate executive summary of risks"Best Practices
Use JSON output format for programmatically parsing responses:
bash# Parse JSON response using jq result=$(codebuddy -p "Generate code" --output-format json) code=$(echo "$result" | jq -r '.result') cost=$(echo "$result" | jq -r '.total_cost_usd')Handle errors gracefully - Check exit codes and stderr:
bashif ! codebuddy -p "$prompt" 2>error.log; then echo "An error occurred:" >&2 cat error.log >&2 exit 1 fiUse session management to maintain context across multi-turn conversations
Consider timeouts for long-running operations:
bashtimeout 300 codebuddy -p "$complex_prompt" || echo "Timed out after 5 minutes"Respect rate limits when making multiple requests, by adding delays between calls
Use
-yto perform operations requiring authorization in non-interactive mode:bash# Complete example in non-interactive mode codebuddy -p "Analyze code and run tests" \ --output-format json \ -y \ --allowedTools "Bash,Read,Grep"⚠️ Important Note:
-y(or--dangerously-skip-permissions) is a required parameter for non-interactive mode. When using the-p/--printparameter for non-interactive execution, this parameter must be added to perform operations that require authorization (file read/write, command execution, network requests, etc.), otherwise these operations will be blocked. With-yalone, HIGH/CRITICAL dangerous commands may still require confirmation; to suppress all prompts in an isolated sandbox, useexport CODEBUDDY_IS_SANDBOX=1 && codebuddy -p -y ...(high risk; process environment only). Use this only in trusted environments and for explicit task scenarios. See CLI Reference and Sandbox full pass (high risk) for details.
Related Resources
- CLI Reference - Complete CLI documentation
- Common Workflows - Step-by-step guides for common use cases
- Interactive Mode - Interactive session features
- IAM Permissions - Tool permissions and access control
Tip: Headless mode is ideal for CI/CD pipelines, automation scripts, and agent integrations. Combine it with MCP servers to extend functionality.