Running Claude Code non-interactively with claude -p: scripts, cron and CI
Use Claude Code's -p mode from scripts and CI: stdin input, --output-format json, allowing tools with --allowedTools, and bounding cost and loops with --max-turns.
Contents
Claude Code is interactive by default, but the -p (print) option turns it into a non-interactive command: pass one prompt, get the answer on standard output, exit. That makes it usable from shell scripts, cron jobs and CI pipelines, for jobs like summarizing a pull request, diagnosing a failing test or updating documentation.
This article goes from the basic invocation to JSON output, tool permissions and the controls that stop a CI run from spiraling.
KEY POINT
What you will learn
- The basics of
claude -p, including stdin and files as input - Processing results with
--output-format json - Permission, turn and cost controls for CI
Basic form
claude -p "Explain the directory structure of this repository in five lines"
The answer is written to stdout and the process exits. You can pipe data in:
git diff main...HEAD | claude -p "Summarize this diff. Put any breaking changes first."
cat error.log | claude -p "Identify the likely cause of these errors and list the files to inspect"
CLAUDE.md and settings.json are loaded exactly as in interactive mode. Operations that require confirmation, however, are not executed automatically.
JSON output
claude -p "List dependencies in package.json that are a major version behind" --output-format json
The JSON includes the answer text, session ID, duration, cost in USD and token counts. Extract them with jq:
result=$(claude -p "$PROMPT" --output-format json)
echo "$result" | jq -r '.result' # the answer
echo "$result" | jq -r '.total_cost_usd' # cost
echo "$result" | jq -r '.session_id' # for --resume later
--output-format stream-json emits progress as newline-delimited JSON, useful for long runs.
用語解説
Continuing a session: -p also accepts --resume <session_id>, so a script can do research in one call and act on it in a second.
Allowing tools
In non-interactive mode, tools that need confirmation do not run. To let Claude edit files or run commands, allow them explicitly with --allowedTools:
claude -p "Fix the lint errors" \
--allowedTools "Edit" "Bash(npm run lint)" "Bash(npm run lint:fix)"
The syntax matches the permissions in settings.json; see Claude Code permissions in settings.json. --disallowedTools blocks tools the same way.
Use --dangerously-skip-permissions only in throwaway environments
--dangerously-skip-permissions disables every confirmation. On a host machine that means arbitrary commands run unchecked. Limit it to disposable containers and CI runners where nothing of value can be damaged.
Keeping runs and cost bounded
| Option | Effect |
|---|---|
--max-turns N | Caps the agent loop so it cannot retry forever |
--model | Pick the model; routine jobs are usually fine on a Sonnet-class model |
--allowedTools | Grant only what the job needs |
--output-format json | Record total_cost_usd and aggregate it daily |
claude -p "$PROMPT" --max-turns 10 --model claude-sonnet-5 --output-format json
Example: GitHub Actions PR summary
A minimal workflow that comments a summary on each pull request. The API key comes from repository secrets.
name: PR summary
on:
pull_request:
types: [opened, synchronize]
jobs:
summary:
runs-on: ubuntu-latest
permissions:
pull-requests: write
contents: read
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- run: curl -fsSL https://claude.ai/install.sh | bash
- name: Summarize
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
run: |
git diff origin/${{ github.base_ref }}...HEAD \
| ~/.local/bin/claude -p "Summarize this diff and list three review points" \
--max-turns 3 --output-format json \
| jq -r '.result' > summary.md
- uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
await github.rest.issues.createComment({
...context.repo,
issue_number: context.issue.number,
body: fs.readFileSync('summary.md', 'utf8'),
});
The install path varies by environment; check the installer's output if claude is not found. For a fuller integration (replying to comments, fixing issues), the official GitHub Action is simpler.
Where it fits
- Cron every morning: summarize the dependency vulnerability report and post it to Slack
- pre-push hook: a lightweight review of the diff (
--max-turns 2) - Batch jobs: ask the same question across many repositories and collect the answers in a CSV
- Docs generation: update
docs/to match changed APIs and open a PR
Summary
claude -p "prompt"runs non-interactively; data can come from stdin--output-format jsonreturns the answer, cost and session ID forjq- Grant edits and commands explicitly with
--allowedTools, and cap loops with--max-turns - Keep
--dangerously-skip-permissionsto disposable, isolated environments
FAQ
- What happens to confirmation prompts in non-interactive mode?
- Operations that would need confirmation are not executed. Allow them explicitly with --allowedTools, or use --dangerously-skip-permissions inside an isolated environment.
- How do I process the result programmatically?
- Add --output-format json. The output includes the answer, the session ID, cost and token usage.
- How does authentication work in CI?
- Set the ANTHROPIC_API_KEY environment variable to an API key from the Anthropic Console. This is pay-as-you-go, so cap runs with --max-turns.
Primary sources
This article was drafted by AI from official documentation and reviewed by the site operator before publishing. Found a mistake? Let us know via the contact page.