cd /news/ai-tools/interactive-vs-non-interactive-claud… · home topics ai-tools article
[ARTICLE · art-48954] src=outofcontext.dev ↗ pub= topic=ai-tools verified=true sentiment=· neutral

Interactive vs Non-Interactive Claude Code: When to Pair, When to Script

Anthropic's Claude Code tool can be run in two modes: interactive REPL for exploratory tasks requiring human judgment, and non-interactive -p mode for scripting and CI pipelines. The -p mode with --bare flag ensures reproducibility by skipping hooks and customizations, making it suitable for automated workflows.

read9 min views1 publishedJul 7, 2026
Interactive vs Non-Interactive Claude Code: When to Pair, When to Script
Image: Outofcontext (auto-discovered)

You already run claude

, watch it read the repo, approve a tool call, nudge it, and move on. That’s one mode. There’s a second one you may not have wired up yet, and it’s the one that turns Claude Code from a thing you talk to into a thing you build with.

Same binary. Same tools. Same agent loop. The only thing that changes is whether you’re steering it live or handing it a job and walking away:

claude "prompt"

— a live REPL. A pairing session.claude -p "prompt"

— one shot, prints to stdout, exits. A Unix command you can pipe, script, and put in CI.

Pick the wrong one and you either babysit a task that should have been a one-liner, or you fire off an unattended run that aborts the first time it needs a decision you didn’t pre-make. Here’s the split I actually use.

Two ways to run the same agent #

The mental model is one sentence: the REPL is a conversation; -p is a command that returns a value.

Everything downstream follows from that. In the REPL you’re present — you approve tools mid-run, ask follow-ups, change your mind. With -p

you’re not there, so every decision has to be made up front, and the output is something a script consumes. Nothing about the agent’s capability changes between them. What changes is who’s in the loop.

Interactive: claude #

and claude "prompt"

claude                          # start a session
claude "explain the auth module and suggest a refactor"   # seed it with a first prompt

Both open the REPL. claude "prompt"

just skips the first thing you’d type. From there you get the interactive affordances:

Mid-run approvals— Claude asks before it runs a tool; you say yes or no.** Permission cycling**—Shift+Tab

moves through permission modes without restarting.Follow-ups— the conversation holds context, so you can steer turn by turn.

Reach for this when the task is exploratory, ambiguous, or needs human judgment somewhere in the middle — the cases where you can’t write the whole spec before you start. It’s a one-off you think through with Claude, not a step in a pipeline.

Non-interactive: claude -p #

/ --print

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

-p

(or --print

) runs the prompt once, prints the response, and exits. No REPL, no interactivity. Because it reads stdin and writes stdout, it behaves like any other command-line tool — which means you can pipe into it and redirect out of it:

cat build-error.txt | claude -p 'concisely explain the root cause of this build error' > output.txt

For scripts and CI, add --bare

:

claude --bare -p "Summarize this file" --allowedTools "Read"

--bare

skips auto-discovery of hooks, skills, plugins, MCP servers, auto memory, and CLAUDE.md

. Without it, claude -p

loads the same context an interactive session would — including whatever happens to be configured in the working directory or ~/.claude

. Bare mode is how you get the same result on every machine: a teammate’s hook or a project .mcp.json

server won’t silently change the run, because bare mode never reads them. Only the flags you pass explicitly take effect.

Two things to know: in bare mode Claude has Bash, file-read, and file-edit tools, and you pass anything else in by flag (--append-system-prompt

, --mcp-config

, --settings

, …). And it skips OAuth and keychain, so auth has to come from ANTHROPIC_API_KEY

(or an apiKeyHelper

in --settings

).

The docs call

--bare

the recommended mode for scripted and SDK calls, and say itwill become the default forWiring it in now is future-proofing, not extra work.-p

in a future release.

--bare

is the reproducibility twin of the interactive-side debugging flag: when a live session misbehaves, --safe-mode strips the same customization layers so you can find which one broke. One skips your context for consistency; the other skips it to isolate a fault.

Making -p #

machine-readable

Text output is fine for a human reading a terminal. For a script, use --output-format

:

text

(default) — plain text.json

— structured JSON: the answer lands in.result

, alongsidesession_id

,total_cost_usd

, and a per-model cost breakdown.stream-json

— newline-delimited JSON events, token by token.

Parse json

with jq

:

claude -p "Summarize this project" --output-format json | jq -r '.result'

Need the output to conform to a shape? Pair --output-format json

with --json-schema

. The result lands in .structured_output

— the extract-and-pipe use case:

claude -p "Extract the main function names from auth.py" \
  --output-format json \
  --json-schema '{"type":"object","properties":{"functions":{"type":"array","items":{"type":"string"}}},"required":["functions"]}' \
  | jq '.structured_output'

For live progress, stream-json

with --verbose --include-partial-messages

emits an event per token. It also emits system/init

(session metadata: model, tools, MCP servers, loaded plugins) and system/api_retry

(attempt count, delay, error category) — useful for CI observability and failing a build when a plugin didn’t load.

Sessions without the REPL #

Headless runs can still be multi-step. Two flags carry state across invocations:

--continue

/-c

— reload the most recent conversation in this directory.--resume

/-r <id|name>

— resume a specific session.

claude -p "Review this codebase for performance issues"
claude -p "Now focus on the database queries" --continue

