jevhooks.git / crates / jevhooks-events

Chapter 7: jevhooks-events, the treaty

Two programs that talk over a socket have to agree on words. If the mod sends "PreToolUse" and the daemon expects "pre_tool_use", nothing crashes: the daemon just refuses the event, the mod reads the refusal as "no opinion", and the plugin quietly stops doing anything. That is the worst kind of bug, the kind that looks like everything is fine.

This crate is the treaty both sides sign. It is a handful of Rust types, and both the mod and the daemon are compiled from them: the mod into JavaScript (chapter 9), the daemon natively (chapter 11). Rename a variant here and both sides change in the same build. They cannot disagree, because there is only one of each word.

flowchart LR
  events["jevhooks-events<br/>HookEvent, Role, Verdict, Decision"]
  events -- "compiled to wasm, then JS" --> modjs["plugin/hooks/jevhooks.js"]
  events -- "compiled natively" --> daemon["plugin/bin/jevhooks"]
  modjs -- "JSON on a Unix socket" --> daemon

It depends on serde and nothing else: no network, no files, no clock. That is not tidiness. The mod's copy has to run inside Claude Code as plain JavaScript, where none of those exist.

Aside: serde. If you come from JavaScript, serde is the part of Rust that JSON.stringify and JSON.parse are built into. Putting #[derive(Serialize, Deserialize)] on a type writes both directions for you, and #[serde(rename_all = "lowercase")] decides that Verdict::Ask travels as "ask".

Try it. cargo test -p jevhooks-events runs its two tests: every event is listed once, and an event's JSON name is its hook name ("PreToolUse", "Stop").

For the people who maintain it

The one place a word is not shared: the daemon's reply, a Decision, is read on the TypeScript side through plugin/types/index.d.ts, which mirrors it by hand (chapter 4).

In this folder

PathWhat
src/Chapter 8: the types.
Cargo.tomlThe crate: serde, and serde_json for the tests.

← Previous: Chapter 6, crates/ · Up: crates · Next: Chapter 8, jevhooks-events/src/ →