claude-p Daemon Backend — Flag Discovery Entrypoint
The installed CLI's own help is the authority for Claude Code flags; this page is
only the entrypoint. Conversion rules and persistence live in the parent
reference/cli-backends/SKILL.md. claude-p is the
canonical print-mode backend id; claude-code is a compatibility alias.
Discover flags from the installed CLI
- Confirm the CLI is installed:
which claude→${HOME}/.local/bin/claude. - Run
claude --versionandclaude --helpin bash. The daemon wrapsclaude --print; the print-mode flags are the relevant surface. - Translate what you found into
backend_optionswith the parent's generic conversion rules. Nothing Claude-specific is added to that contract here.
Key flags at a glance
The installed CLI's --help is still the authority — this table only shortcuts
the flags you will most often translate; it is not a catalog. Long flags
translate through backend_options underscore keys per the parent's conversion
rules ({"fallback_model": "claude-sonnet-5"} → --fallback-model claude-sonnet-5); the example below shows one full translation.
| Flag | Purpose |
|---|---|
-p / --print |
Non-interactive mode — run, print result, exit. Harness-owned: the daemon already passes it (reserved). |
--dangerously-skip-permissions |
Skip permission prompts (required for automation). The harness already passes it for every run. |
--effort max |
Maximum reasoning effort for complex tasks |
--model opus / --model sonnet |
Model choice (Sonnet is the default) |
--max-budget-usd N |
Spending limit per call — the only spend bound; see Harness boundary |
--allowedTools "Bash Edit Read Write" |
Restrict which tools Claude can use |
--system-prompt "..." |
Custom system prompt |
--add-dir /path/to/dir |
Grant access to additional directories |
-d /path/to/repo |
Set working directory |
Example: automatic fallback model for a long print run
Through backend_options, an underscore key becomes a dashed long flag:
{
"backend": "claude-p",
"tasks": [{
"task": "Implement and validate the change.",
"tools": [],
"backend_options": {
"fallback_model": "claude-sonnet-5"
}
}]
}
// argv: --fallback-model claude-sonnet-5
The model-name vocabulary belongs to the installed CLI and the provider account — LingTai does not validate, enumerate, or simulate model names.
Profile selection via backend_options.env
backend_options reserves one non-flag key, env (string → string), injected
into the spawned CLI subprocess. Here it picks which Claude profile the run
authenticates as, via CLAUDE_CONFIG_DIR:
"backend_options": {
"env": {"CLAUDE_CONFIG_DIR": "/Users/me/.claude-profiles/phai-labs/config"}
}
// emits no argv token; the variable is set on the spawn instead
Applied after the daemon's env stripping, so it wins over the inherited
environment. The value is used verbatim ($HOME/~ are not expanded — pass an
absolute path). Verified profiles live under ~/.claude-profiles/; inspect one
with read-only claude auth status or claude -p '/usage' only — never logout,
re-authenticate, or copy credential files between profiles.
Subscription & auth
Uses the human's Claude subscription (Pro/Max) via claude login OAuth
(~/.claude/.credentials.json) — no additional API costs. The daemon spawn
strips ANTHROPIC_API_KEY / ANTHROPIC_AUTH_TOKEN / CLAUDE_CODE_OAUTH_TOKEN
so refreshed OAuth wins over stale inherited tokens.
Manual shell calls: the env -u auth-hygiene wrapper
Shell remains a supported way to run claude — but a manual claude -p
invocation is not protected by the daemon's env stripping, so wrap it:
env \
-u CLAUDE_CODE_OAUTH_TOKEN \
-u ANTHROPIC_API_KEY \
-u ANTHROPIC_AUTH_TOKEN \
-u ANTHROPIC_BASE_URL \
-u ANTHROPIC_MODEL \
-u ANTHROPIC_SMALL_FAST_MODEL \
claude -p "your prompt here" --dangerously-skip-permissions
Why the
env -u …prefix? IfANTHROPIC_API_KEY(or relatedANTHROPIC_*variables) is set in the agent environment, theclaudeCLI prefers the API-key billing path over the Claude Max subscription/OAuth token — that path can fail withCredit balance is too lowand bills the API key instead of using the subscription. Separately, a stale inheritedCLAUDE_CODE_OAUTH_TOKENcan override a refreshed~/.claude/.credentials.jsonand make Claude Code falsely reportYou've hit your weekly limit. Unsetting these variables for the child forces Claude Code onto the current first-party OAuth/subscription credentials. If you've confirmed your environment has no auth overrides you can drop the prefix; when in doubt, keep it. Never echo the variable values while diagnosing — they are secrets.
Weekly-limit smoke test
If claude reports You've hit your weekly limit from inside LingTai but the
human recently refreshed Claude Code OAuth credentials, first rule out a stale
inherited env token before concluding the subscription is truly exhausted:
# Do not print token values. This only removes the stale override for the child.
env -u CLAUDE_CODE_OAUTH_TOKEN claude -p 'Reply exactly OK' --allowedTools Read -c
If this succeeds while plain claude -p ... fails, the problem is a stale env
override, not an exhausted subscription — keep using the sanitized env -u ...
wrapper (the daemon backend strips the override automatically).
Find and remove the stale-token source
The smoke test proves a child process can work when the bad override is
removed. To make the fix durable, find where the variable is being exported and
remove or comment out that source. Common places are shell startup files
(~/.zshrc, ~/.zprofile, ~/.bashrc, ~/.bash_profile) or launch-service
environment configuration. Safe diagnostic commands (names, never values):
# 1. Check whether macOS launchd is injecting it. Do not print token values.
if launchctl getenv CLAUDE_CODE_OAUTH_TOKEN >/dev/null 2>&1; then
echo "launchctl may define CLAUDE_CODE_OAUTH_TOKEN"
fi
# 2. Search shell startup files for the variable name, not the value.
grep -n 'CLAUDE_CODE_OAUTH_TOKEN\|ANTHROPIC_API_KEY\|ANTHROPIC_AUTH_TOKEN' \
~/.zshenv ~/.zprofile ~/.zshrc ~/.bash_profile ~/.bashrc ~/.profile 2>/dev/null
# 3. Verify a clean future shell does not recreate the variable.
env -u CLAUDE_CODE_OAUTH_TOKEN /bin/zsh -lc \
'test -z "${CLAUDE_CODE_OAUTH_TOKEN:-}" && echo NOT_SET || echo STILL_SET'
If the variable is hard-coded in a shell startup file, comment out only that
export line and keep a backup. Already-running LingTai agents may still have
inherited the old environment until they are refreshed or restarted; for those
current processes, keep using the env -u ... child-process wrapper.
Official docs: https://platform.claude.com/docs/en/docs/claude-code
Harness boundary
The harness spawns claude --print --dangerously-skip-permissions --output-format stream-json --verbose --name <em_id>, then your
backend_options argv, then harness-owned MCP flags, with the task prompt as
the trailing positional argument. Validation refuses the harness-owned flags
--settings, --print, --output-format, --mcp-config, and
--strict-mcp-config in backend_options before spawn: breaking stream-json
output or the per-run MCP config silently breaks progress/result extraction
and completion enforcement.
Related run-scoped behavior you should not fight through flags:
- MCP: the harness writes stdio registrations (including
daemon_common) to the run'sclaude-mcp-config.jsonand appends--mcp-config <path> --strict-mcp-configitself asbackend_harness_argv. - Safe mode:
--safe-modedisables customizations including MCP servers; do not use it because claude-p terminal success requires the injecteddaemon_common.finish(status="done"). For read-only runs, keep MCP enabled and combine a read-only brief with the live-help--allowedToolssurface. - Resume:
daemon(action="ask", input={"id": ..., "message": ...})runsclaude --resume <claude_session_id> --print ...against the session id persisted todaemon.json.claude_session_id;backend_optionsare not re-passed on ask — emanate-time flags persist for the session's life. - No built-in timeout: Claude Code has no built-in timeout — nothing in the
CLI bounds a run.
--max-budget-usd Nis the only spend bound and there is no daemon-side equivalent: pass it viabackend_optionswhenever the scope is unknown, so a runaway task hits a budget cap instead of running indefinitely. - Print-mode background jobs never notify back: inside
claude -p, the inner Claude's own background jobs (run_in_background,&, and wait-loops) are interactive-session affordances; in--printthere is no second prompt and no<task-notification>re-entry, so the model is never woken when the job finishes. If you are the Claude running inside aclaude -pdaemon, do not background a job and then end your turn waiting for its completion — run validation synchronously with an adequate explicit timeout and read the result in the same turn, or report a blocker. Theclaude-pbackend enforces this: a run that ends while awaiting a background-job notification is marked failed, not done. - Auth-env hygiene: the daemon spawn strips
ANTHROPIC_API_KEY,ANTHROPIC_AUTH_TOKEN, andCLAUDE_CODE_OAUTH_TOKENso refreshed OAuth wins over stale inherited tokens (a stale token surfaces as a false "weekly limit" — the smoke test above distinguishes it from a truly exhausted subscription). Manual shell calls need the fullenv -uwrapper in Subscription & auth above. Never print token values.
Troubleshooting
| Issue | Fix |
|---|---|
| Timeout after 30s | For a genuinely short inline task, set an explicit modest bash timeout (for example 300s). For long/complex work, prefer the claude-p daemon backend or a supervised background wrapper instead of blocking the agent turn. |
Agent appears stuck while claude -p runs |
You likely used synchronous CLI for work that should have been daemon-backed or supervised in the background. Inspect/kill the child if needed, then resume with a non-blocking wrapper. |
| Claude Code not found | Check which claude → ${HOME}/.local/bin/claude |
| Output truncated | Check whether the run hit the budget limit (--max-budget-usd N) |
| Rate limited | Wait and retry; the Claude Max tier is generous (rate-limit tier default_claude_max_20x, effectively unlimited for typical use) |
Credit balance is too low |
An ANTHROPIC_* env override is bypassing the subscription — see the env -u wrapper above |