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