Summary
Codex plugin docs/examples imply that plugin-local hooks.json is a supported plugin surface, but the current runtime appears to only load hooks from config-layer hooks.json files (for example ~/.codex/hooks.json).
In practice, an installed plugin can contribute skills / .mcp.json / .app.json, but its plugin-local hooks.json does not seem to run.
Environment
- Codex CLI:
v0.118.0
- OS: Linux
Repro
- Create or install a local plugin with this shape:
.codex-plugin/plugin.json
hooks.json
scripts/runtime-hook.cjs
- Put simple lifecycle hooks in
hooks.json, for example SessionStart, UserPromptSubmit, Stop, each running a command that writes a file under /tmp or $HOME.
- Install/enable the plugin normally so it is present under
~/.codex/plugins/cache/<marketplace>/<plugin>/local and enabled in ~/.codex/config.toml.
- Start a new Codex session.
Expected
If plugin-level hooks are supported, the installed plugin's hooks.json should be discovered and executed.
Actual
The plugin installs and can be enabled, but its lifecycle hooks do not run.
If I place the exact same hooks into ~/.codex/hooks.json, they immediately start working.
Why this looks inconsistent
There are several signals that suggest plugin-local hooks are intended to work:
- The plugins docs/examples mention
hooks.json as a plugin companion surface.
- The public plugin examples include plugin folders with
hooks.json.
- Example manifests include a
hooks field pointing at ./hooks.json.
But the current runtime code seems to say otherwise:
codex-rs/core/src/plugins/manifest.rs parses plugin manifests and recognizes skills, mcpServers, and apps, but not hooks.
codex-rs/hooks/src/engine/discovery.rs discovers hooks by scanning config-layer folders for hooks.json, not installed plugin roots.
So right now the observed behavior is:
- plugin install/load path: plugin capabilities work for skills/MCP/apps
- hook runtime path: only config-layer
hooks.json is actually executed
Request
Could you clarify which of these is intended?
- Plugin-local
hooks.json is supposed to work, and this is a runtime bug.
- Plugin-local
hooks.json is not supported yet, and the docs/examples should be updated to avoid implying support.
If plugin-local hooks are intended to be supported, that would make plugin packaging much cleaner because lifecycle hooks could live inside the plugin bundle instead of requiring a second global ~/.codex/hooks.json installation path.
Summary
Codex plugin docs/examples imply that plugin-local
hooks.jsonis a supported plugin surface, but the current runtime appears to only load hooks from config-layerhooks.jsonfiles (for example~/.codex/hooks.json).In practice, an installed plugin can contribute
skills/.mcp.json/.app.json, but its plugin-localhooks.jsondoes not seem to run.Environment
v0.118.0Repro
.codex-plugin/plugin.jsonhooks.jsonscripts/runtime-hook.cjshooks.json, for exampleSessionStart,UserPromptSubmit,Stop, each running a command that writes a file under/tmpor$HOME.~/.codex/plugins/cache/<marketplace>/<plugin>/localand enabled in~/.codex/config.toml.Expected
If plugin-level hooks are supported, the installed plugin's
hooks.jsonshould be discovered and executed.Actual
The plugin installs and can be enabled, but its lifecycle hooks do not run.
If I place the exact same hooks into
~/.codex/hooks.json, they immediately start working.Why this looks inconsistent
There are several signals that suggest plugin-local hooks are intended to work:
hooks.jsonas a plugin companion surface.hooks.json.hooksfield pointing at./hooks.json.But the current runtime code seems to say otherwise:
codex-rs/core/src/plugins/manifest.rsparses plugin manifests and recognizesskills,mcpServers, andapps, but nothooks.codex-rs/hooks/src/engine/discovery.rsdiscovers hooks by scanning config-layer folders forhooks.json, not installed plugin roots.So right now the observed behavior is:
hooks.jsonis actually executedRequest
Could you clarify which of these is intended?
hooks.jsonis supposed to work, and this is a runtime bug.hooks.jsonis not supported yet, and the docs/examples should be updated to avoid implying support.If plugin-local hooks are intended to be supported, that would make plugin packaging much cleaner because lifecycle hooks could live inside the plugin bundle instead of requiring a second global
~/.codex/hooks.jsoninstallation path.