Skip to content

feat(cli): resolve hook registration at install, and on demand - #100

Merged
MajorLift merged 1 commit into
jongsun/ci/ship-hooks-and-trigger-checkfrom
jongsun/feat/hook-registration
Jul 31, 2026
Merged

feat(cli): resolve hook registration at install, and on demand#100
MajorLift merged 1 commit into
jongsun/ci/ship-hooks-and-trigger-checkfrom
jongsun/feat/hook-registration

Conversation

@MajorLift

Copy link
Copy Markdown
Contributor

Stacked on #99, which is what makes hooks/ reach a consumer at all. Base is #99's branch, not main — review that one first.

The gap

Copying a hook does not activate it. Claude Code runs one only once it is registered in settings.json, and the path to register is absolute — different per machine, per consumer repo, and per mms- prefixed skill directory. So the setup reference could only ever say:

python3 /absolute/path/to/evidence/hooks/pr-evidence-gate.py

and leave the reader to work out what that is. Meanwhile evidence cites the hook twice in its body as its enforcement mechanism, so someone installing it reasonably assumes the gate is live. It isn't.

Two surfaces, one output

At install — when any installed skill ships a hook, tools/install prints the registration with every path resolved against the actual install:

Note: 1 skill(s) ship a hook. Copying the file does not activate it —
Claude Code runs a hook only once it is registered in settings.json.

Add this to ~/.claude/settings.json (or <target>/.claude/settings.json):

  {
    "hooks": {
      "PreToolUse": [
        {
          "matcher": "Bash",
          "hooks": [
            { "type": "command", "command": "python3 <target>/.claude/skills/mms-gatekeeper/hooks/evidence-gate.py" }
          ]
        }
      ]
    }
  }

On demandmetamask-skills hooks [--target <path>] prints the same thing, for anyone who scrolled past it or is re-registering later. Says "No installed skill ships a hook" rather than printing empty JSON, and exits non-zero only when the target has no installed skills at all.

What it deliberately does not do

Neither surface writes to settings.json. Editing a user's operator config is a materially larger permission than "copy files into the repo you pointed me at", and it should be decided deliberately rather than inherited as a side effect of shipping one hook. This is also the only hook in the corpus — a sample of one is thin evidence for automating a write to $HOME.

Test plan

  • yarn test — 69 pass / 0 fail across three files
  • Fixture skill with hooks/evidence-gate.py: file delivered, registration printed, path resolved
  • Printed registration parses as JSON — asserted by JSON.parse, matcher and command checked
  • metamask-skills hooks emits the same registration
  • Target with skills but no hooks → message, exit 0
  • Target with no installed skills → warning, exit 1

Note

An earlier version emitted a trailing comma and told the reader to delete it. Handing someone JSON that doesn't parse is worse than handing them none, so the entries are joined properly and the output is valid as printed.

Copying a hook does not activate it. Claude Code runs one only once it is
registered in settings.json, and the path to register is absolute — different
per machine and per consumer repo — so the setup doc could only ever say
`/absolute/path/to/...` and leave the reader to work it out.

`tools/install` now prints the registration when any installed skill ships a
hook, with every path resolved against the actual install. `metamask-skills
hooks` prints the same thing on demand, for anyone who scrolled past it or is
re-registering later.

The output is valid JSON as printed. An earlier version emitted a trailing comma
and told the reader to remove it, which is a worse thing to hand someone than no
JSON at all.

Neither surface writes to settings.json. Editing a user's operator config is a
larger permission than "copy files into the repo you pointed me at", and worth
deciding deliberately rather than inheriting as a side effect of shipping one
hook.
@MajorLift
MajorLift merged commit c579591 into jongsun/ci/ship-hooks-and-trigger-check Jul 31, 2026
28 checks passed
@MajorLift
MajorLift deleted the jongsun/feat/hook-registration branch July 31, 2026 15:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant