Skip to content

Fix six open upstream issues in the hookify and plugin-dev plugins - #4

Merged
adri22235 merged 20 commits into
mainfrom
fix/issues-batch
Oct 2, 2026
Merged

adri22235 merged 20 commits into
mainfrom
fix/issues-batch

Conversation

@adri22235

@adri22235 adri22235 commented Oct 2, 2026 •

Copy link
Copy Markdown
Owner

Summary

Fixes bugs reported as open issues in anthropics/claude-code that live in files of this repo (the bundled hookify and plugin-dev plugins). Each issue was reproduced first; one commit per issue plus a test commit.

Upstream issue Bug Fix
93028 A frontmatter value containing --- (a regex like a---b) was cut there and the rest moved into the message The closing delimiter is now a line holding only ---
89026 Rule files and transcripts opened with the locale's encoding, so rules with non-ASCII text were skipped on Windows Read as UTF-8; rule files also accept a BOM
81448 No module named 'hookify' on every hook: the plugin cache names the directory by version, not hookify Register the plugin root as the hookify package instead of relying on the parent being on sys.path
78490 An install path with a space split the unquoted ${CLAUDE_PLUGIN_ROOT} in hooks.json Quoted, for hookify's four hooks
79143 The bundled examples lacked the hookify. prefix, so copying one as-is never loaded Renamed, and the writing-rules skill says so
92798 parse-frontmatter.sh treated text after a --- in the body as frontmatter Reads only the leading block; CRLF tolerated
98839 (mitigation) If the plugin directory moves under a running session, python3 cannot open the script and exits 2, which blocks every prompt and tool call Each hook command checks the script exists; if not, it prints the path to stderr and exits 1, a visible non-blocking error

The 98839 change only protects hookify. The root cause in that issue (a Cowork session keeping stale hook paths after an org plugin re-syncs, and exit 2 from a missing file being treated as a block) is in Claude Code itself. The hookify scripts only ever exit 0, so no intended block is lost.

Reproduced but not fixed: upstream issue 84745 (PreToolUse does not filter undefined events). I could not make a rule of another event fire on a Read call, so there was no failure to fix. Other hookify issues such as 20747 (warnings do not reach the model) are about how Claude Code treats systemMessage and are not changeable from this repo.

Not touched: the unquoted ${CLAUDE_PLUGIN_ROOT} in the ralph-wiggum and the two output-style hooks.json files (issue 78490 covers them too). The first PR in this fork already changes those three, so they are left out here to avoid a conflict.

Overlap with open upstream PRs: 81672 and 81670 fix the same import, quoting and example-name issues. 81672 puts the package registration in a shared _bootstrap.py, a cleaner layout than the per-hook block here.

Test plan

  • python3 -m unittest discover -s plugins/hookify/tests -p 'test_*.py': 15 pass. Against the code before the fixes the same tests fail, so each one catches its bug (the missing-script test fails for all four hooks on the previous hooks.json)
  • plugins/plugin-dev/skills/plugin-settings/scripts/parse-frontmatter.test.sh: 6 pass (5 fail before)
  • The original reproduction of each issue now behaves correctly, including running the real pretooluse.py from cache/hookify/1.0.0 and from a path with a space, and the UserPromptSubmit command with its plugin directory gone (exit 1 under sh and bash, was 2)
  • hooks.json still valid JSON
  • Not tested on Windows or macOS; the encoding case is simulated with PYTHONUTF8=0 LC_ALL=C
  • validate-hook-schema.sh reports an error on this hooks.json, as it does on the original: it does not understand the wrapped {"hooks": {...}} format (fixed by the first PR in this fork)

🤖 Generated with Claude Code

https://claude.ai/code/session_01LoLAv3aLaRJocsRMcjFbpv

genesisdayabl-droid and others added 19 commits October 2, 2026 15:30
…ample.py

The example script pointed to the old docs.anthropic.com domain while
every other reference in the repo uses code.claude.com/docs/en/...
Eight bundled skills declare a title-cased `name` with spaces, e.g.

    name: Skill Development     # plugins/plugin-dev/skills/skill-development/
    name: MCP Integration       # plugins/plugin-dev/skills/mcp-integration/

The Agent Skills specification requires the `name` field to contain only
lowercase alphanumerics and hyphens, and to match the parent directory
name. Both constraints are violated by all eight, so `skills-ref validate`
rejects them.

The two skills outside plugin-dev/hookify already follow the spec —
`claude-opus-4-5-migration` and `frontend-design` — so this changes the
eight outliers to the convention the repository already uses elsewhere.

Only the frontmatter `name` field is touched. The prose headings in
plugins/plugin-dev/README.md and the `#` titles inside each SKILL.md keep
their human-readable capitalization, since neither is a skill identifier.

Spec: https://agentskills.io/specification#name-field
…) and stop false-flagging valid agents

