Compare commits

..

1 Commits

Author SHA1 Message Date
Claude
411381bf3e fix: Add missing matcher fields to hooks.json files
Stop hooks (and other hook types) were not firing because they were
missing the required "matcher" field. According to the hook development
documentation, all hooks must have a matcher field - "*" for wildcard
matching.

Changes:
- Add matcher: "*" to all hooks in hookify, ralph-wiggum,
  explanatory-output-style, and learning-output-style plugins
- Update validate-hook-schema.sh to properly handle plugin format
  (with 'hooks' wrapper) vs settings format (events at root)
- Add validate-all-hooks.sh script to validate all hooks.json files

Fixes: https://anthropic.slack.com/archives/C08EHE6JF3L/p1765822035850959
2025-12-15 18:14:58 +00:00
9 changed files with 93 additions and 58 deletions

View File

@@ -1,32 +1,5 @@
# Changelog
## 2.0.71
- Added `blockedMarketplaces` to enterprise managed settings, allowing administrators to prevent specific plugin marketplaces from being installed
- Added /config toggle to enable/disable prompt suggestions
- Added `/settings` as an alias for the `/config` command
- Fixed @ file reference suggestions incorrectly triggering when cursor is in the middle of a path
- Fixed MCP servers from `.mcp.json` not loading when using `--dangerously-skip-permissions`
- Fixed permission rules incorrectly rejecting valid bash commands containing shell glob patterns (e.g., `ls *.txt`, `for f in *.png`)
- Bedrock: Environment variable `ANTHROPIC_BEDROCK_BASE_URL` is now respected for token counting and inference profile listing
- New syntax highlighting engine for native build
## 2.0.70
- Added Enter key to accept and submit prompt suggestions immediately (tab still accepts for editing)
- Added wildcard syntax `mcp__server__*` for MCP tool permissions to allow or deny all tools from a server
- Added auto-update toggle for plugin marketplaces, allowing per-marketplace control over automatic updates
- Added `plan_mode_required` spawn parameter for teammates to require plan approval before implementing changes
- Added `current_usage` field to status line input, enabling accurate context window percentage calculations
- Fixed input being cleared when processing queued commands while the user was typing
- Fixed prompt suggestions replacing typed input when pressing Tab
- Fixed diff view not updating when terminal is resized
- Improved memory usage by 3x for large conversations
- Improved resolution of stats screenshots copied to clipboard (Ctrl+S) for crisper images
- Removed # shortcut for quick memory entry (tell Claude to edit your CLAUDE.md instead)
- Fix thinking mode toggle in /config not persisting correctly
- Improve UI for file creation permission dialog
## 2.0.69
- Minor bugfixes

View File

@@ -22,29 +22,23 @@ Performs automated code review on a pull request using multiple specialized agen
- **Agent #4**: Analyze git blame/history for context-based issues
5. Scores each issue 0-100 for confidence level
6. Filters out issues below 80 confidence threshold
7. Outputs review (to terminal by default, or as PR comment with `--comment` flag)
7. Posts review comment with high-confidence issues only
**Usage:**
```bash
/code-review [--comment]
/code-review
```
**Options:**
- `--comment`: Post the review as a comment on the pull request (default: outputs to terminal only)
**Example workflow:**
```bash
# On a PR branch, run locally (outputs to terminal):
# On a PR branch, run:
/code-review
# Post review as PR comment:
/code-review --comment
# Claude will:
# - Launch 4 review agents in parallel
# - Score each issue for confidence
# - Output issues ≥80 confidence (to terminal or PR depending on flag)
# - Skip if no high-confidence issues found
# - Post comment with issues ≥80 confidence
# - Skip posting if no high-confidence issues found
```
**Features:**
@@ -120,23 +114,17 @@ This plugin is included in the Claude Code repository. The command is automatica
### Standard PR review workflow:
```bash
# Create PR with changes
# Run local review (outputs to terminal)
/code-review
# Review the automated feedback
# Make any necessary fixes
# Optionally post as PR comment
/code-review --comment
# Merge when ready
```
### As part of CI/CD:
```bash
# Trigger on PR creation or update
# Use --comment flag to post review comments
/code-review --comment
# Automatically posts review comments
# Skip if review already exists
```

