Paths Subjects Questions Quizzes Pricing Search
Advanced Open Free

Why a CI Job Needs --bare, Not Just -p

Your team runs claude -p "summarize the failing tests" --output-format json inside a GitHub Actions job that checks out a fork's PR branch (an external contributor's fork, not your own repository) to generate a test-failure summary as a comment.

  1. Without --bare, what specifically can happen in this job that wouldn't happen in an ordinary interactive claude session opened in a directory you've never trusted before?
  2. Why doesn't Claude Code show the equivalent of a trust-dialog prompt in this -p invocation the way it would interactively?
  3. Rewrite the invocation to close this gap while still letting Claude read the test output and produce a summary.
Solution

1. What can happen without --bare:

A fork's PR branch is attacker-controlled content from Claude Code's perspective — a malicious contributor could commit a .claude/settings.json with hooks, or a .mcp.json pointing at a malicious MCP server, hoping a CI job running Claude Code against their branch will execute it. Without --bare, claude -p auto-discovers and runs a project's .claude/settings.json hooks and connects .mcp.json servers exactly as an interactive session would — meaning a fork's checked-out branch can get its hooks or MCP servers executed inside your CI runner's environment, with whatever access that runner has (secrets, network, write access to your repository via the job's token).

2. Why no trust dialog in -p mode:

Interactive Claude Code shows a workspace-trust dialog the first time you run it in a new directory, specifically so a human can look at what's in .claude/ before anything in it executes. A -p invocation has no interactive terminal to show that dialog in and no human present to answer it — so rather than blocking indefinitely waiting for an answer nobody can give, headless mode proceeds without showing it, which is precisely why the auto-discovered content runs unconditionally unless something else (namely --bare) prevents it.

3. The fixed invocation:

claude --bare -p "summarize the failing tests from the test output below" \
  --allowedTools "Read" \
  --output-format json

--bare skips auto-discovery of hooks, skills, plugins, MCP servers, auto memory, and CLAUDE.md entirely, so nothing checked into the fork's branch can execute as a side effect of running this command. --allowedTools "Read" further scopes what Claude can do to reading files — no Bash, no Edit — which is all a summarization task actually needs. Since --bare doesn't read OAuth credentials or the system keychain, ANTHROPIC_API_KEY needs to be set in the job's environment (from a repository secret) for authentication to work at all.

Share this question

← Back to Headless Mode, CI Automation & the Agent SDK practice

We use cookies for product analytics to improve OmniAtlas. See our Privacy Policy.