Two defects made the validator fail on plugin-dev's own agent files
(anthropics#83803):

1. Under `set -e`, `((warning_count++))` / `((error_count++))` return a
   nonzero status when the counter was 0, so the script died at the first
   warning or error instead of finishing the run. Increments now use
   `count=$((count + 1))`, which always returns 0.

2. Field extractions like `TOOLS=$(... | grep '^tools:' ...)` aborted the
   script under `set -e` when the field was absent (grep exits 1 on no
   match), instead of reporting the missing field. They now end in
   `|| true`.

3. The description check only read the first physical line of the
   `description:` value, so multi-line descriptions with <example> blocks
   (as in plugin-dev's own agents) were false-flagged as missing examples.
   The extraction now captures the full multi-line value.

Adds validate-agent.test.sh: plugin-dev's own agents must exit 0, a
warning-only file must complete with exit 0, and an invalid file must
still exit 1 with all errors reported.

No-Verification-Needed: standalone shell script in the public repo; driven end-to-end directly plus new regression harness
Reuse the existing _DOC_EXTS path filter for the four XSS-family substring rules so documentation examples do not emit security warnings.

Add regression coverage for every documentation extension while keeping the existing executable-source detections intact.
Every agent's description was a single unquoted scalar containing
dialogue lines like Daisy: "..." / Assistant: "...", which YAML
parses as an illegal nested mapping. That leaves the agent loading
with empty frontmatter (name/description/model/color all silently
dropped), so it can't be selected by description-based routing.

silent-failure-hunter.md was reported (anthropics#86748); the same defect was
present in the other five agents in this plugin, so all six are
converted to a `description: |` block scalar. Body content and
description text are unchanged, only the frontmatter encoding.

Fixes anthropics#86748

Co-Authored-By: Claude Sonnet 5 <[email protected]>
…h with a quoted path

The Stop hook in ralph-wiggum and the SessionStart hooks in
learning-output-style and explanatory-output-style gave a bare, unquoted
"${CLAUDE_PLUGIN_ROOT}/....sh" as the hook command. Shell-form hook
commands are expanded by the shell from the CLAUDE_PLUGIN_ROOT env var,
so a plugin root containing a space word-splits and the hook exits 127
("not found") with no visible effect; the bare form also relies on the
script's exec bit and, on Windows, on the CLI prepending an interpreter.

Invoke the scripts as `bash "${CLAUDE_PLUGIN_ROOT}/....sh"`, the form
security-guidance already uses, and bump the three plugins to 1.0.1 so
existing installs pick the change up on update (the plugin.json version
is what the updater compares).

Refs anthropics#95673, anthropics#78490.
…es in plugin scripts

Applied from anthropics#84711 without the ralph-wiggum stop-hook.sh
change (rejecting any transcript path containing '..' or that is a symlink
would end the loop for legitimate setups).
… reach; mask credential values in review feedback and session state

Applied from anthropics#96434 (both commits, squashed). Unit tests
in plugins/security-guidance/tests pass.
…es as UTF-8

A rule whose frontmatter value contained '---' (a regex such as a---b) was cut
at that point and the rest moved into the message, because the file was split
on the bare string. The closing delimiter is now a line holding only '---'.

Rule files and transcripts were opened with the locale's encoding, so on
Windows (cp1252) a rule with non-ASCII text was skipped with a decode error.
They are read as UTF-8; rule files also accept a BOM.

Addresses upstream issues 93028 and 89026.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01LoLAv3aLaRJocsRMcjFbpv
… called

The hooks imported 'hookify.core...' after putting the plugin's parent on
sys.path, which only works when the install directory is named 'hookify'. The
plugin cache names it by version, so every hook failed with 'No module named
hookify' and hookify did nothing. Register the plugin root as the package's
search path instead; fall back to the script's own location when
CLAUDE_PLUGIN_ROOT is unset.

Addresses upstream issue 81448.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01LoLAv3aLaRJocsRMcjFbpv
An install path with a space (macOS user folders, for one) split the unquoted
${CLAUDE_PLUGIN_ROOT} into two arguments, so every hook failed to start.

Addresses upstream issue 78490 for hookify. The three plugins that run a script
directly (ralph-wiggum and the two output styles) are not touched here.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01LoLAv3aLaRJocsRMcjFbpv
Only .claude/hookify.*.local.md files are read, but the examples shipped
without the 'hookify.' prefix, so copying one as-is silently did nothing.
Rename them and say so in the writing-rules skill.

Addresses upstream issue 79143.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01LoLAv3aLaRJocsRMcjFbpv
…ter block

The sed range reopened at every later '---' line, so a horizontal rule in the
body made the text after it part of the frontmatter. Read from the opening
'---' on the first line to the next '---' line, and tolerate CRLF.

Addresses upstream issue 92798.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01LoLAv3aLaRJocsRMcjFbpv
Run the hook scripts from an installed copy in a directory not named hookify,
under a path with a space, and under a non-UTF-8 default encoding. Each test
fails on the code before the fixes.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01LoLAv3aLaRJocsRMcjFbpv
Each hook ran 'python3 <script>'. When the script cannot be opened, python3
exits 2, and a hook that exits 2 blocks: every prompt (UserPromptSubmit) and
every tool call (PreToolUse) is refused, with no way out from inside the
session. This happens when the plugin directory moves under a running session,
as with an org-synced plugin re-synced to a new path.

The commands now check that the script exists; if not, they print the path to
stderr and exit 1, a non-blocking error Claude Code shows instead of a block.
The hookify scripts themselves only ever exit 0, so no intended block is lost.

Mitigates upstream issue 98839 for hookify. The root cause there, stale hook
paths in a live Cowork session and exit 2 from a missing file being treated as
a block, is in Claude Code itself.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01LoLAv3aLaRJocsRMcjFbpv
@adri22235 adri22235 changed the title Fix five open upstream issues in the hookify and plugin-dev plugins Fix six open upstream issues in the hookify and plugin-dev plugins Oct 2, 2026
@adri22235
adri22235 marked this pull request as ready for review October 2, 2026 16:14
Resolves the conflict in parse-frontmatter.sh: keep the field-name validation
from page 1 (upstream PR 84711) and this branch's extraction of the leading
frontmatter block.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01LoLAv3aLaRJocsRMcjFbpv
@adri22235
adri22235 merged commit 8284b12 into main Oct 2, 2026
1 check passed
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.