Skip to content

PostToolUse hook #23

Description

@Forenxics

Upstream issue draft — cockroachdb plugin v0.1.9 hook ${CLAUDE_PLUGIN_ROOT} not substituted

Plugin: cockroachdb/claude-plugin v0.1.9
Filed by: (operator)
Discovered: 2026-06-16, during Patch project Session 34

Summary

The PostToolUse hook at hooks/hooks.json ships with the command:

json"command": "python3 "${CLAUDE_PLUGIN_ROOT}/scripts/check-sql-files.py""

In our Cowork sandbox runtime, ${CLAUDE_PLUGIN_ROOT} is not being substituted to the plugin's install path. The literal string ${CLAUDE_PLUGIN_ROOT} is passed straight through to bash, which then asks python3 to open:

C:\Users\patri\AppData\Local\Python\pythoncore-3.14-64\python.exe:
can't open file 'C:\Users\patri\AppData\Roaming\Claude
local-agent-mode-sessions<session-id>\outputs${CLAUDE_PLUGIN_ROOT}
\scripts\check-sql-files.py': [Errno 2] No such file or directory

(The bash workdir is the outputs scratchpad, so the un-substituted ${CLAUDE_PLUGIN_ROOT} is interpreted as a relative path under that.)

Impact

Not a correctness break — the hook is PostToolUse, so Write/Edit/MultiEdit complete normally before the hook fires. Files are written; subsequent reads see the updated content.
Visual noise — every Write or Edit emits a PostToolUse:Edit hook blocking error block to the conversation. In a session with dozens of file edits, this fires dozens of times.
Affected scope: the Write|Edit|MultiEdit matcher means the hook fires on every file edit regardless of file type. The script itself filters correctly (SQL_EXTENSIONS = {".sql", ".go", ".py", ".js", ".ts", ".java", ".rb"} + sys.exit(0) on miss), so the actual no-op behaviour on non-SQL files is correct — but only AFTER the script successfully starts. Currently it doesn't.

Reproduction

Install the cockroachdb plugin v0.1.9 in a Cowork session.
Use the Write or Edit tool to touch ANY file (does not have to be SQL).
Observe the PostToolUse:Edit hook blocking error after the write completes.

Root cause hypothesis

Claude Code's documented contract is that ${CLAUDE_PLUGIN_ROOT} in a hooks.json command field is substituted at hook-execution time with the plugin's resolved install path. Our session is evidently not performing that substitution — either because:

This Cowork sandbox runtime uses a different hook-substitution path than Claude Code CLI, OR
The plugin install path lookup is failing silently, leaving ${CLAUDE_PLUGIN_ROOT} un-substituted, OR
The bash shell invocation is escaping the ${CLAUDE_PLUGIN_ROOT} token before substitution.

We don't have Claude Code CLI side-by-side to confirm whether this is a Cowork-specific runtime bug or a general plugin-shape issue. Worth a check on the plugin maintainer's side for whether the hook works in standard Claude Code.

Suggested fixes (any of these)

Move the script reference to an absolute path captured at install time. When the plugin is installed, write the resolved path to a config file the hook command reads — sidesteps the ${CLAUDE_PLUGIN_ROOT} substitution dependency entirely.
Add a matcher filter on file extension so the hook only fires on writes whose tool_input.file_path matches *.{sql,go,py,js,ts,java,rb}. This both reduces hook noise AND limits the blast radius if the substitution does fail.
Have the script itself locate the plugin root via os.path.dirname(file) (or similar self-relative path resolution) so it doesn't need any env var at all when invoked.

Option (3) is the simplest and most robust — the script just needs to know where IT lives, not where the plugin is mounted.

Workaround for operators

If filing upstream takes time, operators in the meantime can:

Ignore — the hook is non-blocking (writes succeed). Just visual noise.
Disable — remove the PostToolUse block from hooks/hooks.json in the locally-installed plugin. Loses SQL-anti-pattern checking for any project that does have SQL files.
Patch in place — replace ${CLAUDE_PLUGIN_ROOT} with the absolute install path (/sessions//mnt/.remote-plugins/plugin_01J1ZUJcofzWJxiajSBsZ8US). Brittle (session-id changes) but works for a single session.

Plugin metadata (for the issue)

Plugin: cockroachdb v0.1.9
License: Apache-2.0
Repository: https://github.com/cockroachdb/claude-plugin
Hook file: hooks/hooks.json
Affected script: scripts/check-sql-files.py (the script itself is correct; only the hook command's path resolution is broken)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions