Repository navigation
Conversation
…as a skill
A Claude Code plugin install registered no hooks: .claude-plugin/plugin.json
did not declare hooks/claude.hooks.json, and Claude Code only discovers
hooks/hooks.json by itself. Once declared, the gate could never open, because
the routers are skills that Claude Code runs inline through the Skill tool, so
SubagentStop never fires for the lead, and a finished subagent is named
agent_type, which mark did not read.
- Declare the hooks file in .claude-plugin/plugin.json.
- Run mark on PostToolUse for Skill, as Copilot's postToolUse already does,
and read agent_type.
- finalize honours stop_hook_active, emits a stop decision with the reason on
stderr too, and no longer says a file was edited when the gate refused it.
- Context names the event that fired; ${CLAUDE_PLUGIN_ROOT} is quoted; the
$comment key Claude Code warns about at every session start is removed.
hooks/guard.test.mjs goes from 22 to 30 tests.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes the hooks part of #219. The MCP server, root
CLAUDE.mdand repository-relative path items are left for follow-ups, because each needs a decision from you.What was wrong
From a Claude Code plugin install, the enforcement gate did nothing, and once loaded it could never open:
.claude-plugin/plugin.jsondidn't declarehooks/claude.hooks.json, and Claude Code only discovershooks/hooks.jsonby itself.SubagentStopnever fires for the lead. When a subagent does finish, Claude Code names itagent_type, whichmarkdidn't read.finalizeignoredstop_hook_active, so it could keep refusing a stop it had already refused.What changed
.claude-plugin/plugin.jsondeclares"hooks": "./hooks/claude.hooks.json".hooks/claude.hooks.jsonmarkalso runs onPostToolUsewith matcherSkill. That's the same approach Copilot'spostToolUsealready takes, andsubagentName()already readtool_input.skill.${CLAUDE_PLUGIN_ROOT}is quoted in every command, which clears theclaude plugin validatewarnings.$commentkey is removed, because Claude Code reports it as unknown at every session start.hooks/guard.mjssubagentName()readsagent_type.finalizereturns 0 whenstop_hook_activeis set, and emits{decision, reason}rather thanPreToolUseoutput, with the reason also on stderr.finalizemessage no longer says a file "was edited". The marker is set when the gate refuses an edit.emitContextlabels context with the event that fired, sopersistonSessionStartis no longer reported asUserPromptSubmit..claude/settings.jsongets the samePostToolUseentry, so repository development still mirrors the shipped manifest.docs/standards/hooks-by-client.mdanddocs/reference/hooks.mddescribe the Claude Code mark events and the manifest declaration, with test counts updated.CHANGELOG.mdhas an Unreleased entry.Design note
guard.mjsopens the gate when the lead completes, not when it launches. An inline skill has no completion event in Claude Code.PostToolUsefor Skill fires when the skill's instructions load. That's the same trade-off the Copilot manifest already makes. The issue describes an alternative: marking whenrender-report.mjsruns. I'll switch to it if you prefer.Tests
hooks/guard.test.mjsgoes from 22 to 30 tests. The new tests cover:agent_typeonSubagentStopmarkrunning on both eventsstop_hook_activenot loopingSessionStartcontext naming its eventSix of the eight new tests fail on
main, and so does the extended persist test. The two Skill tool tests pass onmainbecause the script already readtool_input.skill. What was missing was the manifest entry, which the manifest tests catch.npm run verifyreports the same five failing gates on this branch as onmainin my environment. All five come from dependencies I couldn't install from my sandbox:skills-ref, the MCP SDK, npm advisories, andverify:live. None of them involve the hooks.claude plugin validate(2.1.283) passes. Its only remaining warning is the rootCLAUDE.mdone described in the issue.Live check
I ran Claude Code 2.1.293 with
claude --plugin-dir <this branch>from an empty folder holding a fixture page:/hookslisted the seven plugin hooks.<h1>edit was refused before review.accessibility-agents:accessibility-leadopened the gate, five specialists reported, and the same edit went through.The
$commentremoval and the reworded Stop message came out of that run.Source verification
hooks/hooks.json, and other files are declared with ahookskey, which must wrap its events in"hooks".Source: https://code.claude.com/docs/en/plugins/manifest-reference
Verified on: 2026-10-15
SubagentStopinput carriesagent_typeandstop_hook_active; Stop is blocked with{"decision": "block", "reason"}or exit 2 with stderr;stop_hook_activeshould be checked to avoid looping;hookSpecificOutput.hookEventNameis the event name.Source: https://code.claude.com/docs/en/hooks
Verified on: 2026-10-15