Every point at which Claude Code lets a plugin in, and what jevhooks does there: the page's map.
The list of events and what is done with each is not written here. It is the plugin's own
(jevhooks_events::HookEvent and its role), the same code the mod and the daemon are compiled
from, so the page cannot say an event is judged when the plugin does not judge it. What is
written here is where an event sits in a session, one sentence on what happens there today, and
the ideas that have been written down for it. An idea is only ever one somebody wrote down:
nothing here is a promise, and an event with no idea says so.
10use jevhooks_events::{HookEvent, Role};
Where in a session an event is raised, in the order a session meets them.
23impl Phase { 24 pub const ALL: [Phase; 6] = [Phase::Start, Phase::Prompt, Phase::Loop, Phase::TurnEnd, Phase::Between, Phase::Whenever]; 25 26 pub fn title(self) -> &'static str { 27 match self { 28 Phase::Start => "A session starts", 29 Phase::Prompt => "You send a prompt", 30 Phase::Loop => "The assistant works (the loop)", 31 Phase::TurnEnd => "The turn ends", 32 Phase::Between => "Between turns, and at the end", 33 Phase::Whenever => "Whenever they happen", 34 } 35 } 36}
What jevhooks does with an event today. The first three are the plugin's own Role; the
last two split "nothing" by whether an idea for it has been written down.
Jev is asked, and the answer changes what happens.
43 Judged,
Sent to the daemon, which needs it to judge something else.
45 Heard,
Nothing yet, and an idea is written down.
47 Idea,
52impl State {
The states an event is in. Idea is on the legend too, counted apart (ideas).
54 pub const ALL: [State; 4] = [State::Judged, State::Heard, State::Idea, State::Nothing];
The word on the chip.
The legend's sentence.
67 pub fn means(self) -> &'static str { 68 match self { 69 State::Judged => "Built. Jev is asked, and the answer changes what happens.", 70 State::Heard => "Built. The plugin listens, because it needs this to judge something else.", 71 State::Idea => "Not built. An addition somebody has written down for an event; it is not a promise.", 72 State::Nothing => "Not built, and nothing is planned.", 73 } 74 }
The chip's class in page.css.
What happens there today; empty when nothing does.
92 pub today: &'static str,
97impl Hook {
The event's name, as Claude Code spells it.
From the plugin's own role for the event, so it is never claimed here and absent there.
114const OUTCOME: &str = "What became of a command Jev judged is written to the log, and the memory booked for it is given back.";
Where event sits and what is said of it. No _ arm on purpose: the day Claude Code adds an
event and it is added to HookEvent, this stops compiling until someone places it.
118pub fn hook(event: HookEvent) -> Hook { 119 use HookEvent as E; 120 let (phase, today, ideas): (Phase, &'static str, &'static [&'static str]) = match event { 121 E::Setup => (Phase::Start, "", &[]), 122 E::SessionStart => (Phase::Start, "", &[]), 123 124 E::UserPromptSubmit => ( 125 Phase::Prompt, 126 "The last five prompts are kept: a turn's end is judged against what you asked for. A new prompt also settles any judged command that never ran.", 127 &[ 128 "Attach the standing rule the prompt is about, chosen from the project's own rule files.", 129 "Notice \"note this\" and remind the assistant to write the note first.", 130 "Notice a correction and record it, so it is not needed twice.", 131 "Notice a request that does not say what done looks like.", 132 ], 133 ), 134 E::UserPromptExpansion => (Phase::Prompt, "", &[]), 135 136 E::PreToolUse => ( 137 Phase::Loop, 138 "Every Bash command that is not plainly read-only: what kind of act it is, how hard to undo, how much memory it needs. It runs, is put to you, waits for memory, or is left to your own rules.", 139 &[], 140 ), 141 E::PermissionRequest => (Phase::Loop, "", &[]), 142 E::PermissionDenied => (Phase::Loop, OUTCOME, &[]), 143 E::Elicitation => (Phase::Loop, "", &[]), 144 E::ElicitationResult => (Phase::Loop, "", &[]), 145 E::PostToolUse => (Phase::Loop, OUTCOME, &[]), 146 E::PostToolUseFailure => (Phase::Loop, OUTCOME, &[]), 147 E::PostToolBatch => (Phase::Loop, "", &[]), 148 E::SubagentStart => (Phase::Loop, "", &[]), 149 E::SubagentStop => (Phase::Loop, "", &[]), 150 E::TaskCreated => (Phase::Loop, "", &[]), 151 E::TaskCompleted => (Phase::Loop, "", &[]), 152 153 E::Stop => ( 154 Phase::TurnEnd, 155 "Which of five endings this is: finished, waiting on you, blocked, still running, or stopped early. A confident \"stopped early\" sends the turn back with the reason.", 156 &[], 157 ), 158 E::StopFailure => (Phase::TurnEnd, "", &[]), 159 160 E::TeammateIdle => (Phase::Between, "", &[]), 161 E::PreCompact => (Phase::Between, "", &[]), 162 E::PostCompact => (Phase::Between, "", &[]), 163 E::SessionEnd => (Phase::Between, "The session is forgotten: its prompts, its cost, and any memory still booked for it.", &[]), 164 165 E::Notification => (Phase::Whenever, "", &[]), 166 E::ConfigChange => (Phase::Whenever, "", &[]), 167 E::PreModelSwitch => (Phase::Whenever, "", &[]), 168 E::PostModelSwitch => (Phase::Whenever, "", &[]), 169 E::WorktreeCreate => (Phase::Whenever, "", &[]), 170 E::WorktreeRemove => (Phase::Whenever, "", &[]), 171 E::CwdChanged => (Phase::Whenever, "", &[]), 172 E::FileChanged => (Phase::Whenever, "", &[]), 173 E::DirectoryAdded => (Phase::Whenever, "", &[]), 174 E::InstructionsLoaded => (Phase::Whenever, "", &[]), 175 E::MessageDisplay => (Phase::Whenever, "", &[]), 176 }; 177 Hook { event, phase, today, ideas } 178}
Every event, in the order a session meets them (the order the page shows): each phase's
events as they follow one another, then the ones that can come at any time. A test holds
this to HookEvent::ALL, so an event cannot be left off the page.
183pub const ORDER: [HookEvent; 33] = { 184 use HookEvent as E; 185 [ 186 E::Setup, 187 E::SessionStart, 188 E::UserPromptSubmit, 189 E::UserPromptExpansion, 190 E::PreToolUse, 191 E::PermissionRequest, 192 E::PermissionDenied, 193 E::Elicitation, 194 E::ElicitationResult, 195 E::PostToolUse, 196 E::PostToolUseFailure, 197 E::PostToolBatch, 198 E::SubagentStart, 199 E::SubagentStop, 200 E::TaskCreated, 201 E::TaskCompleted, 202 E::Stop, 203 E::StopFailure, 204 E::TeammateIdle, 205 E::PreCompact, 206 E::PostCompact, 207 E::SessionEnd, 208 E::Notification, 209 E::ConfigChange, 210 E::PreModelSwitch, 211 E::PostModelSwitch, 212 E::WorktreeCreate, 213 E::WorktreeRemove, 214 E::CwdChanged, 215 E::FileChanged, 216 E::DirectoryAdded, 217 E::InstructionsLoaded, 218 E::MessageDisplay, 219 ] 220};
How many events have an idea written down, and how many ideas there are in all. An event can be built and still have ideas for more, so this is not one of the four states.
How many events are in state.
Two more places the plugin steps in that are not among Claude Code's hook events: they belong to the newer plugin interface the mod is written against. Both are for choosing a model, which is off unless switched on.
The map is the plugin's whole surface: every event Claude Code raises is on it, once.
251 #[test] 252 fn every_event_is_on_the_map_once() { 253 for event in HookEvent::ALL { 254 assert_eq!(ORDER.iter().filter(|&&placed| placed == event).count(), 1, "{event:?}"); 255 } 256 // And a phase's events are together, in the order the phases come. 257 let phases: Vec<Phase> = all().iter().map(|hook| hook.phase).collect(); 258 let mut seen: Vec<Phase> = phases.clone(); 259 seen.dedup(); 260 assert_eq!(seen, Phase::ALL); 261 }
What the page calls built is exactly what the plugin's own routing sends to the daemon, and a sentence about "today" is written for those events and for no other.
265 #[test] 266 fn built_is_what_the_plugin_says_is_built() { 267 for hook in all() { 268 let built = hook.event.role() != Role::Ignore; 269 assert_eq!(matches!(hook.state(), State::Judged | State::Heard), built, "{}", hook.name()); 270 assert_eq!(!hook.today.is_empty(), built, "{}: a sentence about today exactly when something happens", hook.name()); 271 } 272 assert_eq!(count(State::Judged), 2); 273 assert_eq!(count(State::Heard), 5); 274 }
The four states cover every event, so the legend's counts add up to the whole list.