For multiple parallel conversations, capture the session_id

and resume it by hand:

session_id=$(claude -p "Start a review" --output-format json | jq -r '.session_id')
claude -p "Continue that review" --resume "$session_id"

Run both from the same directory — session lookup is scoped to the current project directory and its git worktrees.

Guardrails: bounding a headless run #

An unattended -p

run can loop on its own, so bound it. These two are print-mode only:

--max-turns N

— cap agentic turns; error on overflow.--max-budget-usd N

— hard dollar ceiling per invocation, stop when hit.

Then set the permission posture. --permission-mode

takes one of: default

, acceptEdits

, plan

, auto

, dontAsk

, bypassPermissions

, manual

. The two that matter for scripting:

dontAsk

— denies anything not in yourpermissions.allow

rules or the built-in read-only command set. This is your locked-down CI mode.acceptEdits

— auto-approves file writes plus common filesystem commands (mkdir

,touch

,mv

,cp

). Other shell commands and network calls still need an allow rule, or the run aborts when one is attempted.

For finer control, allow specific tools with permission-rule syntax:

claude -p "Apply the lint fixes" --allowedTools "Bash(git diff *),Edit"

Mind the space before *

: Bash(git diff *)

prefix-matches git diff …

, while Bash(git diff*)

would also match git diff-index

.

There’s also --dangerously-skip-permissions

(equivalent to --permission-mode bypassPermissions

). It approves everything. Justified in a throwaway sandbox or container you’ve isolated; reckless against a repo you care about or anything with network and secrets. Reach for dontAsk

plus an explicit allow-list first. (The same review-vs-autonomy trade-off shows up in the editor, covered in Cursor auto-review vs YOLO.)

Real recipes #

These are the payoff. Every one is a real -p

invocation.

Explain a failure. Pipe a log in, get the root cause out:

cat build-error.txt | claude -p 'concisely explain the root cause of this build error' > output.txt

Project linter as a package.json script. Piping the diff means Claude doesn’t need Bash permission to read it; the escaped quotes keep it portable to Windows:

{
  "scripts": {
    "lint:claude": "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.\""
  }
}

Commit helper. Review staged changes and write the message:

claude -p "Look at my staged changes and create an appropriate commit" \
  --allowedTools "Bash(git diff *),Bash(git log *),Bash(git status *),Bash(git commit *)"

PR security review. Feed a PR diff in, reshape Claude’s role with --append-system-prompt

, get JSON out:

gh pr diff "$1" | claude -p \
  --append-system-prompt "You are a security engineer. Review for vulnerabilities." \
  --output-format json

Batch loop. One -p

call per item, collect the JSON:

for f in src/**/*.py; do
  claude --bare -p "Summarize what $f does in one sentence" \
    --allowedTools "Read" --output-format json | jq -r '.result'
done

User-invoked skills and custom commands work in -p

too — put /skill-name

in the prompt string and Claude Code expands it before running. Commands that open an interactive dialog, like /login

, don’t work headless.

Which mode, when #

Task shape Mode Key flags
Exploratory, ambiguous, needs mid-run judgment Interactive
One-off you want to think through Interactive claude "prompt"
Deterministic, repeatable, composable -p --bare , --allowedTools
Unattended / CI -p --bare , --permission-mode dontAsk , --max-budget-usd
Output another program parses -p --output-format json , --json-schema
Multi-step scripted workflow -p --output-format json + --resume "$session_id"

Rule of thumb: if a human needs to be in the loop, use the REPL. If a program is, use -p

.

Gotchas #

Things that bite the first time you script this:

No mid-run approvals. In-p

there’s nobody to ask. Decide permissions up front, or the run aborts the first time it hits an un-approved tool. This is the single most common headless surprise.Quoting and escaping. Shell quoting rules apply, and inpackage.json

you escape the double quotes (\"…\"

) for Windows portability.Piped stdin is capped at 10MB(as of v2.1.128). Exceed it and Claude Code exits with a clear error and non-zero status. For larger input, write it to a file and reference the path in the prompt instead of piping.Cost.-p

can loop autonomously. Pair--max-turns

and--max-budget-usd

, and track spend withtotal_cost_usd

from--output-format json

.Great for reproducibility, surprising if you expected your--bare

skips your local context.CLAUDE.md

or MCP servers to load. If a bare run behaves differently than your interactive session, this is usually why.Background tasks at exit. A background Bash task (dev server, watch build) is terminated ~5s after Claude returns its result and stdin closes (a fix landed in v2.1.163 so a never-exiting process no longer holds the run open forever). Background subagents and workflows are waited on instead, capped at ten minutes by default since v2.1.182.

TL;DR #

Use the REPL to think with Claude. Use -p

to build with it. The move: take something you keep doing by hand in the REPL, ship it as a one-liner, then graduate the one-liner to a package.json

script, a git hook, or a CI job. Add --bare

and a permission posture, and it runs the same everywhere.

Sources #

All flags and behavior verified against primary docs on 2026-07-06.

,--bare

). - Run Claude Code programmatically (headless) — https://code.claude.com/docs/en/headless(stdin piping and 10MB cap, output formats, JSON fields,--bare

default note, recipes, session capture, background-task grace period).

── more in #ai-tools 4 stories · sorted by recency
── more on @anthropic 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/interactive-vs-non-i…] indexed:0 read:9min 2026-07-07 ·