Skip to content

v1.3.0 — the hook says when it could not check a file

Latest

Choose a tag to compare

@yottayoshida yottayoshida released this 18 Sep 00:08
· 1 commit to main since this release
751ef60

Summary: The PostToolUse hook was silent in exactly the same way for a clean file and for a file it could not check, so a broken hook read as a healthy one from outside (#209). It now says so — one line, shown to the user and not to Claude, the edit never blocked — and a clean file still writes nothing.

Added

  • The PostToolUse hook now says when it could not check a file. A file over 2 MiB, one it could not read, a binary file, or a bundled catalog that loaded no guides used to produce exactly what a clean file produces — nothing on stdout, nothing on stderr, exit 0 — and a path it could not reach or a detector raising ended in a traceback of which Claude Code shows only the first line; either way a broken hook and a healthy one were indistinguishable from outside, and an installation could stay that way for months with nobody the wiser. The hook now writes a one-line systemMessage, the field Claude Code shows to the user and not to Claude, naming the reason; the edit is never blocked, the exit status stays 0, and a clean file still writes nothing. Stderr was never an option: on exit 0 Claude Code sends it to the debug log only. Exiting non-zero was rejected because the transcript renders that as hook error … Failed with non-blocking status code:, which reads as a broken hook rather than an unchecked file, and additionalContext was rejected because the reader is the person, not Claude. Two things moved with it. Path.is_file(), which decided whether a path was worth checking, raised on a parent directory the hook cannot enter on 3.11-3.13 — a traceback, of which Claude Code shows only the first line — and answered False on 3.14, which is silence; the decision is now made with stat(), where only "nothing there" (ENOENT, ENOTDIR, and a name no filesystem can hold) stays silent, so a symlink loop now says so too. And the catalog load moved inside the same guard, which now also refuses an empty catalog — build_index() never raises; a missing or hollow guides directory is logged and comes back empty, and an empty catalog finds nothing in any file, which is the one shape of a broken install that reads exactly like a clean file — with the guard widened to any exception, since a hook must never break an edit and a detector's traceback told the user nothing; the message keeps a foreign exception's type. It passes through the same escaper doctor writes through since #245, and the JSON stays ASCII, so a path or an OS message carrying a newline or an escape sequence cannot add a line or redraw one. mpg check <file> is named in the message only for the failures it reproduces — too large, unreadable, binary — because for the others check ends in a traceback of its own. There is no per-file switch: the hook says so on every edit, and mpg setup --no-hook removes it altogether. (closes #209)