Summary
SKILL.md:13 registers the PreToolUse hook with:
args: ["${CLAUDE_PLUGIN_ROOT}/skills/clean/scripts/destructive_guard.py",
"--authorized-data-root", "${CLAUDE_PLUGIN_DATA}",
"--disk-hygiene-enabled", "${user_config.disk_hygiene_enabled}"]
The 0.4.1 fix (#376) assumed "inline placeholder substitution resolves in exec-form hook args where
environment injection does not." That premise is now false for skill-scoped hooks.
Empirical reproduction (live, this session, plugin version 0.4.6)
PreToolUse:Bash hook error
Failed to run: Hook command references ${CLAUDE_PLUGIN_DATA} but only ${CLAUDE_PLUGIN_ROOT} is
available for skill hooks (${CLAUDE_PLUGIN_DATA} is plugin-only). Command: python3 ${CLAUDE_PLUGIN_ROOT}/…
Claude Code refuses to launch the hook process at all when its args reference ${CLAUDE_PLUGIN_DATA}
in a skill-scoped context. Per the plugin's own documented doctrine, "Claude Code treats a failed hook
launch as a non-blocking error" — so the destructive-action guard never runs. Confirmed live: a bare
python hygiene.py scan ... call executed completely ungated, no denial, no substitution — the guard
simply never fired.
Why this isn't just a doc bug
This is the same fail-open shape #380 fixed (interpreter resolution) recurring through a different
vector (hook launch failure via an unsupported substitution token). It also directly relates to what
#386/D2 already flags as a false premise in the docs — but #386 frames that as a doc-accuracy
issue. It undersells it: the doc isn't just wrong, the mechanism it describes doesn't work at all, and
the guard is currently non-functional on skill-frontmatter hooks as a result.
Suggested direction (not prescriptive — needs its own research pass)
Related
#376, #380, #386
Summary
SKILL.md:13registers the PreToolUse hook with:The 0.4.1 fix (#376) assumed "inline placeholder substitution resolves in exec-form hook
argswhereenvironment injection does not." That premise is now false for skill-scoped hooks.
Empirical reproduction (live, this session, plugin version 0.4.6)
Claude Code refuses to launch the hook process at all when its args reference
${CLAUDE_PLUGIN_DATA}in a skill-scoped context. Per the plugin's own documented doctrine, "Claude Code treats a failed hook
launch as a non-blocking error" — so the destructive-action guard never runs. Confirmed live: a bare
python hygiene.py scan ...call executed completely ungated, no denial, no substitution — the guardsimply never fired.
Why this isn't just a doc bug
This is the same fail-open shape #380 fixed (interpreter resolution) recurring through a different
vector (hook launch failure via an unsupported substitution token). It also directly relates to what
#386/D2 already flags as a false premise in the docs — but #386 frames that as a doc-accuracy
issue. It undersells it: the doc isn't just wrong, the mechanism it describes doesn't work at all, and
the guard is currently non-functional on skill-frontmatter hooks as a result.
Suggested direction (not prescriptive — needs its own research pass)
args(confirmed:${CLAUDE_PLUGIN_ROOT}works). IfCLAUDE_PLUGIN_DATAtruly cannot reach a skill hook by anychannel (arg substitution or env), the guard needs a different authority source — e.g. derive the
data root deterministically from
${CLAUDE_PLUGIN_ROOT}if the data-root path is a fixed siblingunder
.claude/plugins/data/<plugin-name>/, or petition the Claude Code runtime to add data-rootsubstitution support for skill hooks (may be a platform gap, not something the plugin alone can fix).
destructive_guard.py's internal arg-parsing once launched — that's the part currently untested andcurrently broken.
live-exploitable rather than doc-only.
Related
#376, #380, #386