# Claude Code in scripts and CI: claude -p, JSON output and GitHub Actions

Run claude -p followed by a prompt and Claude Code does the task without the interactive screen, prints the answer and exits. Allow the tools the job needs with --allowedTools, use --output-format json to read the result in a script, and add --bare in CI so the run does not load the machine's or repository's own settings. In GitHub, the Claude Code GitHub Action runs it from @claude comments or on a schedule.

- Page: https://tecyfy.com/coding-agents/claude-code/headless
- Guide: Claude Code (https://tecyfy.com/coding-agents/claude-code), from Anthropic's documentation (https://code.claude.com/docs/en/overview)

Most of the time you talk to Claude Code in its chat screen. Add `-p` (short for print) and it works like any other command-line program instead: it takes a prompt, does the job, prints the answer and exits. That is how you use it in shell scripts, scheduled jobs and CI.

```bash
claude -p "What does the auth module do?"
```

It exits with code 0 on success and another code when the run fails, so a script can check whether it worked.

## Pipe work in and out

`-p` reads standard input, so you can send it a file or another command's output and send its answer on:

```bash
npm test 2>&1 | claude -p "Which tests failed and why? One line each." > failures.txt
```

Piped input is capped at 10 MB; for anything bigger, save it to a file and name the file in the prompt.

A one-line review of your changes before you push, as a `package.json` script:

```json
{
  "scripts": {
    "review:claude": "git diff main | claude -p \"List anything in this diff that looks like a bug, as file:line and one sentence. Say nothing else.\""
  }
}
```

Piping the diff in means Claude needs no permission to run Git itself.

## Allow the tools the job needs

Nobody is there to answer a permission prompt, so allow what the job needs in advance with `--allowedTools`, in the same rule format as your [permission rules](/coding-agents/claude-code/permissions):

```bash
claude -p "Run the tests and fix any failures" \
  --allowedTools "Read,Edit,Bash(npm test *)"
```

`Bash(npm test *)` allows any command that starts with `npm test`. Keep the space before `*`: `Bash(git diff*)` would also allow `git diff-index`.

Then pick a permission mode for everything else:

| Mode | In a script |
| --- | --- |
| `dontAsk` | Refuses anything that would have asked. The safest choice for CI |
| `acceptEdits` | Edits files without asking; other commands still need an allow rule |
| `auto` | A classifier approves or blocks each action instead of you |

```bash
claude -p "Apply the lint fixes" --permission-mode acceptEdits
```

Cap a run with `--max-turns 10` or `--max-budget-usd 2` so a job that goes wrong stops on its own.

## Read the answer in a script

`--output-format json` returns the answer in a `result` field, with the session id and what the run cost:

```bash
claude -p "Summarise this project in 3 sentences" --output-format json | jq -r '.result'
```

For data in a fixed shape, give it a JSON Schema. The data comes back in `structured_output`:

```bash
claude -p "List every API route in src/routes" \
  --output-format json \
  --json-schema '{"type":"object","properties":{"routes":{"type":"array","items":{"type":"string"}}},"required":["routes"]}' \
  | jq '.structured_output.routes'
```

`--output-format stream-json` prints events one per line as they happen, for showing progress.

## Keep runs the same everywhere with --bare

Without it, `claude -p` loads everything an interactive session would: your CLAUDE.md, skills, plugins, MCP servers and hooks, from your home folder and from the project. In a project you did not write, that means its `.claude/settings.json` hooks and `.mcp.json` servers run, and in `-p` mode there is no trust prompt first.

`--bare` loads none of it. The run gets only the file and shell tools and what you pass on the command line:

```bash
claude --bare -p "Summarise README.md" --allowedTools "Read" \
  --append-system-prompt "Answer in plain English for a new team member."
```

Bare mode does not use your subscription sign-in: set `ANTHROPIC_API_KEY` in the environment. Add context with flags such as `--settings`, `--mcp-config` or `--append-system-prompt`. Anthropic recommends `--bare` for scripted runs and plans to make it the default for `-p`.

## Run a skill, or carry on a conversation

A [skill](/coding-agents/claude-code/skills) runs in `-p` mode by putting its command in the prompt: `claude -p "/release-notes"`.

Each `-p` call starts a new conversation unless you continue one. `--continue` picks up the most recent; for a specific one, keep its id:

```bash
id=$(claude -p "Review src/billing for performance problems" --output-format json | jq -r '.session_id')
claude -p "Now write fixes for the 2 worst ones" --resume "$id"
```

## GitHub Actions

The Claude Code GitHub Action runs Claude Code inside your repository's workflows. The quickest setup is from Claude Code itself, in the repository:

```text
/install-github-app
```

It installs the Claude GitHub App, saves your key as a repository secret (`ANTHROPIC_API_KEY`, or `CLAUDE_CODE_OAUTH_TOKEN` for a subscription) and opens a pull request with the workflow file. Merge it, then write in any issue or pull request comment:

```text
@claude the date filter on /orders ignores the time zone. Fix it and add a test.
```

Claude replies in the thread, updates its comment as it works and can push a branch with the fix. Only people with write access to the repository can start it, and bots cannot unless you allow them.

Give the action a `prompt` instead and it runs without being mentioned, on any GitHub event, including a schedule. It then has no shell or GitHub access beyond what you allow in `claude_args`, such as `--allowedTools "mcp__github__list_issues"`.

Every run costs GitHub Actions minutes and tokens. Write specific requests, keep CLAUDE.md short (it is read on every run), and set `--max-turns` in `claude_args` and a timeout on the job. Keys go in GitHub secrets, never in the workflow file, and Claude's changes still need a person to review them before merge.

## Questions

### How do I run Claude Code without the interactive screen?

Use claude -p and a prompt in quotes, such as claude -p "What does the auth module do?". It runs the task, prints the answer and exits with code 0 on success, so it works in scripts, pipes and scheduled jobs.

### How do I stop Claude Code asking for permission in a script?

Allow what the job needs in advance with --allowedTools, such as --allowedTools "Read,Edit,Bash(npm test *)". In a script nobody can answer a prompt, so choose a permission mode too - dontAsk refuses anything not allowed, which suits locked-down CI.

### How do I get JSON output from Claude Code?

Add --output-format json. The answer is in the result field, alongside the session id and the cost. To get data in a fixed shape, add --json-schema with a JSON Schema, and the data comes back in structured_output.

### What does --bare do?

It starts Claude Code without loading hooks, skills, plugins, MCP servers, auto memory or CLAUDE.md from the machine or the folder, so a run behaves the same everywhere and a repository's own hooks cannot run. It needs an API key, not your subscription sign-in. Anthropic recommends it for scripts.

### How do I use Claude Code in GitHub Actions?

Run /install-github-app in Claude Code inside the repository, which installs the Claude GitHub App, saves your key as a secret and opens a pull request with the workflow. After merging it, mention @claude in an issue or pull request comment. You can also give the action a prompt to run on its own, such as on a schedule.

## Sources

- [Run Claude Code programmatically](https://code.claude.com/docs/en/headless)
- [Claude Code GitHub Actions](https://code.claude.com/docs/en/github-actions)
- [CLI reference](https://code.claude.com/docs/en/cli-reference)
