Files
gh-glittercowboy-taches-cc-…/skills/create-hooks/references/command-vs-prompt.md
2025-11-29 18:28:37 +08:00

6.6 KiB

Command vs Prompt Hooks

Decision guide for choosing between command-based and prompt-based hooks.

Decision Tree

Need to execute a hook?
│
├─ Simple yes/no validation?
│  └─ Use COMMAND (faster, cheaper)
│
├─ Need natural language understanding?
│  └─ Use PROMPT (LLM evaluation)
│
├─ External tool interaction?
│  └─ Use COMMAND (formatters, linters, git)
│
├─ Complex decision logic?
│  └─ Use PROMPT (reasoning required)
│
└─ Logging/notification only?
   └─ Use COMMAND (no decision needed)

Command Hooks

Characteristics

  • Execution: Shell command
  • Input: JSON via stdin
  • Output: JSON via stdout (optional)
  • Speed: Fast (no LLM call)
  • Cost: Free (no API usage)
  • Complexity: Limited to shell scripting logic

When to use

Use command hooks for:

  • File operations (read, write, check existence)
  • Running tools (prettier, eslint, git)
  • Simple pattern matching (grep, regex)
  • Logging to files
  • Desktop notifications
  • Fast validation (file size, permissions)

Don't use command hooks for:

  • Natural language analysis
  • Complex decision logic
  • Context-aware validation
  • Semantic understanding

Examples

1. Log bash commands

{
  "type": "command",
  "command": "jq -r '\"\\(.tool_input.command) - \\(.tool_input.description // \\\"No description\\\")\"' >> ~/.claude/bash-log.txt"
}

2. Block if file doesn't exist

#!/bin/bash
# check-file-exists.sh

input=$(cat)
file=$(echo "$input" | jq -r '.tool_input.file_path')

if [ ! -f "$file" ]; then
  echo '{"decision": "block", "reason": "File does not exist"}'
  exit 0
fi

echo '{"decision": "approve", "reason": "File exists"}'

3. Run prettier after edits

{
  "type": "command",
  "command": "prettier --write \"$(echo {} | jq -r '.tool_input.file_path')\"",
  "timeout": 10000
}

4. Desktop notification

{
  "type": "command",
  "command": "osascript -e 'display notification \"Claude needs input\" with title \"Claude Code\"'"
}

Parsing input in commands

Command hooks receive JSON via stdin. Use jq to parse:

#!/bin/bash
input=$(cat)  # Read stdin

# Extract fields
tool_name=$(echo "$input" | jq -r '.tool_name')
command=$(echo "$input" | jq -r '.tool_input.command')
session_id=$(echo "$input" | jq -r '.session_id')

# Your logic here
if [[ "$command" == *"rm -rf"* ]]; then
  echo '{"decision": "block", "reason": "Dangerous command"}'
else
  echo '{"decision": "approve", "reason": "Safe"}'
fi

Prompt Hooks

Characteristics

  • Execution: LLM evaluates prompt
  • Input: Prompt string with $ARGUMENTS placeholder
  • Output: LLM generates JSON response
  • Speed: Slower (~1-3s per evaluation)
  • Cost: Uses API credits
  • Complexity: Can reason, understand context, analyze semantics

When to use

Use prompt hooks for:

  • Natural language validation
  • Semantic analysis (intent, safety, appropriateness)
  • Complex decision trees
  • Context-aware checks
  • Reasoning about code quality
  • Understanding user intent

Don't use prompt hooks for:

  • Simple pattern matching (use regex/grep)
  • File operations (use command hooks)
  • High-frequency events (too slow/expensive)
  • Non-decision tasks (logging, notifications)

Examples

1. Validate commit messages

{
  "type": "prompt",
  "prompt": "Evaluate this git commit message: $ARGUMENTS\n\nCheck if it:\n1. Starts with conventional commit type (feat|fix|docs|refactor|test|chore)\n2. Is descriptive and clear\n3. Under 72 characters\n\nReturn: {\"decision\": \"approve\" or \"block\", \"reason\": \"specific feedback\"}"
}

2. Check if Stop is appropriate

{
  "type": "prompt",
  "prompt": "Review the conversation transcript: $ARGUMENTS\n\nDetermine if Claude should stop:\n1. All user tasks completed?\n2. Any errors that need fixing?\n3. Tests passing?\n4. Documentation updated?\n\nIf incomplete: {\"decision\": \"block\", \"reason\": \"what's missing\"}\nIf complete: {\"decision\": \"approve\", \"reason\": \"all done\"}"
}

3. Validate code changes for security

{
  "type": "prompt",
  "prompt": "Analyze this code change for security issues: $ARGUMENTS\n\nCheck for:\n- SQL injection vulnerabilities\n- XSS attack vectors\n- Authentication bypasses\n- Sensitive data exposure\n\nIf issues found: {\"decision\": \"block\", \"reason\": \"specific vulnerabilities\"}\nIf safe: {\"decision\": \"approve\", \"reason\": \"no issues found\"}"
}

4. Semantic prompt validation

{
  "type": "prompt",
  "prompt": "Evaluate user prompt: $ARGUMENTS\n\nIs this:\n1. Related to coding/development?\n2. Appropriate and professional?\n3. Clear and actionable?\n\nIf inappropriate: {\"decision\": \"block\", \"reason\": \"why\"}\nIf good: {\"decision\": \"approve\", \"reason\": \"ok\"}"
}

Writing effective prompts

Be specific about output format:

Return JSON: {"decision": "approve" or "block", "reason": "explanation"}

Provide clear criteria:

Block if:
1. Command contains 'rm -rf /'
2. Force push to main branch
3. Credentials in plain text

Otherwise approve.

Use $ARGUMENTS placeholder:

Analyze this input: $ARGUMENTS

Check for...

The $ARGUMENTS placeholder is replaced with the actual hook input JSON.


Performance Comparison

Aspect Command Hook Prompt Hook
Speed <100ms 1-3s
Cost Free ~$0.001-0.01 per call
Complexity Shell scripting Natural language
Context awareness Limited High
Reasoning No Yes
Best for Operations, logging Validation, analysis

Combining Both

You can use multiple hooks for the same event:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "echo \"$input\" >> ~/bash-log.txt",
            "comment": "Log every command (fast)"
          },
          {
            "type": "prompt",
            "prompt": "Analyze this bash command for safety: $ARGUMENTS",
            "comment": "Validate with LLM (slower, smarter)"
          }
        ]
      }
    ]
  }
}

Hooks execute in order. If any hook blocks, execution stops.


Recommendations

High-frequency events (PreToolUse, PostToolUse):

  • Prefer command hooks
  • Use prompt hooks sparingly
  • Cache LLM decisions when possible

Low-frequency events (Stop, UserPromptSubmit):

  • Prompt hooks are fine
  • Cost/latency less critical

Balance:

  • Command hooks for simple checks
  • Prompt hooks for complex validation
  • Combine when appropriate