Drop-in replacement for claude -p that drives the interactive TUI via PTY, preserving subscription billing
Find a file
jedarden ef79f73ce3 docs(bf-4aw): verify main.rs execution path implementation
- Prompt resolution: --input-file, positional, stdin (exit 4 on errors)
- Session dispatch: session::run() with claude_args
- Result matching: Interrupted(130), Timeout(3), NoResponse(2), other(2)
- Stream-JSON output: replay transcript line-by-line
- AS-5: binary not found check with human-readable error (exit 2)

All 81 tests pass. No dead_code warnings for output format arms.
2026-06-14 01:01:38 -04:00
.beads docs(bf-3ag): verify session implementation complete - all tests pass 2026-06-13 15:56:07 -04:00
docs docs(adr): add ADR-004 — no silent agent escalation in NEEDLE workers 2026-06-11 07:31:56 -04:00
notes docs(bf-4aw): verify main.rs execution path implementation 2026-06-14 01:01:38 -04:00
src fix: five PTY→FIFO pipeline bugs that prevented end-to-end operation 2026-06-13 23:32:50 -04:00
test-fixtures/mock-claude Add bf-5nr validation notes: claude-print-ci WorkflowTemplate YAML is valid 2026-06-10 02:11:37 -04:00
tests Add bf-5nr validation notes: claude-print-ci WorkflowTemplate YAML is valid 2026-06-10 02:11:37 -04:00
~/.needle/state docs(bf-3ag): verify session implementation complete - all tests pass 2026-06-13 15:56:07 -04:00
.needle-predispatch-sha docs(bf-3ag): verify session implementation complete - all tests pass 2026-06-13 15:56:07 -04:00
AGENTS.md docs: update AGENTS.md — add session.rs to module map, document FIFO invariants and timeout-thread detach 2026-06-13 23:32:50 -04:00
Cargo.lock fix(deps): add missing atty and which to Cargo.toml 2026-06-14 00:56:43 -04:00
Cargo.toml fix(deps): add missing atty and which to Cargo.toml 2026-06-14 00:56:43 -04:00
claude-print-ci-workflowtemplate.yml fix(install): use x86_64-linux asset names instead of musl target suffix 2026-06-14 00:10:05 -04:00
claude-print.yaml Add Phase 9: NEEDLE integration — install.sh, claude-print.yaml, --check subcommand 2026-06-10 01:36:28 -04:00
install.sh fix(install): use x86_64-linux asset names instead of musl target suffix 2026-06-14 00:10:05 -04:00
README.md docs: expand README with usage examples, build-from-source, and limitations 2026-06-13 23:32:50 -04:00

claude-print

Drop-in replacement for claude -p (print/headless mode) that drives the Claude Code interactive TUI via PTY — preserving subscription billing after the June 15, 2026 Agent SDK credit split.

Why this exists

Starting June 15, 2026, Anthropic separates claude -p (headless) into a separate Agent SDK credit pool ($100$200/month on Max plans). Only the interactive TUI (cc_entrypoint=cli) continues drawing from the unlimited subscription.

The billing path is determined by an isatty check inside the claude binary: when stdout is a TTY, the session is tagged cc_entrypoint=cli and billed against the subscription. When stdout is a pipe (as with claude -p), it becomes cc_entrypoint=sdk-cli and draws from the credit pool instead.

claude-print allocates a PTY, drives the interactive TUI over it, auto-dismisses the trust dialog, injects the user prompt via bracketed paste, waits for the Stop hook via a FIFO, reads the JSONL transcript, and emits clean stdout output — giving callers claude -p wire-compatible output while billing against the subscription.

Prerequisites

  • Claude Code must be installed and authenticated. See claude.ai/code.
  • An active Claude subscription (Pro or Max plan) is required. The whole point is to bill against subscription, not credits.
  • Linux only. PTY support requires POSIX — no Windows ConPTY.

Install

sh install.sh

install.sh downloads a pre-built static musl binary from GitHub Releases (jedarden/claude-print), runs --check to verify the setup, and copies claude-print.yaml to ~/.needle/agents/ if NEEDLE is present.

Set SKIP_MOCK_CLAUDE=1 to skip the mock_claude test fixture download.

Build from source

