mroberts 129354cda8 Read hook payload from fd 0, not /dev/stdin
Claude Code delivers the hook payload on a socket. Opening it by path
('/dev/stdin' -> /proc/self/fd/0) fails with ENXIO, so bash-guard.mjs threw on
every invocation and its bare catch exited 0 silently. The guard looked like it
was never dispatched; it was dying on line 29 each time.

readFileSync(0) is read() on the descriptor with no open(), which works on a
socket. The catch now logs instead of swallowing, so this failure mode can never
again masquerade as non-dispatch.

Adds test-bash-guard.py, which drives the guard over a socketpair. A pipe would
not reproduce the bug, so the socket is load-bearing. Verified failing against
the pre-fix guard (silent, no output) and passing after.

Removes the five diagnostic probes and probe.mjs; they served their purpose.
Bumps to 1.0.2 because the plugin cache is keyed by version and an unchanged
version silently skips reinstall.
2026-07-21 10:24:06 -05:00

mroberts — Claude Code plugin marketplace

Personal marketplace hosting multiple Claude Code plugins.

Install

claude plugin marketplace add ssh://[email protected]/mroberts/claude-plugin.git
claude plugin install guards@mroberts

Layout

.claude-plugin/marketplace.json   # index — every plugin must be listed here
plugins/
  guards/
    .claude-plugin/plugin.json
    hooks/hooks.json
    hooks/bash-guard.mjs

Adding a plugin

  1. mkdir -p plugins/<name>/.claude-plugin
  2. Write plugins/<name>/.claude-plugin/plugin.json with name, version, description.
  3. Add components: hooks/hooks.json, commands/, agents/, skills/ as needed.
  4. Append an entry to plugins[] in .claude-plugin/marketplace.json:
{ "name": "<name>", "source": "./plugins/<name>", "description": "...", "category": "productivity" }

A plugin absent from plugins[] is not installable, regardless of its directory.

  1. Push, then claude plugin marketplace update mroberts.

Plugins

Plugin Purpose
guards Enforces the hard constraints in ~/.claude/CLAUDE.md — jj-only VCS, no Claude attribution, build+test gate before push/tag, secret scrubbing on writes.

Note on guards

hooks/bash-guard.mjs is referenced via ${CLAUDE_PLUGIN_ROOT} and travels with the plugin. shush is referenced by absolute path (/home/mroberts/.local/bin/shush) because it is a separately installed binary, not a repo file — that path is machine-specific and will need changing on another host.

S
Description
My personal claude plugins
Readme
396 KiB
Languages
Python 91.6%
JavaScript 5.6%
Shell 2.8%