Skip to the page

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.

Automate

Take this page into your AI

Open in ChatGPT (new tab)Open in Claude (new tab)

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.

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:

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:

{
  "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:

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:

ModeIn a script
dontAskRefuses anything that would have asked. The safest choice for CI
acceptEditsEdits files without asking; other commands still need an allow rule
autoA classifier approves or blocks each action instead of you
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:

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:

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:

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 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:

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:

/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:

@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

Where each fact comes from

  1. 1Run Claude Code programmatically
  2. 2Claude Code GitHub Actions
  3. 3CLI reference