Proposal: add PreToolUse/PostToolUse lifecycle hooks to Codex hooks engine

Resolved 💬 6 comments Opened Mar 17, 2026 by pengjunfeng11 Closed Mar 23, 2026

Summary

I'd like to propose adding first-class tool lifecycle hooks to the new hooks engine, specifically:

  • PreToolUse
  • PostToolUse

This would extend the current lifecycle coverage beyond SessionStart and Stop and make it possible to observe, validate, or block tool execution before and after a tool call runs.

I reviewed the recent hooks work in PR #13276 and built a prototype locally using the same overall architecture.

Motivation

The current lifecycle hooks cover session boundaries, but not the most important execution boundary in Codex: tool calls.

Tool-use hooks would enable workflows like:

  • validating tool arguments before execution
  • blocking risky tool invocations with feedback to the model
  • logging tool usage for project memory / learning workflows
  • post-tool verification, auditing, and automatic warnings
  • Claude-style PreToolUse / PostToolUse compatibility for users migrating workflows

Proposed design

Following the direction of the new hooks engine introduced in PR #13276, the proposal is:

  • add new hook event names:
  • PreToolUse
  • PostToolUse
  • support them in hooks.json
  • define explicit input/output schemas for both events
  • route them through the same hook engine / dispatcher / output parser path as the existing lifecycle hooks
  • emit HookStarted / HookCompleted events for these hooks as well
  • invoke them from the core tool dispatch path

Matching

Use the existing matcher-group pattern and match on tool_name, for example:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "shell_command|exec_command",
        "hooks": [
          {
            "type": "command",
            "command": "python my_pre_hook.py"
          }
        ]
      }
    ],
    "PostToolUse": [
      {
        "matcher": "shell_command",
        "hooks": [
          {
            "type": "command",
            "command": "python my_post_hook.py"
          }
        ]
      }
    ]
  }
}

Semantics

  • PreToolUse
  • runs before the tool executes
  • can block execution and provide feedback
  • PostToolUse
  • runs after the tool executes
  • receives execution metadata such as success, duration, and output preview

Example input fields

In the prototype, tool hook payloads include fields like:

  • sessionId
  • turnId
  • cwd
  • transcriptPath
  • model
  • permissionMode
  • callId
  • toolName
  • toolKind
  • toolInput

And for post-tool hooks additionally:

  • executed
  • success
  • durationMs
  • outputPreview

Local prototype status

I implemented this locally against the current hooks architecture and verified:

  • cargo check -p codex-core
  • cargo test -p codex-hooks
  • a focused integration test that confirms both PreToolUse and PostToolUse are actually triggered around a real shell_command

The integration test verifies that:

  • HookStarted / HookCompleted are emitted for both events
  • the hook commands are executed
  • the hook payloads contain the expected tool metadata
  • the wrapped tool still executes normally

Question

Would the maintainers be open to this feature?

I saw the contribution policy in docs/contributing.md and understand that external PRs are only accepted when invited. If this direction makes sense, I'd be happy to open a PR from my prototype branch after maintainer guidance on:

  • event naming / semantics
  • expected schema shape
  • whether PostToolUse should support blocking vs warning-only behavior
  • whether this should coexist with legacy after_tool_use hooks or replace them over time

View original on GitHub ↗

6 Comments

pengjunfeng11 · 4 months ago

Additional context: my main reason for wanting tool-use hooks is usage analysis.

I want to track which tools I use most, which tool sequences repeat in real work, and which tools tend to be used together. I am mainly using this as a foundation for lightweight workflow analysis and continuous learning.

A big reference for me is the everything-claude-code project. What is useful there is not just session memory, but observing tool behavior over time and learning from repeated patterns.

That is why PreToolUse / PostToolUse matter more than session-only hooks for my use case. If I want to understand real Codex workflows, the important signals are around tool boundaries:

  • which tool was used
  • with what input
  • whether it succeeded
  • how long it took
  • what tool came next

