Hook Schema
Version: 0
Template
- Shape:
config_fragment - Filename: ``
Ecosystem Notes
- Event naming convergence: Claude and Codex use identical PascalCase event names (PreToolUse, PostToolUse, SessionStart, UserPromptSubmit, Stop, PermissionRequest, ...). Gemini uses BeforeTool / AfterTool. Translator event-mapping table is two-way (Claude/Codex ↔ Gemini), not three-way. Significant translator simplification vs the original schema's three-way assumption.
- Timeout unit divergence has TWO sides, not three: Claude=seconds, Codex=seconds, Gemini=milliseconds. Adapter capability matrix must declare which unit it emits and the translator must convert.
- Timeout corpus anomaly: shanraisshan/claude-code-best-practice (.claude/settings.json) has timeout values 5000 and 30000 in a field Claude treats as seconds — almost certainly the author confused units. Their hooks effectively never time out (5000 seconds = 83 minutes). Translator should warn when a Claude/Codex timeout exceeds a sane upper bound (e.g., 600 seconds) to catch this user error class.
- PostToolUseFailure is in 3 of 7 files (2 Claude + 1 Codex) but is NOT in the published Codex spec event-name table. Treat as canonical based on cross-host adoption; flag in adapter docs that Codex parser behavior on it is undocumented.
- In-the-wild non-spec TOML shapes (NOT in this corpus): flat array-of-tables
[[hooks]] event = '...' command = '...'(seen at alinaqi/maggy) and flat key-value map[hooks] PreToolUse = '...'(seen at vbcherepanov/total-agent-memory). These may not actually install on current Codex — the spec only documents the nested form surveyed here. Translator should accept them as IMPORTS for legacy interop but EMIT the canonical nested form. - Gemini's current settings schema stores hook bindings under the top-level
hooksobject (hooks.BeforeTool,hooks.AfterTool, ...), withhooksConfig.enabledas a global boolean andhooksConfig.disabledas the per-name disable list. The older corpus file used a top-levelenabled: [hook-name, ...]registry; dotpack does NOT emit that legacy registry. The Gemini-onlynamefield on hook-spec remains useful for logs and disabled-list targeting, but is not adopted universally. - Hermes' shell-hook config supports only a subset of the canonical event surface. The adapter can honestly map
PreToolUse,PostToolUse,UserPromptSubmit,SessionStart,SessionEnd,SubagentStart,SubagentStop, andPermissionRequest. Canonical events with no Hermes equivalent (for examplePreCompact,PostCompact,Stop,Notification,PostToolUseFailure) are adapter-side install errors rather than silent drops.
Deliberately Excluded Concepts
Concept: gemini_hook_execution_mode
Gemini-specific execution-mode flags: async toggles non-blocking
hook execution (default: sync), once toggles single-fire-per-session
(default: every-trigger). The omission-failure rationale (Deviation
2 of ADR-0011) doesn't hold — both defaults are functional, just
different from the explicit-true behaviour. Below floor. Adapter
behaviour:
- gemini-cli adapter: emit natively when present.
- non-gemini adapters: surface as lossy. Although the defaults are
functional, an author who explicitly set async: true chose
non-blocking execution; silently emitting a blocking hook elsewhere
changes runtime characteristics. Install proceeds only with
--allow-lossy.
Aliases:
| Host | Field Name |
|---|---|
gemini-cli |
async |
antigravity-cli |
async |
gemini-cli |
once |
antigravity-cli |
once |
Concept: gemini_hook_registry_identifiers
Gemini-specific identifying fields used in hook logs and by
Gemini's hooksConfig.disabled list. Claude and Codex have no
equivalent hook-name control surface; bindings are active when
present. Adapter behaviour:
- gemini-cli adapter: emit natively when present. When absent,
the adapter may synthesize a stable name from the dotpack hook
resource name so Gemini users can later target it in
hooksConfig.disabled.
- non-gemini adapters: drop silently. These fields are pure
identification metadata with no runtime effect on hosts that
don't parse hook names. NOT lossy (lossy_when_dropped: false) —
dropping them on Claude/Codex does not change install behaviour.
This is the canonical example for the lossy_when_dropped: false
escape hatch introduced in ADR-0012 §8. If a future host introduces
a hook-name control concept and would parse these fields, add it to
aliases and flip lossy_when_dropped to true.
Aliases:
| Host | Field Name |
|---|---|
gemini-cli |
name |
antigravity-cli |
name |
gemini-cli |
description |
antigravity-cli |
description |