1# jevhooks: a guide in thirteen chapters 2 3**Jev judges what Claude Code is about to do.** Claude Code is an AI coding 4assistant that runs in your terminal. It runs shell commands for you and 5decides for itself when it has finished. jevhooks is a plugin that, at those 6two moments, stops to ask a second opinion of something that does not 7write, cannot be talked round, and costs a few thousandths of a cent to ask. 8 9That something is Jev, TypeSafe AI's "System One" model. Jev does not write 10text. You hand it a situation and a question whose answers are already 11written down (one of these eight kinds of act; somewhere on this four-step 12scale), and it says how likely each answer is, in about a quarter of a 13second, with numbers that mean what they say. jevhooks asks it two things: 14 15- **Before a Bash command runs:** what kind of act is this (one of eight, 16 from "only reads" to "runs code nobody has read"), how hard would it be to 17 put back (four levels), and how much of the machine will it take (four 18 levels)? A confident consequential act, or anything hard to undo, is put 19 to you with the reason. A heavy command is put to you when too little 20 memory is free for it. Ordinary work runs without a prompt. 21- **Before a turn ends:** which of five endings is this (finished, waiting 22 on you, blocked, still running, stopped early)? A confident "stopped 23 early" is sent back to the assistant with the reason, and the turn goes 24 on. 25 26And one promise underneath both: **Jev being slow, absent, unsure or broken 27never blocks anything.** Then the verdict is "pass", and whatever would have 28happened without the plugin happens. 29 30Jev's own documentation is at <https://docs.typesafe.ai>. 31 32> **Try it.** You do not need Rust, a Jev key or a GitHub account to see 33> the plugin work. With Claude Code installed: 34> 35> git clone --recurse-submodules https://lmjtfy.fun/jevhooks.git 36> cd jevhooks 37> claude plugin test plugin 38> 39> Eight tests run against a cardboard stand-in for the daemon (chapter 5) 40> and pass in about five seconds. One of them sends `git status` and checks 41> that nothing left the process at all; chapter 10 says why. 42 43## How to read this 44 45Every folder in this repository is a chapter, and each one ends with a link 46to the next. Read them in order and you will know how all of it works; jump 47in anywhere and the chapter tells you what it is and what is beside it. Each 48chapter starts with the idea, in plain words, and ends with the parts a 49maintainer needs (files, invariants, commands). The `CLAUDE.md` beside each 50README is what an AI agent working in that folder is told on top of it. 51 52The guide follows an event from where Claude Code raises it to where Jev 53answers it: first the plugin Claude Code loads, then the Rust behind it. 54 55| Chapter | Folder | What you learn | 56| --- | --- | --- | 57| 1 | [plugin/](plugin/) | What a Claude Code plugin is, and what this one brings. | 58| 2 | [plugin/.claude-plugin/](plugin/.claude-plugin/) | The manifest: a name, a version, and a promise about state. | 59| 3 | [plugin/hooks/](plugin/hooks/) | The mod: hooks with a `next`, like middleware, and the band above the prompt. | 60| 4 | [plugin/types/](plugin/types/) | The one piece of state the plugin keeps, typed. | 61| 5 | [plugin/tests/](plugin/tests/) | Testing a mod against a fake daemon and a fake host. | 62| 6 | [crates/](crates/) | Three Rust packages, and why there are three. | 63| 7 | [crates/jevhooks-events/](crates/jevhooks-events/) | One vocabulary, compiled into both sides of a socket. | 64| 8 | [crates/jevhooks-events/src/](crates/jevhooks-events/src/) | Thirty-three events and the three things that can happen to one. | 65| 9 | [crates/jevhooks-mod/](crates/jevhooks-mod/) | Rust that becomes WebAssembly that becomes JavaScript. | 66| 10 | [crates/jevhooks-mod/src/](crates/jevhooks-mod/src/) | Routing, and a read-only filter that is wrong in only one direction. | 67| 11 | [crates/jevhooks-daemon/](crates/jevhooks-daemon/) | One daemon for the machine, and how sessions find it. | 68| 12 | [crates/jevhooks-daemon/src/](crates/jevhooks-daemon/src/) | The questions Jev is asked, and how its numbers become a verdict. | 69| 13 | [third-party/](third-party/) | The shared Jev client, borrowed as a submodule. | 70 71## If you know web frameworks 72 73None of this needs Rust or Claude Code experience to follow. Most of the 74parts have a counterpart you already know. 75 76| Here | What it is | In web terms | 77| --- | --- | --- | 78| A hook | Claude Code raises an event (a tool is about to run, a turn is ending) and hands it to every plugin that registered for it. | A request reaching your middleware. | 79| `next(e)` | Runs whatever is beneath this hook: other plugins, the user's own settings hooks. | Express's `next()`, except you get its answer back and can combine it with yours. | 80| The mod | TypeScript that Claude Code runs inside its own process (`plugin/hooks/`). | A middleware module. | 81| The daemon | One long-running local server for the whole machine, on a Unix socket. | A small API server on localhost. | 82| A Unix socket | A file that behaves like a port: only processes on this machine can connect. | `localhost:3000`, but private to the machine. | 83| The MCP server | A process Claude Code starts per session, offering tools the assistant can call. | A per-tab service worker that also boots the API server. | 84| wasm2js | Translates compiled WebAssembly into plain JavaScript. | A transpiler, run on machine code instead of source. | 85| Cargo, crates | Rust's package manager, and its packages. | npm and npm packages; a Cargo workspace is a monorepo workspace. | 86| Nix | Every tool at an exact version, from `nix develop`. | `package.json` and a Node version manager, for every tool at once. | 87 88## The whole story in one picture 89 90Here is what happens between "the assistant wants to run a command" and the 91command running. Every arrow is a chapter somewhere in this guide. 92 93```mermaid 94sequenceDiagram 95 participant C as Claude Code 96 participant M as Mod (plugin/hooks) 97 participant R as Routing (Rust, as JS) 98 participant D as Daemon (jevhooks serve) 99 participant J as Jev 100 C->>M: classic.PreToolUse (Bash: cargo build) 101 M->>R: route(event, tool, command) 102 R-->>M: decide (not plainly read-only) 103 par the mod asks the daemon 104 M->>D: POST /decide, over the Unix socket 105 D->>J: one request, three questions 106 J-->>D: a probability for every option 107 D-->>M: Decision: verdict, line, ms, cost 108 and the hooks beneath run too 109 M->>C: next(e): the user's own hooks 110 end 111 M-->>C: the stricter answer: deny, then ask, then allow 112 C->>M: classic.PostToolUse 113 M-)D: POST /observe (not waited on): it ran 114``` 115 116In words: 117 1181. **The mod hears every hook event** (chapter 3) and asks a few lines of 119 Rust, compiled into JavaScript and running in the same process, what to 120 do with it (chapters 9 and 10). A plainly read-only command, a tool that 121 is not Bash, or an event nobody needs goes no further: no round trip, no 122 question, no cost. 1232. **Anything worth judging goes to the daemon** (chapter 11), one process 124 for every session on the machine, which keeps the connection to Jev warm 125 and the key in one place. 1263. **The daemon asks Jev**, all of a judgment's questions in one request, 127 and turns the typed answers into a verdict with rules written in code, 128 where a test can pin them (chapter 12). 1294. **The mod combines that verdict with the hooks beneath it**, and the 130 stricter one wins. Then it reports what became of the command, so the 131 log can show whether the judgment was any good. 132 133> **Aside.** Why not let the assistant police itself? Because it is the 134> thing being checked. A model that writes can be argued with, by the very 135> text it is about to act on; Jev can only pick from options written in 136> `judge.rs`, so the worst a cleverly worded command can do is move a 137> probability. And it is cheap enough to ask every time: a command's three 138> questions come to about 1,700 input tokens, which at Jev's published 139> price of $0.042 per million is under a hundredth of a cent. 140 141## Running it yourself 142 143Anyone who clones it can read every line and run the plugin's tests (the 144Try it above). Building the Rust needs one thing an outside reader cannot 145fetch yet: the daemon depends on a private fork of rmcp, the Rust MCP 146library, at `ssh://git@github.com/deizel/rust-sdk.git` (the 147`[patch.crates-io]` entry in [Cargo.toml](Cargo.toml), and chapter 6 says 148why). Cargo resolves the whole workspace before building any of it, so 149without that fork every `cargo` command stops at `failed to load source for 150dependency rmcp`, even for a crate that does not use it. 151 152With access to the fork: 153 154 nix develop # stable Rust with the wasm32 target, binaryen, node, just 155 just check # Rust tests, the mod's JavaScript, the daemon, plugin validation 156 claude --plugin-dir ./plugin # a session with jevhooks loaded 157 158In a session, a band above the prompt shows the last decision, how long it 159took and what the session's questions have cost; `/jev` prints the daemon's 160status and the latest decisions. Outside one: `plugin/bin/jevhooks status`, 161`recent [count]` and `stop`. 162 163The daemon needs a TypeSafe API key in `TYPESAFE_API_KEY`. Without one it 164runs, judges nothing, and every verdict is "pass" (chapter 11 has where the 165key comes from, the files the daemon keeps, and its one setting). 166 167## Files at the top 168 169| File | What | 170| --- | --- | 171| [Cargo.toml](Cargo.toml) | The Cargo workspace: the three crates, every dependency's version, the release profile, and the rmcp patch. | 172| [Cargo.lock](Cargo.lock) | Every dependency at the exact version built. | 173| [flake.nix](flake.nix) | The devshell, for x86_64-linux: stable Rust with the wasm32 target, binaryen (`wasm-opt`, `wasm2js`), node and just. | 174| [flake.lock](flake.lock) | The flake's inputs, pinned. | 175| [justfile](justfile) | `just test`, `just mod` (chapter 9), `just daemon` (chapter 11), `just check` (all three, then `claude plugin validate plugin`). | 176| [.gitmodules](.gitmodules) | The jevcrates submodule, by a relative URL (chapter 13). | 177| [.gitignore](.gitignore) | Build output, the built daemon in `plugin/bin/`, and the type files Claude Code lays in `plugin/.claude-plugin/types/`. | 178 179Next: [Chapter 1, plugin/](plugin/) →