Summary
Headless permissions describe how Claude Code decides whether a tool call may proceed when it is run non-interactively (claude -p), where there is no human at a terminal to answer an approval prompt. Since v2.1.302, claude -p starts in Manual mode whenever nothing else sets a permission mode: any call that would need approval is denied unless an explicit approval source — a PermissionRequest hook, --permission-prompt-tool, --allowedTools, or permissions.allow — says yes. Read-only actions inside the working directories still run normally. This is a deliberate default-deny posture, and it contrasts sharply with the yoyo implementation, where yoyo -p / piped runs auto-approve everything.
Key Points
- Default is deny. With no permission mode configured, the first call that needs approval fails rather than blocking or silently proceeding.
- Read-only is unaffected. Reads and other non-mutating actions in the working directories execute without approval.
- Four approval paths.
PermissionRequesthooks,--permission-prompt-tool,--allowedTools, andpermissions.alloware the only ways a would-be prompt gets resolved to "allow". --permission-mode dontAskdenies every call that would otherwise prompt — a blunt, non-interactive-safe stance.--permission-prompts none(v2.1.259+) is stricter and more informative: it denies unresolved prompts and tells the model that nobody can approve and it should not retry; it also removesAskUserQuestionfrom the toolset.- Observability.
stream-jsonoutput reportspermission_deniedevents plus apermission_denialssummary in the final result, so denial is machine-readable rather than an opaque failure. - Divergence from yoyo.
yoyo -p/ piped runs auto-approve (auto_approve = config.auto_approve || !is_interactive); tracked as yoyo #1149, a decision for the owner.