mirror of
https://github.com/anthropics/claude-code.git
synced 2026-02-19 04:27:33 -08:00
Compare commits
1 Commits
claude/sla
...
claude/sla
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
411381bf3e |
@@ -12,7 +12,6 @@ Learn more in the [official plugins documentation](https://docs.claude.com/en/do
|
||||
|
||||
| Name | Description | Contents |
|
||||
|------|-------------|----------|
|
||||
| [accept-with-feedback](./accept-with-feedback/) | Accept permission requests with feedback - provide guidance to Claude when approving operations | **Commands:** `/accept-feedback`, `/configure-feedback` - Set one-time or persistent feedback<br>**Hook:** PermissionRequest - Intercepts permissions to add user guidance |
|
||||
| [agent-sdk-dev](./agent-sdk-dev/) | Development kit for working with the Claude Agent SDK | **Command:** `/new-sdk-app` - Interactive setup for new Agent SDK projects<br>**Agents:** `agent-sdk-verifier-py`, `agent-sdk-verifier-ts` - Validate SDK applications against best practices |
|
||||
| [claude-opus-4-5-migration](./claude-opus-4-5-migration/) | Migrate code and prompts from Sonnet 4.x and Opus 4.1 to Opus 4.5 | **Skill:** `claude-opus-4-5-migration` - Automated migration of model strings, beta headers, and prompt adjustments |
|
||||
| [code-review](./code-review/) | Automated PR code review using multiple specialized agents with confidence-based scoring to filter false positives | **Command:** `/code-review` - Automated PR review workflow<br>**Agents:** 5 parallel Sonnet agents for CLAUDE.md compliance, bug detection, historical context, PR history, and code comments |
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"name": "accept-with-feedback",
|
||||
"version": "1.0.0",
|
||||
"description": "Accept permission requests with feedback - provide guidance to Claude when approving operations",
|
||||
"author": {
|
||||
"name": "Anthropic",
|
||||
"email": "support@anthropic.com"
|
||||
}
|
||||
}
|
||||
@@ -1,151 +0,0 @@
|
||||
# Accept with Feedback
|
||||
|
||||
A Claude Code plugin that lets you approve operations while providing guidance to Claude. Instead of just accepting or rejecting permission requests, you can accept *with feedback* - approving the operation while giving Claude additional context or instructions.
|
||||
|
||||
## Why?
|
||||
|
||||
Sometimes you want to approve an operation but also want to guide Claude's behavior:
|
||||
|
||||
- "Yes, edit that file, but make sure to add error handling"
|
||||
- "Okay to run those tests, but skip the slow integration tests"
|
||||
- "Go ahead and commit, but use conventional commit format"
|
||||
|
||||
This plugin bridges the gap between simple approval and rejection-with-feedback.
|
||||
|
||||
## Installation
|
||||
|
||||
This plugin is included in the Claude Code repository. Enable it in your settings or use:
|
||||
|
||||
```bash
|
||||
claude /plugin install accept-with-feedback
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### One-time feedback
|
||||
|
||||
Use the `/accept-feedback` command to queue feedback for the next permission request:
|
||||
|
||||
```
|
||||
/accept-feedback Make sure to preserve backwards compatibility
|
||||
```
|
||||
|
||||
When Claude next asks for permission (e.g., to edit a file), the operation will be automatically approved and Claude will receive your guidance as a system message.
|
||||
|
||||
### Persistent feedback rules
|
||||
|
||||
Create rules that automatically provide feedback for certain types of operations. Add configuration to `.claude/accept-feedback.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"rules": [
|
||||
{
|
||||
"matcher": "Edit|Write",
|
||||
"conditions": {
|
||||
"file_path": ".py"
|
||||
},
|
||||
"feedback": "Follow PEP 8 style and add type hints to all functions."
|
||||
},
|
||||
{
|
||||
"matcher": "Bash",
|
||||
"conditions": {
|
||||
"command": "npm"
|
||||
},
|
||||
"feedback": "Use --legacy-peer-deps if you encounter peer dependency issues."
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Use `/configure-feedback` for an interactive configuration experience.
|
||||
|
||||
## Configuration
|
||||
|
||||
### Rule properties
|
||||
|
||||
| Property | Description | Example |
|
||||
|----------|-------------|---------|
|
||||
| `matcher` | Tool name pattern | `"Edit"`, `"Write\|Edit"`, `"*"` |
|
||||
| `conditions` | Optional filters on tool input | `{"file_path": ".ts"}` |
|
||||
| `feedback` | Guidance message for Claude | `"Add JSDoc comments"` |
|
||||
|
||||
### Configuration locations
|
||||
|
||||
- **User-level**: `~/.claude/accept-feedback.json`
|
||||
- **Project-level**: `.claude/accept-feedback.json` (takes precedence)
|
||||
|
||||
## Commands
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `/accept-feedback <message>` | Queue feedback for the next permission request |
|
||||
| `/configure-feedback` | Interactive configuration of persistent rules |
|
||||
|
||||
## How it works
|
||||
|
||||
1. The plugin uses a `PermissionRequest` hook to intercept permission requests
|
||||
2. When a permission request occurs:
|
||||
- If there's pending feedback (from `/accept-feedback`), approve with that feedback
|
||||
- If a configured rule matches, approve with the rule's feedback
|
||||
- Otherwise, let the normal permission flow proceed
|
||||
3. Feedback is sent to Claude as a system message, providing guidance for the operation
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1: One-time guidance
|
||||
|
||||
```
|
||||
You: /accept-feedback Please add comprehensive error handling
|
||||
|
||||
Claude: I'll edit src/api.ts to add the new endpoint...
|
||||
[Permission automatically approved with your feedback]
|
||||
|
||||
Claude: I've added the endpoint with try-catch blocks and proper error responses...
|
||||
```
|
||||
|
||||
### Example 2: Persistent Python style rules
|
||||
|
||||
`.claude/accept-feedback.json`:
|
||||
```json
|
||||
{
|
||||
"rules": [
|
||||
{
|
||||
"matcher": "Edit|Write",
|
||||
"conditions": {
|
||||
"file_path": ".py"
|
||||
},
|
||||
"feedback": "Use Google-style docstrings and add type hints to all function signatures."
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Every time Claude edits a Python file, it receives this style guidance.
|
||||
|
||||
### Example 3: Git workflow rules
|
||||
|
||||
```json
|
||||
{
|
||||
"rules": [
|
||||
{
|
||||
"matcher": "Bash",
|
||||
"conditions": {
|
||||
"command": "git commit"
|
||||
},
|
||||
"feedback": "Use conventional commit format: type(scope): description"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Tips
|
||||
|
||||
- Use specific matchers to avoid unnecessary approvals
|
||||
- Conditions use substring matching - `".py"` matches any path containing `.py`
|
||||
- Combine with other permission management for a comprehensive workflow
|
||||
- Feedback is visible in the conversation, so Claude can reference it
|
||||
|
||||
## Related
|
||||
|
||||
- Rejecting with feedback: Built into Claude Code's plan rejection flow
|
||||
- Permission hooks: See the [hook development guide](../plugin-dev/skills/hook-development/SKILL.md)
|
||||
@@ -1,63 +0,0 @@
|
||||
---
|
||||
description: Set feedback to provide when accepting the next permission request
|
||||
argument_name: feedback
|
||||
---
|
||||
|
||||
# Accept with Feedback
|
||||
|
||||
You are helping the user set feedback that will be automatically provided to Claude when the next permission request is approved.
|
||||
|
||||
## What the user wants
|
||||
|
||||
The user wants to approve an upcoming operation but also provide guidance or feedback to Claude. This feedback will:
|
||||
1. Automatically approve the next permission request
|
||||
2. Send the feedback message to Claude as guidance
|
||||
|
||||
## Instructions
|
||||
|
||||
1. Parse the user's feedback from the argument: `$ARGUMENTS`
|
||||
|
||||
2. If feedback was provided, save it for the next permission request:
|
||||
- Create/update the file `~/.claude/pending-accept-feedback.json`
|
||||
- Store the feedback with the current session ID
|
||||
|
||||
3. Confirm to the user that their feedback has been queued
|
||||
|
||||
## Saving the feedback
|
||||
|
||||
Use this Python code to save the pending feedback:
|
||||
|
||||
```python
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
feedback = """$ARGUMENTS"""
|
||||
session_id = os.environ.get("CLAUDE_SESSION_ID", "default")
|
||||
|
||||
pending_file = Path.home() / ".claude" / "pending-accept-feedback.json"
|
||||
pending_file.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
try:
|
||||
existing = json.loads(pending_file.read_text()) if pending_file.exists() else {}
|
||||
except:
|
||||
existing = {}
|
||||
|
||||
existing[session_id] = {
|
||||
"message": feedback,
|
||||
"one_time": True
|
||||
}
|
||||
|
||||
pending_file.write_text(json.dumps(existing, indent=2))
|
||||
print(f"Feedback queued for next permission request.")
|
||||
```
|
||||
|
||||
## Example usage
|
||||
|
||||
User runs: `/accept-feedback Make sure to add error handling`
|
||||
|
||||
Then when Claude asks for permission to edit a file, the operation is automatically approved and Claude receives the guidance: "Make sure to add error handling"
|
||||
|
||||
## Response
|
||||
|
||||
After saving, confirm: "Your feedback has been queued. The next permission request will be automatically approved, and Claude will receive your guidance."
|
||||
@@ -1,80 +0,0 @@
|
||||
---
|
||||
description: Configure persistent feedback rules for accept-with-feedback
|
||||
---
|
||||
|
||||
# Configure Accept-with-Feedback Rules
|
||||
|
||||
You are helping the user configure persistent feedback rules that automatically provide guidance to Claude when certain operations are approved.
|
||||
|
||||
## Configuration file location
|
||||
|
||||
Rules are stored in `.claude/accept-feedback.json` in either:
|
||||
- User's home directory (`~/.claude/accept-feedback.json`) for global rules
|
||||
- Project directory (`.claude/accept-feedback.json`) for project-specific rules
|
||||
|
||||
Project rules take precedence over user rules.
|
||||
|
||||
## Configuration format
|
||||
|
||||
```json
|
||||
{
|
||||
"rules": [
|
||||
{
|
||||
"matcher": "Edit|Write",
|
||||
"conditions": {
|
||||
"file_path": ".py"
|
||||
},
|
||||
"feedback": "Ensure all Python code follows PEP 8 style guidelines and includes type hints."
|
||||
},
|
||||
{
|
||||
"matcher": "Bash",
|
||||
"conditions": {
|
||||
"command": "git"
|
||||
},
|
||||
"feedback": "Use conventional commit format for commit messages."
|
||||
},
|
||||
{
|
||||
"matcher": "*",
|
||||
"feedback": "Please explain your reasoning before making changes."
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Rule properties
|
||||
|
||||
- **matcher**: Tool name pattern (e.g., "Edit", "Write|Edit", "*" for all)
|
||||
- **conditions**: Optional key-value pairs to match against tool input
|
||||
- **feedback**: The guidance message to send to Claude when this rule matches
|
||||
|
||||
## Instructions
|
||||
|
||||
1. Ask the user what kind of feedback rules they want to configure
|
||||
2. Help them create appropriate rules based on their needs
|
||||
3. Save the configuration to the appropriate location
|
||||
|
||||
## Common use cases
|
||||
|
||||
1. **Code style guidance**: Provide style guidelines when editing specific file types
|
||||
2. **Git workflow**: Add commit message format requirements for git operations
|
||||
3. **Safety reminders**: Add warnings when working with sensitive files
|
||||
4. **Project conventions**: Enforce project-specific patterns and practices
|
||||
|
||||
## Example interaction
|
||||
|
||||
User: "I want to always remind Claude to add tests when editing Python files"
|
||||
|
||||
You would create:
|
||||
```json
|
||||
{
|
||||
"rules": [
|
||||
{
|
||||
"matcher": "Edit|Write",
|
||||
"conditions": {
|
||||
"file_path": ".py"
|
||||
},
|
||||
"feedback": "Remember to add or update tests for any code changes."
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
@@ -1,152 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Accept with Feedback Hook for Claude Code
|
||||
|
||||
This hook intercepts permission requests and allows users to provide feedback
|
||||
when accepting operations. The feedback is passed to Claude as a system message,
|
||||
giving users a way to provide guidance while still approving the operation.
|
||||
|
||||
Usage:
|
||||
1. Set pending feedback: /accept-feedback "your guidance here"
|
||||
2. When a permission prompt appears, if pending feedback exists:
|
||||
- The operation is automatically approved
|
||||
- Your feedback is sent to Claude as guidance
|
||||
3. Or configure always-on feedback rules in .claude/accept-feedback.json
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def get_config_paths():
|
||||
"""Get paths for config and state files."""
|
||||
claude_dir = Path.home() / ".claude"
|
||||
project_dir = Path(os.environ.get("CLAUDE_PROJECT_DIR", "."))
|
||||
|
||||
return {
|
||||
"user_config": claude_dir / "accept-feedback.json",
|
||||
"project_config": project_dir / ".claude" / "accept-feedback.json",
|
||||
"pending_feedback": claude_dir / "pending-accept-feedback.json",
|
||||
}
|
||||
|
||||
|
||||
def load_json_file(path):
|
||||
"""Load JSON from file, return empty dict if not found."""
|
||||
try:
|
||||
if path.exists():
|
||||
with open(path, "r") as f:
|
||||
return json.load(f)
|
||||
except (json.JSONDecodeError, IOError):
|
||||
pass
|
||||
return {}
|
||||
|
||||
|
||||
def get_pending_feedback(session_id):
|
||||
"""Get pending feedback for this session and clear it."""
|
||||
paths = get_config_paths()
|
||||
pending_file = paths["pending_feedback"]
|
||||
|
||||
pending = load_json_file(pending_file)
|
||||
|
||||
# Check for session-specific pending feedback
|
||||
if session_id in pending:
|
||||
feedback = pending[session_id]
|
||||
# Clear the pending feedback after use
|
||||
del pending[session_id]
|
||||
try:
|
||||
if pending:
|
||||
with open(pending_file, "w") as f:
|
||||
json.dump(pending, f)
|
||||
else:
|
||||
pending_file.unlink(missing_ok=True)
|
||||
except IOError:
|
||||
pass
|
||||
return feedback.get("message"), feedback.get("one_time", True)
|
||||
|
||||
return None, True
|
||||
|
||||
|
||||
def get_configured_feedback(tool_name, tool_input):
|
||||
"""Get feedback configured for this tool type."""
|
||||
paths = get_config_paths()
|
||||
|
||||
# Load both user and project configs
|
||||
user_config = load_json_file(paths["user_config"])
|
||||
project_config = load_json_file(paths["project_config"])
|
||||
|
||||
# Project config takes precedence
|
||||
config = {**user_config, **project_config}
|
||||
|
||||
rules = config.get("rules", [])
|
||||
|
||||
for rule in rules:
|
||||
# Check if rule matches this tool
|
||||
matcher = rule.get("matcher", "*")
|
||||
if matcher == "*" or tool_name in matcher.split("|"):
|
||||
# Check conditions if specified
|
||||
conditions = rule.get("conditions", {})
|
||||
matches = True
|
||||
|
||||
for key, pattern in conditions.items():
|
||||
value = str(tool_input.get(key, ""))
|
||||
if pattern not in value:
|
||||
matches = False
|
||||
break
|
||||
|
||||
if matches:
|
||||
return rule.get("feedback")
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def main():
|
||||
"""Main hook function."""
|
||||
# Read input from stdin
|
||||
try:
|
||||
raw_input = sys.stdin.read()
|
||||
input_data = json.loads(raw_input)
|
||||
except json.JSONDecodeError:
|
||||
# Can't parse input, don't interfere
|
||||
sys.exit(0)
|
||||
|
||||
session_id = input_data.get("session_id", "default")
|
||||
tool_name = input_data.get("tool_name", "")
|
||||
tool_input = input_data.get("tool_input", {})
|
||||
|
||||
# Check for pending feedback first (from /accept-feedback command)
|
||||
pending_message, _ = get_pending_feedback(session_id)
|
||||
|
||||
if pending_message:
|
||||
# We have pending feedback - approve with the feedback as system message
|
||||
result = {
|
||||
"hookSpecificOutput": {
|
||||
"permissionDecision": "allow"
|
||||
},
|
||||
"systemMessage": f"[User Feedback on Approval]: {pending_message}"
|
||||
}
|
||||
print(json.dumps(result))
|
||||
sys.exit(0)
|
||||
|
||||
# Check for configured feedback rules
|
||||
configured_feedback = get_configured_feedback(tool_name, tool_input)
|
||||
|
||||
if configured_feedback:
|
||||
# We have configured feedback for this tool type
|
||||
result = {
|
||||
"hookSpecificOutput": {
|
||||
"permissionDecision": "allow"
|
||||
},
|
||||
"systemMessage": f"[User Guidance]: {configured_feedback}"
|
||||
}
|
||||
print(json.dumps(result))
|
||||
sys.exit(0)
|
||||
|
||||
# No feedback configured - don't interfere with the normal permission flow
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"description": "Accept with feedback - allows users to provide guidance when approving tool permissions",
|
||||
"hooks": {
|
||||
"PermissionRequest": [
|
||||
{
|
||||
"matcher": "*",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/accept_with_feedback.py",
|
||||
"timeout": 300
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
"hooks": {
|
||||
"SessionStart": [
|
||||
{
|
||||
"matcher": "*",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
"hooks": {
|
||||
"SessionStart": [
|
||||
{
|
||||
"matcher": "*",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
"hooks": {
|
||||
"Stop": [
|
||||
{
|
||||
"matcher": "*",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
|
||||
49
scripts/validate-all-hooks.sh
Executable file
49
scripts/validate-all-hooks.sh
Executable 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
|
||||
Reference in New Issue
Block a user