jevhooks.git / tools / README.md
1# Chapter 14: tools, three scripts and one rule about versions
2
3A repository collects commands the way a kitchen drawer collects takeaway
4menus: one in the README, a slightly different one in a git hook, a third in
5somebody's shell history. jevhooks keeps them in one place instead. Every
6job has a name in [mise.toml](../mise.toml), and the README, the git hooks
7and you all call it by that name: `mise run check`, `mise run mod`,
8`mise run daemon`. [mise](https://mise.jdx.dev) installs the tools those
9tasks need as well, at the versions written in the same file, for one user
10and without root.
11
12Most tasks are a line or two and live in `mise.toml` itself. The three
13that needed an `if` are scripts, here.
14
15- **[with-secrets.sh](with-secrets.sh)** runs a command with Jev's key in
16  its environment. The key is *declared* in [fnox.toml](../fnox.toml), with
17  no value and no provider. If you have [fnox](https://fnox.jdx.dev) set up
18  to supply `TYPESAFE_API_KEY` (from age, from a password manager), the
19  command runs under `fnox exec`; if not, it simply runs, and the key must
20  already be in the environment. Either way nothing reads a secret from a
21  file in this repository, because none is ever written to one.
22- **[check-versions.sh](check-versions.sh)** fails when a version written
23  somewhere other than `mise.toml` disagrees with it. There is one such
24  place today: [hk.pkl](../hk.pkl) has to name the hk release it was
25  written for. It also fails if `flake.nix` starts naming a toolchain.
26- **[screenshots.sh](screenshots.sh)** takes the pictures in this guide,
27  and nobody draws them. It starts a real Claude Code session in a
28  terminal you never see (a detached [tmux](https://github.com/tmux/tmux)),
29  with the plugin loaded and a daemon of its own. It types a prompt, waits
30  for a particular line to appear on the screen, and hands the screen,
31  colours and all, to [freeze](https://github.com/charmbracelet/freeze),
32  which writes it out as an SVG. Four scenes, four pictures.
33
34> **Aside: how do you photograph a wait?** The picture of a command
35> waiting for memory needs a machine that is short of memory, on cue. The
36> script does not go and fill your RAM. Its daemon is configured with
37> `available_memory_command = "cat <a file>"` (chapter 11), and the
38> script writes `1000` into the file before the scene and `64000` after
39> it. The daemon believes the file, the build waits, the shutter clicks,
40> and the build is let through.
41
42> **Aside: why the pictures are SVG.** A screenshot as pixels is drawn
43> with one font, and the first version of these had little boxes where
44> Claude Code's symbols should be, because that font did not have them.
45> An SVG keeps the screen as text and names a list of fonts, so your
46> browser takes each character from the first font that has it. The
47> files are also about 4 KB each, and you can select the text.
48
49> **Aside: why a version check at all?** Because the alternative is a
50> comment saying "remember to change both". The hooks file cannot read
51> `mise.toml`, so the number is written twice, and a number written twice
52> is two numbers the moment someone updates one. The check turns that
53> into a failed commit instead of a puzzling afternoon.
54
55> **Aside: where is nix?** `flake.nix` is still at the top, for a machine
56> that uses it: its shell holds mise itself and a C compiler, which is the
57> one thing a downloaded toolchain does not bring (Rust build scripts link
58> with it, even for wasm). It installs nothing else and pins nothing else.
59
60> **Try it.** `mise tasks` prints every task with its description. Then
61> `mise run check-versions` (it needs no Rust), and, to watch
62> it earn its keep, change `hk = "2.5.0"` in `mise.toml` to another number
63> and run it again.
64
65## For the people who maintain it
66
67| Task | What it runs |
68| --- | --- |
69| `submodules` | `git submodule update --init --recursive` |
70| `hooks:install` | `hk install`: the hooks in `hk.pkl` |
71| `mod` | cargo for wasm32, `wasm-opt -Oz`, `wasm2js -O2`, into `plugin/hooks/jevhooks.js` (chapter 9) |
72| `daemon` | a release build of `jevhooks`, copied into `plugin/bin/` (chapter 11) |
73| `test` | `cargo test --workspace` |
74| `test:live` | The one test that asks the real Jev, through `tools/with-secrets.sh` (needs the key and the network) |
75| `clippy` | `cargo clippy --workspace` |
76| `check-versions` | `tools/check-versions.sh` |
77| `screenshots` | `tools/screenshots.sh` through `tools/with-secrets.sh`: redraws the four pictures from a real session (needs the key, a signed-in `claude`, a built daemon; a few cents) |
78| `plugin:validate`, `plugin:test` | `claude plugin validate plugin`, `claude plugin test plugin` (`CLAUDE` names another binary) |
79| `check` | `check-versions`, `test`, `mod`, `daemon`, then `plugin:validate` |
80| `serve` | `tools/with-secrets.sh plugin/bin/jevhooks serve` |
81| `status` | `plugin/bin/jevhooks status` |
82
83### In this folder
84
85| Path | What |
86| --- | --- |
87| [with-secrets.sh](with-secrets.sh) | Runs its arguments under `fnox exec` when fnox resolves the key, plainly otherwise. `JEVHOOKS_NO_FNOX=1` forces the plain path. |
88| [check-versions.sh](check-versions.sh) | POSIX tools only; reads `mise.toml`'s `key = "value"` lines. |
89| [screenshots.sh](screenshots.sh) | The four scenes, in the order they are taken; each `wait_for` fails the run, and prints the screen, when its line never appears. Writes `plugin/hooks/band.svg`, `plugin/hooks/ask.svg`, `crates/jevhooks-daemon/src/waiting.svg` and `crates/jevhooks-daemon/src/model.svg`. |
90
91← Previous: [Chapter 13, third-party/](../third-party/) · Up: [jevhooks](../)