็ฎไฝไธญๆ ยท ็น้ซไธญๆ ยท ๆฅๆฌ่ช ยท ํ๊ตญ์ด
Headless Claude Code automation without the headless mode ๐
ccp runs Claude Code like claude -p (feed a prompt, get the final answer on stdout), but bills your subscription instead of the Agent SDK credit pool.
It does this by driving a real interactive Claude Code TUI inside a detached tmux session: it launches Claude, types in your prompt, auto-answers the permission prompts, and scrapes the final reply. You get headless ergonomics; Claude thinks it's a normal interactive session.
claude -p โ headless mode โ Agent SDK credit pool
ccp.sh "..." โ interactive TUI โ subscription pool โ
brew install mthli/tap/ccpThis pulls in tmux and jq automatically and puts a ccp command on your PATH (use ccp wherever the examples below say ./ccp.sh).
You still need claude installed separately and logged in.
git clone https://github.com/mthli/ccp.git
cd ccp
./ccp.sh "say hi"./ccp.sh "<prompt>"The prompt goes in, the final answer comes out on stdout:
./ccp.sh "summarize README.md"
./ccp.sh "say hi" # quick end-to-end smoke test.By default every tool call is auto-approved (allow).
Choose how tool calls are handled with -p:
./ccp.sh -p allow "refactor utils.py" # auto-approve everything (default).
./ccp.sh -p deny "scan the repo" # reject every tool call.
./ccp.sh -p ask "edit config" # defer to the TUI's normal prompt.Even under
allow, a few irreversible Bash commands (rm -rf,mkfs,dd if=, fork bombs, โฆ) are always hard-denied.
-e KEY=VALUE (repeatable) sets env vars on the launched session, inherited by Claude and every hook it runs:
./ccp.sh -e FOO=bar -e DEBUG=1 "print the FOO env var"By default the run uses a unique cc-<pid> tmux session. Pass -s to name it yourself, handy for attaching (tmux attach -t <name>) or running side by side:
./ccp.sh -s review "review the diff"The name must not contain
.or:, and must not match an existing session (ccp never reuses or kills a session it didn't create).
Everything after -- is forwarded verbatim to the underlying claude, so its own flags just work:
./ccp.sh "review the diff" -- --model opus --add-dir /tmp
./ccp.sh "audit deps" -- --settings ./my-settings.json --mcp-config ./mcp.json
--settingsis deep-merged into ccp's own settings; claude's-p/
| Variable | Default | Meaning |
|---|---|---|
CCP_READY_TIMEOUT |
60 |
Seconds to wait for the input box to appear. |
CCP_ANSWER_TIMEOUT |
0 |
Seconds to wait for the answer; 0 = wait forever. |
Run ./ccp.sh --help for the full list of options.
ccp is pure bash, with no build step and no dependencies beyond the three tools above.
It coordinates four files:
ccp.sh- the orchestrator. Writes a throwaway settings file (your real~/.claude/settings.jsonis never touched), launchesclaudein a detached tmux session, pastes the prompt, waits for completion, and prints the answer.hooks/auto-permission.sh- aPreToolUsehook that answers each permission prompt, so the TUI never blocks on a y/n box.hooks/dump-transcript.sh- aStophook that pulls the final assistant reply out of the transcript and signals the orchestrator that it's done.hooks/dump-failure.sh- aStopFailurehook that fires when an API error ends the turn, so ccp stops waiting and exits instead of hanging.
If you run out of usage, ccp doesn't hang: an API-error turn trips the StopFailure hook (exit 5),
and a subscription usage-limit wall (which the TUI shows without ending the turn, firing no hook) is
detected in the pane and exits 4. The tmux session and temp files are cleaned up on every exit.
MIT License
Copyright (c) 2026 Matthew Lee