219 lines
No EOL
12 KiB
Markdown
219 lines
No EOL
12 KiB
Markdown
---
|
||
title: "Run Claude Code programmatically"
|
||
source: "https://code.claude.com/docs/en/headless"
|
||
author:
|
||
published:
|
||
created: 2026-04-17
|
||
description: "Use the Agent SDK to run Claude Code programmatically from the CLI, Python, or TypeScript."
|
||
tags:
|
||
- "clippings"
|
||
---
|
||
The [Agent SDK](https://code.claude.com/docs/en/agent-sdk/overview) gives you the same tools, agent loop, and context management that power Claude Code. It’s available as a CLI for scripts and CI/CD, or as [Python](https://code.claude.com/docs/en/agent-sdk/python) and [TypeScript](https://code.claude.com/docs/en/agent-sdk/typescript) packages for full programmatic control.
|
||
|
||
The CLI was previously called “headless mode.” The `-p` flag and all CLI options work the same way.
|
||
|
||
To run Claude Code programmatically from the CLI, pass `-p` with your prompt and any [CLI options](https://code.claude.com/docs/en/cli-reference):
|
||
|
||
```shellscript
|
||
claude -p "Find and fix the bug in auth.py" --allowedTools "Read,Edit,Bash"
|
||
```
|
||
|
||
This page covers using the Agent SDK via the CLI (`claude -p`). For the Python and TypeScript SDK packages with structured outputs, tool approval callbacks, and native message objects, see the [full Agent SDK documentation](https://code.claude.com/docs/en/agent-sdk/overview).
|
||
|
||
## Basic usage
|
||
|
||
Add the `-p` (or `--print`) flag to any `claude` command to run it non-interactively. All [CLI options](https://code.claude.com/docs/en/cli-reference) work with `-p`, including:
|
||
|
||
- `--continue` for [continuing conversations](#continue-conversations)
|
||
- `--allowedTools` for [auto-approving tools](#auto-approve-tools)
|
||
- `--output-format` for [structured output](#get-structured-output)
|
||
|
||
This example asks Claude a question about your codebase and prints the response:
|
||
|
||
```shellscript
|
||
claude -p "What does the auth module do?"
|
||
```
|
||
|
||
### Start faster with bare mode
|
||
|
||
Add `--bare` to reduce startup time by skipping auto-discovery of hooks, skills, plugins, MCP servers, auto memory, and CLAUDE.md. Without it, `claude -p` loads the same [context](https://code.claude.com/docs/en/how-claude-code-works#the-context-window) an interactive session would, including anything configured in the working directory or `~/.claude`.
|
||
|
||
Bare mode is useful for CI and scripts where you need the same result on every machine. A hook in a teammate’s `~/.claude` or an MCP server in the project’s `.mcp.json` won’t run, because bare mode never reads them. Only flags you pass explicitly take effect.
|
||
|
||
This example runs a one-off summarize task in bare mode and pre-approves the Read tool so the call completes without a permission prompt:
|
||
|
||
```shellscript
|
||
claude --bare -p "Summarize this file" --allowedTools "Read"
|
||
```
|
||
|
||
In bare mode Claude has access to the Bash, file read, and file edit tools. Pass any context you need with a flag:
|
||
|
||
| To load | Use |
|
||
| --- | --- |
|
||
| System prompt additions | `--append-system-prompt`, `--append-system-prompt-file` |
|
||
| Settings | `--settings <file-or-json>` |
|
||
| MCP servers | `--mcp-config <file-or-json>` |
|
||
| Custom agents | `--agents <json>` |
|
||
| A plugin directory | `--plugin-dir <path>` |
|
||
|
||
Bare mode skips OAuth and keychain reads. Anthropic authentication must come from `ANTHROPIC_API_KEY` or an `apiKeyHelper` in the JSON passed to `--settings`. Bedrock, Vertex, and Foundry use their usual provider credentials.
|
||
|
||
`--bare` is the recommended mode for scripted and SDK calls, and will become the default for `-p` in a future release.
|
||
|
||
## Examples
|
||
|
||
These examples highlight common CLI patterns. For CI and other scripted calls, add [`--bare`](#start-faster-with-bare-mode) so they don’t pick up whatever happens to be configured locally.
|
||
|
||
### Get structured output
|
||
|
||
Use `--output-format` to control how responses are returned:
|
||
|
||
- `text` (default): plain text output
|
||
- `json`: structured JSON with result, session ID, and metadata
|
||
- `stream-json`: newline-delimited JSON for real-time streaming
|
||
|
||
This example returns a project summary as JSON with session metadata, with the text result in the `result` field:
|
||
|
||
```shellscript
|
||
claude -p "Summarize this project" --output-format json
|
||
```
|
||
|
||
To get output conforming to a specific schema, use `--output-format json` with `--json-schema` and a [JSON Schema](https://json-schema.org/) 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 and returns them as an array of strings:
|
||
|
||
```shellscript
|
||
claude -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"]}'
|
||
```
|
||
|
||
Use a tool like [jq](https://jqlang.github.io/jq/) to parse the response and extract specific fields:
|
||
|
||
```shellscript
|
||
# Extract the text result
|
||
claude -p "Summarize this project" --output-format json | jq -r '.result'
|
||
|
||
# Extract structured output
|
||
claude -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'
|
||
```
|
||
|
||
### Stream responses
|
||
|
||
Use `--output-format stream-json` with `--verbose` and `--include-partial-messages` to receive tokens as they’re generated. Each line is a JSON object representing an event:
|
||
|
||
```shellscript
|
||
claude -p "Explain recursion" --output-format stream-json --verbose --include-partial-messages
|
||
```
|
||
|
||
The following example uses [jq](https://jqlang.github.io/jq/) to filter for text deltas and display just the streaming text. The `-r` flag outputs raw strings (no quotes) and `-j` joins without newlines so tokens stream continuously:
|
||
|
||
```shellscript
|
||
claude -p "Write a poem" --output-format stream-json --verbose --include-partial-messages | \
|
||
jq -rj 'select(.type == "stream_event" and .event.delta.type? == "text_delta") | .event.delta.text'
|
||
```
|
||
|
||
When an API request fails with a retryable error, Claude Code emits a `system/api_retry` event before retrying. You can use this to surface retry progress or implement custom backoff logic.
|
||
|
||
| Field | Type | Description |
|
||
| --- | --- | --- |
|
||
| `type` | `"system"` | message type |
|
||
| `subtype` | `"api_retry"` | identifies this as a retry event |
|
||
| `attempt` | integer | current attempt number, starting at 1 |
|
||
| `max_retries` | integer | total retries permitted |
|
||
| `retry_delay_ms` | integer | milliseconds until the next attempt |
|
||
| `error_status` | integer or null | HTTP status code, or `null` for connection errors with no HTTP response |
|
||
| `error` | string | error category: `authentication_failed`, `billing_error`, `rate_limit`, `invalid_request`, `server_error`, `max_output_tokens`, or `unknown` |
|
||
| `uuid` | string | unique event identifier |
|
||
| `session_id` | string | session the event belongs to |
|
||
|
||
The `system/init` event reports session metadata including the model, tools, MCP servers, and loaded plugins. It is the first event in the stream unless [`CLAUDE_CODE_SYNC_PLUGIN_INSTALL`](https://code.claude.com/docs/en/env-vars) is set, in which case `plugin_install` events precede it. Use the plugin fields to fail CI when a plugin did not load:
|
||
|
||
| Field | Type | Description |
|
||
| --- | --- | --- |
|
||
| `plugins` | array | plugins that loaded successfully, each with `name` and `path` |
|
||
| `plugin_errors` | array | plugin load-time errors such as an unsatisfied dependency version, each with `plugin`, `type`, and `message`. Affected plugins are demoted and absent from `plugins`. The key is omitted when there are no errors |
|
||
|
||
When [`CLAUDE_CODE_SYNC_PLUGIN_INSTALL`](https://code.claude.com/docs/en/env-vars) is set, Claude Code emits `system/plugin_install` events while marketplace plugins install before the first turn. Use these to surface install progress in your own UI.
|
||
|
||
| Field | Type | Description |
|
||
| --- | --- | --- |
|
||
| `type` | `"system"` | message type |
|
||
| `subtype` | `"plugin_install"` | identifies this as a plugin install event |
|
||
| `status` | `"started"`, `"installed"`, `"failed"`, or `"completed"` | `started` and `completed` bracket the overall install; `installed` and `failed` report individual marketplaces |
|
||
| `name` | string, optional | marketplace name, present on `installed` and `failed` |
|
||
| `error` | string, optional | failure message, present on `failed` |
|
||
| `uuid` | string | unique event identifier |
|
||
| `session_id` | string | session the event belongs to |
|
||
|
||
For programmatic streaming with callbacks and message objects, see [Stream responses in real-time](https://code.claude.com/docs/en/agent-sdk/streaming-output) in the Agent SDK documentation.
|
||
|
||
### Auto-approve tools
|
||
|
||
Use `--allowedTools` to let Claude use certain tools without prompting. This example runs a test suite and fixes failures, allowing Claude to execute Bash commands and read/edit files without asking for permission:
|
||
|
||
```shellscript
|
||
claude -p "Run the test suite and fix any failures" \
|
||
--allowedTools "Bash,Read,Edit"
|
||
```
|
||
|
||
To set a baseline for the whole session instead of listing individual tools, pass a [permission mode](https://code.claude.com/docs/en/permission-modes). `dontAsk` denies anything not in your `permissions.allow` rules or the [read-only command set](https://code.claude.com/docs/en/permissions#read-only-commands), which is useful for locked-down CI runs. `acceptEdits` lets Claude write files without prompting and also auto-approves common filesystem commands such as `mkdir`, `touch`, `mv`, and `cp`. Other shell commands and network requests still need an `--allowedTools` entry or a `permissions.allow` rule, otherwise the run aborts when one is attempted:
|
||
|
||
```shellscript
|
||
claude -p "Apply the lint fixes" --permission-mode acceptEdits
|
||
```
|
||
|
||
### Create a commit
|
||
|
||
This example reviews staged changes and creates a commit with an appropriate message:
|
||
|
||
```shellscript
|
||
claude -p "Look at my staged changes and create an appropriate commit" \
|
||
--allowedTools "Bash(git diff *),Bash(git log *),Bash(git status *),Bash(git commit *)"
|
||
```
|
||
|
||
The `--allowedTools` flag uses [permission rule syntax](https://code.claude.com/docs/en/settings#permission-rule-syntax). The trailing ` *` enables prefix matching, so `Bash(git diff *)` allows any command starting with `git diff`. The space before `*` is important: without it, `Bash(git diff*)` would also match `git diff-index`.
|
||
|
||
User-invoked [skills](https://code.claude.com/docs/en/skills) like `/commit` and [built-in commands](https://code.claude.com/docs/en/commands) are only available in interactive mode. In `-p` mode, describe the task you want to accomplish instead.
|
||
|
||
### Customize the system prompt
|
||
|
||
Use `--append-system-prompt` to add instructions while keeping Claude Code’s default behavior. This example pipes a PR diff to Claude and instructs it to review for security vulnerabilities:
|
||
|
||
```shellscript
|
||
gh pr diff "$1" | claude -p \
|
||
--append-system-prompt "You are a security engineer. Review for vulnerabilities." \
|
||
--output-format json
|
||
```
|
||
|
||
See [system prompt flags](https://code.claude.com/docs/en/cli-reference#system-prompt-flags) for more options including `--system-prompt` to fully replace the default prompt.
|
||
|
||
### Continue conversations
|
||
|
||
Use `--continue` to continue the most recent conversation, or `--resume` with a session ID to continue a specific conversation. This example runs a review, then sends follow-up prompts:
|
||
|
||
```shellscript
|
||
# First request
|
||
claude -p "Review this codebase for performance issues"
|
||
|
||
# Continue the most recent conversation
|
||
claude -p "Now focus on the database queries" --continue
|
||
claude -p "Generate a summary of all issues found" --continue
|
||
```
|
||
|
||
If you’re running multiple conversations, capture the session ID to resume a specific one:
|
||
|
||
```shellscript
|
||
session_id=$(claude -p "Start a review" --output-format json | jq -r '.session_id')
|
||
claude -p "Continue that review" --resume "$session_id"
|
||
```
|
||
|
||
## Next steps
|
||
|
||
- [Agent SDK quickstart](https://code.claude.com/docs/en/agent-sdk/quickstart): build your first agent with Python or TypeScript
|
||
- [CLI reference](https://code.claude.com/docs/en/cli-reference): all CLI flags and options
|
||
- [GitHub Actions](https://code.claude.com/docs/en/github-actions): use the Agent SDK in GitHub workflows
|
||
- [GitLab CI/CD](https://code.claude.com/docs/en/gitlab-ci-cd): use the Agent SDK in GitLab pipelines |