git clone https://github.com/jedarden/claude-print
cd claude-print
cargo build --release
# binary at target/release/claude-print

# fully static binary (recommended for deployment):
cargo build --target x86_64-unknown-linux-musl --release

Architectures: x86_64 and aarch64.

Self-check

After install, verify the PTY, Stop hook, and billing entrypoint:

claude-print --check

This confirms cc_entrypoint=cli appears in the session JSONL. install.sh runs this automatically, but it's worth running manually after upgrades.

Usage

claude-print [OPTIONS] [PROMPT]

Reads the prompt from a positional argument, --input-file, or stdin (when not a TTY). These are mutually exclusive.

Examples

# Positional prompt
claude-print "Summarize this in one sentence"

# Stdin pipe
echo "what is the capital of France?" | claude-print

# File input
claude-print --input-file prompt.txt

# Specify a model
claude-print --model claude-opus-4-8 "Write a haiku about Rust"

# JSON output
claude-print --output-format json "what is 2+2?" | jq .text

# Stream-JSON — real-time JSONL event replay
claude-print --output-format stream-json "Write a story"

# Agentic task with tool use
claude-print --max-turns 5 "List files in current dir and summarize"

# Short timeout for quick questions
claude-print --timeout 30 "quick question"

Flags

Flag Short Default Description
[PROMPT] Prompt string (mutually exclusive with --input-file and stdin)
--input-file <FILE> -f Read prompt from file
--model <MODEL> -m claude-sonnet-4-6 Model to use
--max-turns <N> 30 Maximum agentic turns
--output-format <FORMAT> -o text Output format: text, json, stream-json
--allowedTools <LIST> Comma-separated list of allowed tools
--disallowedTools <LIST> Comma-separated list of disallowed tools
--dangerously-skip-permissions Skip permission prompts (dangerous)
--timeout <SECS> 3600 Wall-clock timeout in seconds
--claude-binary <PATH> PATH lookup Path to claude binary
--no-inherit-hooks Disable user hook inheritance
--verbose Write timing traces to stderr
--check Run installation self-test and exit
--version -V Print version and exit
--help -h Print help

Output formats

  • text (default): plain text response, printed to stdout.
  • json: one-line JSON object with text, session_id, model, and usage fields.
  • stream-json: JSONL replay of the raw transcript events in real time, one event per line.

Exit codes

Code Meaning
0 Success
1 Assistant error (is_error: true in transcript)
2 Internal error (PTY spawn, hook setup, parse failure)
3 Timeout exceeded
4 Bad input (missing prompt, unreadable file)
130 Interrupted (SIGINT)

How it works

  1. PTY fork — spawns claude under a PTY so isatty returns true and the session is tagged cc_entrypoint=cli.
  2. Trust dialog dismiss — watches for the one-time "do you trust this project?" prompt and sends the confirmation keypress automatically.
  3. Bracketed paste injection — sends the prompt wrapped in bracketed-paste escape sequences (\x1b[200~ / \x1b[201~), which Claude Code's TUI accepts as user input without triggering shell interpretation.
  4. Stop hook FIFO — installs a temporary Claude Code Stop hook that writes a payload to a FIFO when the response is complete; the process blocks on the FIFO read.
  5. Transcript read — reads the JSONL session transcript Claude writes to ~/.claude/projects/, extracts the assistant turn, and emits it in the requested format.

NEEDLE integration

If you use NEEDLE for LLM fleet dispatch, install.sh automatically copies claude-print.yaml to ~/.needle/agents/. This registers claude-print as the adapter for Anthropic subscription models (sonnet/opus/haiku) so NEEDLE workers bill against the subscription rather than the Agent SDK credit pool. See claude-print.yaml in the repo root for the full adapter config, including --no-inherit-hooks isolation mode and the use_or_lose cost type.

Limitations

  • Linux only — PTY allocation is POSIX. No Windows ConPTY support.
  • Claude Code must be authenticatedclaude-print delegates entirely to the claude binary; it cannot authenticate on its own.
  • One prompt per invocation — there is no multi-turn session mode; each call starts a fresh session.
  • Startup latency ~25s — the PTY handshake and Claude Code startup add overhead versus a direct HTTP call.

Structure

  • docs/notes/ — design decisions, constraints, integration details
  • docs/plan/plan.md — complete implementation plan