jevhooks.git / CLAUDE.md

For agents, on top of README.md, which they read first.

Notes for agents

Every folder has its own CLAUDE.md with the invariants of the code in it: the questions and verdicts in crates/jevhooks-daemon/src/, the read-only filter in crates/jevhooks-mod/src/, the mod's memory, $ calls and hook layering in plugin/hooks/, running the plugin tests in plugin/tests/. What follows holds everywhere.

Never make Jev's absence block anything. No key, a throttle, a timeout, an unreachable daemon: all are Pass. The mod has its own 2.5 s limit on the daemon for the same reason.

Run just mod after any change to crates/jevhooks-mod or jevhooks-events, and just daemon after any change to the daemon. The plugin loads plugin/hooks/jevhooks.js (wasm2js output, never edited by hand) and starts plugin/bin/jevhooks (a copy, not a link into target/). A session started after a rebuild replaces the running daemon by itself; to do it by hand, plugin/bin/jevhooks stop and start a session, or op-env-run -- plugin/bin/jevhooks serve.

Never remove the [patch.crates-io] rmcp entry because "3.5.0 is the latest". rmcp 3.5.0 refuses every request after Claude Code's server/discover then initialize opening, so the MCP server connects and lists no tools. The fix is on the private deizel/rust-sdk fork (commit 1c69053, on top of the rmcp-v3.5.0 tag). Before dropping it, confirm in a session that the status tool is listed.

Assume nothing about the machine. This is a plugin for anyone's machine, not for the one it was written on: no host-specific helpers, no WSL, no systemd, no particular host in the daemon or the mod. Where a figure is only knowable on some systems (available memory is MemAvailable on Linux and nothing built in elsewhere), the default is the generic one, a config key lets the user supply a command, and the absence of both is a Pass, never a guess. A configured command is the only source once set: do not fall back to the built-in figure when it fails, because the user set it to say the built-in figure is wrong there.

The key is never written anywhere. Not in .mcp.json's env, an .envrc or a file, and never printed; status says whether it arrived.

Build in the devshell, and check memory first. Crates' build scripts link with a host cc even when the target is wasm32; mkShell supplies it. A release build of the daemon uses several gigabytes: free -m before starting one.

The docs are a guide, chapter by chapter. Every folder with tracked files has a README that is a chapter (# Chapter N: <name>, <subtitle>, an opening that teaches, > **Aside.** detours, a > **Try it.**, then "For the people who maintain it" with a file map) and a CLAUDE.md beside it starting @README.md. The last line of each README is ← Previous: … · Up: … · Next: … →, and the chain is a depth-first preorder of the folders in the order the root's table lists them; the lmjtfy site's README viewer follows those lines. A new folder gets its chapter, and the chapters after it are renumbered. Playful, never at the expense of a true fact: read the code before describing it, and keep every relative link pointing at a tracked path.

The README must not claim a build works for outsiders. The rmcp patch is fetched over SSH from a private repository, and Cargo resolves the whole workspace before building any member, so without access every cargo command fails (measured 2026-10-02 with a fresh CARGO_HOME and SSH disabled). claude plugin test plugin needs no Cargo and is the thing an outside reader can run.