Skip to content
Zarif Automates
Topics:Coding AI

Headless and CI Use of Coding Agents

ZarifZarif
Published Updated

On your laptop, a coding agent stops and asks before it runs a command. In CI, the system that runs checks automatically on every pull request, nobody is there to answer. So you decide the answers ahead of time: which tools run without asking, which are refused, and which secrets the agent can see. Get that right and the rest is flags.

Below are the flags for running Claude Code and Codex without a person at the keyboard (often called headless), the GitHub integrations each vendor offers, how to handle keys, and a read-only review workflow for each tool. Everything is from the vendor docs as of September 26, 2026, starting with Claude Code's non-interactive mode page. The two workflows combine documented settings, and the page says where they go beyond a copied example.

Claude Code in a script

Add -p to any claude command and it runs once, prints the result, and exits (non-interactive docs):

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

You can pipe text into it like any command-line tool. When something fails, it exits with a non-zero code, so a script can tell whether it worked. These are the flags that matter in automation:

FlagWhat it does
--allowedTools "Read,Edit,Bash(git diff *)"Pre-approves these tools. Uses permission-rule syntax. The trailing space-star allows any command that starts with that prefix
--permission-mode dontAskDenies anything that would prompt, except what allow rules cover. The docs call it useful for locked-down CI
--permission-mode acceptEditsWrites files without prompting. Other shell commands still need an allow rule
--output-format jsonReturns the result, session ID, and cost estimate as JSON
--json-schema '...'Forces the result into your schema, in a structured_output field
--bareSkips hooks, skills, plugins, MCP servers, memory, and CLAUDE.md from the machine and repo
--resume "$session_id"Continues a specific earlier run

--bare is worth a closer look. Without it, the docs say a -p session runs the hooks in the repo's .claude/settings.json and connects the servers listed in its .mcp.json. It does this with no trust prompt and no approval per server, even in a folder you've never trusted.

In CI, that means whoever wrote the branch being checked decides what runs. The docs recommend --bare for scripted calls and say it will become the default for -p in a future release. Bare mode also ignores your subscription login and needs ANTHROPIC_API_KEY.

One example from the docs shows what a useful script check looks like. It pipes a diff in, so Claude doesn't need permission to run Bash just to read it:

git diff main | claude -p "you are a typo linter. for each typo in this diff, report filename:line on one line and the issue on the next. return nothing else."

Codex in a script

codex exec is the Codex version (Codex non-interactive docs). Progress messages go to stderr, and only the final message goes to stdout, so you can capture the answer on its own.

The default is the important part. codex exec runs in a read-only sandbox, a fenced-off space where it can look but not change files. You turn on writes with --sandbox workspace-write, or --sandbox danger-full-access for broader access, which the docs reserve for controlled environments. The older --full-auto flag is deprecated and prints a warning.

Other flags you'll use:

  • --json for a stream of events, one JSON object per line
  • -o or --output-last-message to write the final message to a file
  • --output-schema to make the answer match a JSON Schema
  • --ephemeral to skip saving session files
  • codex exec resume --last to continue the last run

For logging in from CI, the docs show setting CODEX_API_KEY for that one command. For GitHub Actions they point to the Codex action instead, which keeps the key behind a proxy.

Cursor's CLI follows the same pattern: agent -p for print mode, and changes are only proposed unless you add --force (Cursor headless docs).

The GitHub integrations

Claude Code GitHub Actions. The action is anthropics/claude-code-action@v1 (docs). The quick setup is /install-github-app inside Claude Code. It installs the Claude GitHub app, stores the secret, and opens a pull request with the workflow.

The action has two modes. With no prompt input, Claude waits for someone to mention @claude in an issue or pull request comment and replies there. With a prompt, it runs on whatever event starts the workflow.

Two checks run before Claude starts. The person who triggered the run needs write access to the repo, and bots are turned away unless they're listed in allowed_bots. On public repositories, GitHub doesn't hand secrets to runs started by pull requests from forks, so reviews only run on branches in the same repository.

The app's permissions are broad. Installing the official Claude GitHub app grants read and write on Actions, Checks, Contents, Discussions, Issues, Pull requests, Repository hooks, and Workflows, because one app serves several Claude features. GitHub doesn't let you accept only some of them. For organizations that only need the action, the docs describe making your own GitHub app with just Contents, Issues, and Pull requests.

Codex. Codex has two routes. The first is Codex cloud, which runs tasks in cloud environments and returns diffs or pull requests. You can start a task from the web, the IDE, the CLI, or by mentioning @codex on GitHub, and @codex review asks for a code review (Codex cloud docs). By default, Codex blocks internet access while the agent works. Setup scripts still get internet so dependencies can install (internet access docs).

The second route is openai/codex-action@v1, which runs codex exec inside your own workflow (openai/codex-action).

Secrets and permissions