So for me, this feature is mainly about tool statistics and workflow learning, not just blocking or policy enforcement.

HeMuling · 4 months ago

This hook would be a very valuable addition, and a concrete use case is RTK: https://github.com/rtk-ai/rtk

Specifically:

  • RTK rewrites common development commands to reduce LLM token consumption by roughly 60–90%.
  • RTK already supports Claude Code and OpenCode, because Claude Code provides a PreToolUse hook and OpenCode provides tool.execute.before.
  • Codex currently supports neither of these. Because of that, I previously added an experimental RTK integration that relies on Codex subprocess configuration to rewrite commands instead: https://github.com/rtk-ai/rtk/pull/642

Native support for a PreToolUse-style hook in Codex would be much cleaner.

If this is not something you plan to support, I would still appreciate guidance on whether it makes sense for me to continue maintaining the experimental RTK workaround.

pengjunfeng11 · 4 months ago

@etraut-openai I'm very excited to submit this PR!!! Could you consider opening permissions, or are there other options?

who96 · 4 months ago

I read the merged hooks work, the current codex-rs/hooks implementation, and the open proposals. I think the narrowest useful proposal to discuss here is smaller than a full PreToolUse + PostToolUse expansion plus a separate PermissionRequest hook.

What seems already true:

  • the merged hooks engine already has the right shape for synchronous lifecycle hooks
  • the current Stop and UserPromptSubmit hooks already show how blocking / feedback semantics are encoded at the hook layer
  • #15211 shows that a shell-focused PreToolUse path is implementable with the current plumbing
  • #13498 is useful context, but it is an open PR aimed at broader lifecycle coverage and is explicitly narrower than the broader Claude-compatible hooks line

If the goal is to land something practical first, the smallest cut that still looks useful to me is:

  1. add blocking PreToolUse
  2. keep the first version to shell-like tools only
  3. make it deny-only for v1
  4. defer PostToolUse, PermissionRequest, persistent allow rules, and non-shell tool coverage

I think that version is easier to review because it fits the current hooks engine, solves an obvious use case, and keeps the schema and test surface much smaller than a combined Pre/Post/PermissionRequest feature.

It would still cover a lot of real workflows, including the command-gating cases described in #14754, while leaving room for a later step toward #15311 if maintainers want a dedicated approval-oriented hook after PreToolUse exists.

If this direction sounds right, I’d expect the next step to be one of:

  • evolve #15211 toward the agreed MVP shape
  • open a new invited PR with this narrower scope
  • or narrow it even further before code

I’m not posting code here because docs/contributing.md is explicit that external code contributions are by invitation only and unsolicited PRs will be closed. I wanted to sanity-check the scope here before going further.

oxysoft · 2 months ago

Indexed this hook ticket in the umbrella tracker: #21753

Goal: collect the scattered Codex hook requests and bugs into one parity matrix for Full Claude Code Hook Parity (29+), while preserving this issue as the detailed thread for its specific behavior.

Ar9av · 1 month ago

For design reference: agent-manual (https://github.com/Ar9av/agent-manual) documents how Claude Code and Gemini CLI implement these hooks today.

Claude Code fires PreToolUse before every tool call with a JSON stdin payload (session_id, transcript_path, hook_event_name, tool_name, tool_input). Exit code 2 blocks the call and sends stderr back to the model as context. PostToolUse runs after with the same fields plus tool_response. Both support a matcher field so hooks can target specific tools (e.g. only Bash, or mcp__github__*).

Gemini CLI calls them BeforeTool and AfterTool. Same general pattern but with hook_event_name instead of event, and AfterTool can suppress the tool output from reaching the model via decision: deny — which Claude Code's PostToolUse doesn't support.

The biggest divergence is blocking on PostToolUse/AfterTool: Claude Code treats it as observe-only; Gemini CLI lets you intercept. Worth deciding which way Codex goes before shipping.