View File

@@ -52,9 +52,7 @@ Note: Still review Claude generated PR's.
6. Filter out any issues that were not validated in step 5. This step will give us our list of high signal issues for our review.
7. Finally, output the review.
- If the `--comment` argument is provided, post the review as a comment on the pull request using `gh pr comment`
- Otherwise (default), output the review directly to the terminal for local viewing
7. Finally, comment on the pull request.
When writing your comment, follow these guidelines:
a. Keep your output brief
b. Avoid emojis

View File

@@ -3,6 +3,7 @@
"hooks": {
"SessionStart": [
{
"matcher": "*",
"hooks": [
{
"type": "command",

View File

@@ -3,6 +3,7 @@
"hooks": {
"PreToolUse": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
@@ -14,6 +15,7 @@
],
"PostToolUse": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
@@ -25,6 +27,7 @@
],
"Stop": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
@@ -36,6 +39,7 @@
],
"UserPromptSubmit": [
{
"matcher": "*",
"hooks": [
{
"type": "command",

View File

@@ -3,6 +3,7 @@
"hooks": {
"SessionStart": [
{
"matcher": "*",
"hooks": [
{
"type": "command",

View File

@@ -40,7 +40,27 @@ echo ""
echo "Checking root structure..."
VALID_EVENTS=("PreToolUse" "PostToolUse" "UserPromptSubmit" "Stop" "SubagentStop" "SessionStart" "SessionEnd" "PreCompact" "Notification")
for event in $(jq -r 'keys[]' "$HOOKS_FILE"); do
# Detect format: plugin format has { description?, hooks: {...} } wrapper
# Settings format has events directly at root level
is_plugin_format=false
if jq -e '.hooks' "$HOOKS_FILE" >/dev/null 2>&1; then
is_plugin_format=true
HOOKS_PATH=".hooks"
echo "Detected plugin format (with 'hooks' wrapper)"
# Validate allowed root keys for plugin format
for key in $(jq -r 'keys[]' "$HOOKS_FILE"); do
if [ "$key" != "hooks" ] && [ "$key" != "description" ]; then
echo "⚠️ Unknown root key in plugin format: $key (expected: 'hooks', 'description')"
fi
done
else
HOOKS_PATH="."
echo "Detected settings format (events at root)"
fi
# Validate event types
for event in $(jq -r "$HOOKS_PATH | keys[]" "$HOOKS_FILE"); do
found=false
for valid_event in "${VALID_EVENTS[@]}"; do
if [ "$event" = "$valid_event" ]; then
@@ -62,12 +82,12 @@ echo "Validating individual hooks..."
error_count=0
warning_count=0
for event in $(jq -r 'keys[]' "$HOOKS_FILE"); do
hook_count=$(jq -r ".\"$event\" | length" "$HOOKS_FILE")
for event in $(jq -r "$HOOKS_PATH | keys[]" "$HOOKS_FILE"); do
hook_count=$(jq -r "$HOOKS_PATH.\"$event\" | length" "$HOOKS_FILE")
for ((i=0; i<hook_count; i++)); do
# Check matcher exists
matcher=$(jq -r ".\"$event\"[$i].matcher // empty" "$HOOKS_FILE")
matcher=$(jq -r "$HOOKS_PATH.\"$event\"[$i].matcher // empty" "$HOOKS_FILE")
if [ -z "$matcher" ]; then
echo "$event[$i]: Missing 'matcher' field"
((error_count++))
@@ -75,7 +95,7 @@ for event in $(jq -r 'keys[]' "$HOOKS_FILE"); do
fi
# Check hooks array exists
hooks=$(jq -r ".\"$event\"[$i].hooks // empty" "$HOOKS_FILE")
hooks=$(jq -r "$HOOKS_PATH.\"$event\"[$i].hooks // empty" "$HOOKS_FILE")
if [ -z "$hooks" ] || [ "$hooks" = "null" ]; then
echo "$event[$i]: Missing 'hooks' array"
((error_count++))
@@ -83,10 +103,10 @@ for event in $(jq -r 'keys[]' "$HOOKS_FILE"); do
fi
# Validate each hook in the array
hook_array_count=$(jq -r ".\"$event\"[$i].hooks | length" "$HOOKS_FILE")
hook_array_count=$(jq -r "$HOOKS_PATH.\"$event\"[$i].hooks | length" "$HOOKS_FILE")
for ((j=0; j<hook_array_count; j++)); do
hook_type=$(jq -r ".\"$event\"[$i].hooks[$j].type // empty" "$HOOKS_FILE")
hook_type=$(jq -r "$HOOKS_PATH.\"$event\"[$i].hooks[$j].type // empty" "$HOOKS_FILE")
if [ -z "$hook_type" ]; then
echo "$event[$i].hooks[$j]: Missing 'type' field"
@@ -102,7 +122,7 @@ for event in $(jq -r 'keys[]' "$HOOKS_FILE"); do
# Check type-specific fields
if [ "$hook_type" = "command" ]; then
command=$(jq -r ".\"$event\"[$i].hooks[$j].command // empty" "$HOOKS_FILE")
command=$(jq -r "$HOOKS_PATH.\"$event\"[$i].hooks[$j].command // empty" "$HOOKS_FILE")
if [ -z "$command" ]; then
echo "$event[$i].hooks[$j]: Command hooks must have 'command' field"
((error_count++))
@@ -114,7 +134,7 @@ for event in $(jq -r 'keys[]' "$HOOKS_FILE"); do
fi
fi
elif [ "$hook_type" = "prompt" ]; then
prompt=$(jq -r ".\"$event\"[$i].hooks[$j].prompt // empty" "$HOOKS_FILE")
prompt=$(jq -r "$HOOKS_PATH.\"$event\"[$i].hooks[$j].prompt // empty" "$HOOKS_FILE")
if [ -z "$prompt" ]; then
echo "$event[$i].hooks[$j]: Prompt hooks must have 'prompt' field"
((error_count++))
@@ -128,7 +148,7 @@ for event in $(jq -r 'keys[]' "$HOOKS_FILE"); do
fi
# Check timeout
timeout=$(jq -r ".\"$event\"[$i].hooks[$j].timeout // empty" "$HOOKS_FILE")
timeout=$(jq -r "$HOOKS_PATH.\"$event\"[$i].hooks[$j].timeout // empty" "$HOOKS_FILE")
if [ -n "$timeout" ] && [ "$timeout" != "null" ]; then
if ! [[ "$timeout" =~ ^[0-9]+$ ]]; then
echo "$event[$i].hooks[$j]: Timeout must be a number"

View File

@@ -3,6 +3,7 @@
"hooks": {
"Stop": [
{
"matcher": "*",
"hooks": [
{
"type": "command",

49
scripts/validate-all-hooks.sh Executable file
View File

@@ -0,0 +1,49 @@
#!/bin/bash
# Validate all hooks.json files in the repository
# This script can be run in CI to ensure all plugins have valid hook configurations
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(dirname "$SCRIPT_DIR")"
VALIDATOR="$REPO_ROOT/plugins/plugin-dev/skills/hook-development/scripts/validate-hook-schema.sh"
echo "🔍 Validating all hooks.json files in the repository..."
echo ""
# Find all hooks.json files
mapfile -t HOOKS_FILES < <(find "$REPO_ROOT/plugins" -name "hooks.json" -type f 2>/dev/null)
if [ ${#HOOKS_FILES[@]} -eq 0 ]; then
echo "No hooks.json files found"
exit 0
fi
echo "Found ${#HOOKS_FILES[@]} hooks.json file(s)"
echo ""
errors=0
for hooks_file in "${HOOKS_FILES[@]}"; do
relative_path="${hooks_file#$REPO_ROOT/}"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "📄 $relative_path"
echo ""
if bash "$VALIDATOR" "$hooks_file"; then
echo ""
else
echo ""
((errors++))
fi
done
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
if [ $errors -eq 0 ]; then
echo "✅ All ${#HOOKS_FILES[@]} hooks.json file(s) are valid!"
exit 0
else
echo "$errors hooks.json file(s) have validation errors"
exit 1
fi