The docs from both vendors agree on the basics, and one detail from each is easy to miss.

  • Store keys as repository or organization secrets. Never commit them. Claude's action takes ANTHROPIC_API_KEY or CLAUDE_CODE_OAUTH_TOKEN (a subscription token from claude setup-token). For a secret shared across an organization, the docs recommend an API key, because an OAuth token is tied to one person's subscription.
  • Set the workflow's permissions block to the minimum. A review job needs contents: read. Only jobs that push need contents: write.
  • Claude: id-token: write is required for the action's default GitHub app authentication, per the docs' own examples.
  • Codex: the key is readable in the job. The codex-action README warns that the API key can still be reached through the proxy even in read-only mode. It recommends against running on pull requests you don't trust. By default it uses a drop-sudo setting that takes away admin rights from the job's user before Codex starts.
  • Cap the run. Claude's docs suggest --max-turns in claude_args, workflow timeouts, and GitHub's concurrency controls so a run can't keep spending. Runs on an API key bill at API rates plus your Actions minutes.

Worked workflow: a read-only rules check on pull requests

Here's the goal. On every pull request, the agent reads the changes against the base branch, checks them against the repo's instruction file, and writes what it finds to the job log. It can't edit, push, or comment.

This example was put together for this page. The settings and permission values come from the docs. The prompt and tool list were written here.

Claude Code version. Save as .github/workflows/agent-rules-check.yml:

name: Agent rules check
on:
  pull_request:
    types: [opened, synchronize, ready_for_review]

concurrency:
  group: agent-rules-${{ github.event.pull_request.number }}
  cancel-in-progress: true

jobs:
  check:
    runs-on: ubuntu-latest
    timeout-minutes: 15
    permissions:
      contents: read
      pull-requests: read
      id-token: write
    steps:
      - uses: actions/checkout@v6
        with:
          fetch-depth: 0
      - uses: anthropics/claude-code-action@v1
        with:
          anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
          prompt: |
            Compare this pull request's changes to origin/${{ github.base_ref }}
            with git diff. Check them against the rules in CLAUDE.md.
            List each rule the change breaks, with file and line.
            If it breaks none, say so in one line. Do not edit files.
          claude_args: |
            --max-turns 10
            --allowedTools "Read,Grep,Glob,Bash(git diff *),Bash(git log *)"

What each choice does:

  • fetch-depth: 0 downloads the base branch history so git diff has something to compare against. The docs' examples use fetch-depth: 1, which only grabs the latest commit. That's enough for replying to a mention, but not for a diff against the base.
  • The permissions block grants read only, plus the id-token: write the action needs to authenticate.
  • --allowedTools lists read tools and two git prefixes. The docs say that in automation mode Claude has no shell access until you grant the tools the prompt needs, so nothing else runs.
  • concurrency cancels an old run when new commits land, and timeout-minutes stops one that gets stuck.

Findings show up in the workflow run log, which is the documented default for automation mode. To post a comment instead, the docs' code-review example shows the extra tool and flag it needs.

Codex version. Here's the same check with the Codex action. The checkout and fetch steps follow the action's README example. :read-only is the permission profile the README names for read-only workflows:

name: Codex rules check
on:
  pull_request:
    types: [opened, synchronize]

jobs:
  check:
    runs-on: ubuntu-latest
    timeout-minutes: 15
    permissions:
      contents: read
    steps:
      - uses: actions/checkout@v5
        with:
          ref: refs/pull/${{ github.event.pull_request.number }}/merge
          persist-credentials: false
      - name: Fetch base and head
        env:
          PR_BASE_REF: ${{ github.event.pull_request.base.ref }}
          PR_NUMBER: ${{ github.event.pull_request.number }}
        run: |
          git fetch --no-tags origin "$PR_BASE_REF" "+refs/pull/$PR_NUMBER/head"
      - uses: openai/codex-action@v1
        with:
          openai-api-key: ${{ secrets.OPENAI_API_KEY }}
          permission-profile: ":read-only"
          prompt: |
            Compare the changes between origin/${{ github.base_ref }} and HEAD.
            Check them against the rules in AGENTS.md. List each rule the
            change breaks, with file and line. Do not edit files.

The README notes that permission profiles are beta and need Codex CLI 0.138.0 or later, so don't pin an older codex-version with this input. The final message is available as the step's final-message output if you want a later job to post it.

What to automate first

Start read-only. A check that only writes to the log costs little when it's wrong. A week of its output tells you whether it's worth letting it comment, and later push. The diff review guide in this series covers what to look for when it does start writing code. For deployment concerns beyond coding agents, deploying AI agents to production covers monitoring and rollback.

Previous in the series: Skills and reusable instructions for coding agents. Next: Codex vs Claude Code vs Cursor.

Zarif Choudhury

Zarif Choudhury

Zarif builds AI agents and automation workflows. He writes about what keeps working once real people use it: who is worth reading, the new jobs AI is creating, and agent workflows you can check step by step.