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. Interactive vs Non-Interactive Claude Code: When to Pair, When to Script Interactive vs Non-Interactive Claude Code: When to Pair, When to Script 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 /blog/claude-code-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 , alongside session 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