Validate tool-routing plugin installation and hooks format
Verify tool-routing plugin installation and hook format. Run this to validate manifests, test route patterns, and confirm hooks fire correctly before using the plugin in production.
/plugin marketplace add technicalpickles/pickled-claude-plugins/plugin install tool-routing@technicalpickles-marketplaceRun these steps to verify the tool-routing plugin is correctly installed and working.
This plugin is part of the pickled-claude-plugins monorepo. Routes are contributed by multiple plugins:
| Plugin | Route Location | Routes |
|---|---|---|
| tool-routing | hooks/tool-routes.yaml | bash-cat-heredoc, bash-echo-*, tool-routing-manual-test |
| dev-tools | hooks/tool-routes.yaml | atlassian |
| git-workflows | skills/writing-pull-requests/tool-routes.yaml | github-pr, git-commit-multiline, gh-pr-create-multiline |
| ci-cd-tools | skills/working-with-buildkite-builds/tool-routes.yaml | buildkite |
| mcpproxy | skills/working-with-mcp/tool-routes.yaml | bash-mcp-cli, bash-mcp-tool |
Run claude plugin validate on each plugin with a manifest:
# From repo root
for plugin in plugins/*/; do
if [ -d "$plugin/.claude-plugin" ]; then
echo "=== Validating $plugin ==="
claude plugin validate "$plugin"
fi
done
The plugin includes pytest tests that validate hooks.json format:
cd plugins/tool-routing
uv run pytest tests/test_plugin_structure.py -v
These tests catch silent failures where hooks.json is valid JSON but wrong format:
| Wrong Format | Correct Format |
|---|---|
"hooks": [...] (array) | "hooks": {"PreToolUse": [...]} (object keyed by event) |
"matcher": {"tool_name": "X"} | "matcher": "X" (string pattern) |
"type": "preToolUse" | "type": "command" |
$CLAUDE_PLUGIN_ROOT | ${CLAUDE_PLUGIN_ROOT} (with braces) |
Test that route patterns match correctly across all plugins in the monorepo:
cd plugins/tool-routing
CLAUDE_PLUGIN_ROOT="$PWD" CLAUDE_PLUGINS_DIR="../" uv run tool-routing test
This discovers and tests routes from all plugins, not just tool-routing.
Expected output: Routes from 5 sources, 30+ tests passing.
After the above pass, test that hooks actually fire:
WebFetch https://github.com/owner/repo/pull/123
gh pr viewIf the WebFetch goes through instead of being blocked, the hooks aren't loading. Check:
/plugin → Manage Pluginsclaude --debug for hook loading errorsRun Claude Code with debug logging:
claude --debug
Look for:
[DEBUG] Executing hooks for PreToolUse:WebFetch| Symptom | Cause | Fix |
|---|---|---|
| WebFetch not blocked | Hooks not loading | Check format, restart Claude Code |
| "No module named tool_routing" | Wrong working directory | Run from plugin root with uv run |
| Plugin not in list | Missing manifest | Create .claude-plugin/plugin.json |
| Tests fail on matcher format | Old hooks.json format | Update to object-keyed structure |
| Only 1 route source found | Stale cache or wrong plugins_dir | See troubleshooting in route-discovery.md |
Critical check: Ensure routes from ALL plugins are discovered, not just tool-routing.
cd plugins/tool-routing
CLAUDE_PLUGIN_ROOT="$PWD" CLAUDE_PLUGINS_DIR="../" uv run tool-routing list
Expected: "Routes (merged from 5 sources)"
If you only see 1 source:
hooks/tool-routes.yaml or skills/*/tool-routes.yamldocs/route-discovery.md#troubleshooting for more diagnosticsFor full verification that hooks actually block at runtime, use subagents:
Get test cases:
uv run tool-routing integration-test --list > .tmp/integration-tests.json
Spawn a subagent to execute the tests. The subagent should:
[{"id": 0, "result": "blocked", "message": "..."}, ...]Save the subagent's JSON report to .tmp/integration-report.json
Evaluate results:
uv run tool-routing integration-test --evaluate \
--tests .tmp/integration-tests.json \
--report .tmp/integration-report.json
This verifies the full integration path: Claude Code → hooks → tool-routing check → block/allow.
The tests/test_plugin_structure.py file can be adapted for any plugin with hooks. Copy it and adjust:
PLUGIN_ROOT path