Agent Hooks¶
No more swipe-and-sweep context switching for multi-session AI coding.
Agent Hooks gives Claude Code and Codex one local callback layer: a macOS-ready CLI for native permission dialogs and notifications, plus a FastAPI-like framework when you want to own the policy in Python.
What It Looks Like¶

Claude Code permission requests become a native local dialog with Deny, Allow Once, and session-scoped Always Allow.

Codex PreToolUse requests become the same local dialog flow, with Deny, Allow Once, and optional execpolicy short-circuiting for already-allowed Bash commands.
from agent_hooks import AgentHook, PermissionRequestEvent, build_permission_response
from agent_hooks.enums import DialogButton
app = AgentHook()
@app.permission()
def permission_handler(hook_event: PermissionRequestEvent):
if hook_event.tool_name == "Bash":
return build_permission_response(DialogButton.ALLOW_ONCE, hook_event)
return build_permission_response(DialogButton.DENY, hook_event)
One typed handler can serve Claude Code PermissionRequest and Codex PreToolUse without writing provider-specific schema glue.
Centralized local permission UI
Bring provider permission prompts back to the current macOS desktop instead of hunting through separate AI session windows and desktop spaces.
Business logic over wire glue
Write handlers against typed, normalized events while Agent Hooks handles schema differences and response rendering for each provider.
Zero extra local baggage
No runtime Python dependency chain in the library itself and no extra macOS package install beyond what already ships with the OS.
- Keep hook UX local on macOS with the system
osascriptbinary out-of-the-box - Build custom apps with FastAPI-like decorator routes such as
@app.permission()with unified schema - Zero runtime Python package dependencies in the library itself (no dependency chain attack to worry about!)
- Keep raw inputs, rendered outputs, and app logs under one local logging model
- Support both
claude-codeandcodex
Why It Exists¶
Multi-session AI coding tends to break flow in the same places:
- permission prompts appear in separate sessions
- provider payloads differ
- local hook responses need provider-specific wire shapes
- stop and notification events want OS-local behavior, not more terminal noise
Agent Hooks normalizes those problems into one package.
Two Products In One Package¶
What matters most
- Use
agent-hooks callbackwhen you want a working local callback target immediately. - Use
AgentHookwhen you need to define custom permission, notification, or stop behavior in Python.
Built-in CLI¶
The built-in app is exposed as agent_hooks.cli_app.app:app and run through:
This path is designed for local-first usage on macOS:
- permission dialogs
- notifications
- provider-aware response rendering
- rotating logs and audit logs
Framework¶
The framework is centered on AgentHook, a decorator-based router that looks and feels closer to FastAPI than to handwritten hook glue.
You register handlers with route decorators such as:
@app.notification()@app.permission()@app.session_start()@app.user_prompt_submit()@app.post_tool_use()@app.stop()@app.stop_failure()
Provider-Neutral Core¶
Internally, incoming payloads are normalized into shared models before dispatch. That gives you one app-level programming model even when providers use different raw event names.
Examples:
- Claude
PermissionRequestand CodexPreToolUseboth route through@app.permission() - both providers share the same
HookPayloadbase model - provider-specific response wire formats are handled by adapters
Start Here¶
If you want the fastest path, install the tool and wire the built-in callback into your provider config.
Install the CLI:
Put this in ~/.claude/settings.json:
{
"hooks": {
"PermissionRequest": [
{
"hooks": [
{
"type": "command",
"command": "agent-hooks callback --provider claude-code"
}
]
}
],
"Notification": [
{
"matcher": "permission_prompt",
"hooks": [
{
"type": "command",
"command": "agent-hooks callback --provider claude-code"
}
]
}
],
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "agent-hooks callback --provider claude-code"
}
]
}
],
"StopFailure": [
{
"hooks": [
{
"type": "command",
"command": "agent-hooks callback --provider claude-code"
}
]
}
]
}
}
This is enough to route Claude Code permission, notification, and stop events into the built-in callback.
Install the CLI:
If your Codex build still requires the feature flag, add this to ~/.codex/config.toml:
Put this in ~/.codex/hooks.json (The timeout config is optional but recommended to prevent hanging sessions if the callback fails or is misconfigured):
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "agent-hooks callback --provider codex",
"timeout": 30
}
]
}
],
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "agent-hooks callback --provider codex",
"timeout": 30
}
]
}
]
}
}
This is enough to route Codex Bash permission checks and stop notifications into the built-in callback.
Recommended setup
Pass --provider explicitly in your provider config when you can. The built-in callback can infer providers from payload markers, but the explicit flag keeps local setup easier to reason about and debug.
If you want to build your own hook app, start with AgentHook and then run it with agent-hooks run.
Docs Map¶
- Features: what the project does today
- macOS Quickstart: install, wire up, and smoke-test the built-in callback
- Built-in Callback: the out-of-box CLI behavior
- AgentHook: the callback framework
- Architecture Overview: end-to-end callback flow
- Claude Code and Codex: provider-specific implementation details and limitations
Scope¶
Agent Hooks currently supports only two providers:
- Claude Code
- Codex
Current scope
The docs stay aligned with the current implementation. They describe supported behavior that exists today, not placeholder integrations for future providers.
The docs intentionally stay aligned with the current implementation rather than promising future providers.