Skill Authoring
Hook
Hooks
What jarrodwatts-claude-code-config runs automatically, and when. A hook is a command Claude Code fires at a fixed moment, without you asking for it.
Install
$ npx -y skills add jarrodwatts/claude-code-config --agent claude-codeShips with jarrodwatts-claude-code-config. Installing the plugin gets these hooks.
Where it lives
- hooks/check-comments.pyGitHub
Read the script
#!/usr/bin/env python3 """ Comment Checker Hook for Claude Code Analyzes code changes for excessive comments """ import json import sys import re from pathlib import Path # Configuration MAX_COMMENT_RATIO = 0.25 # 25% comments triggers warning CODE_EXTENSIONS = {'.ts', '.tsx', '.js', '.jsx', '.py', '.go', '.rs', '.java', '.cpp', '.c', '.sol'} # Patterns for valid comments (should not be flagged) VALID_PATTERNS = [ r'^\s*#\s*#?\s*(given|when|then|and|but)\b', # BDD comments (Python) r'^\s*//\s*#?\s*(given|when|then|and|but)\b', # BDD comments (JS/TS) r'^\s*"""', # Python docstrings r"^\s*'''", # Python docstrings r'^\s*/\*\*', # JSDoc/JavaDoc r'^\s*\*\s*@', # JSDoc annotations r'^\s*#!', # Shebang r'^\s*//\s*@ts-', # TypeScript directives r'^\s*//\s*eslint-', # ESLint directives r'^\s*#\s*type:', # Python type comments r'^\s*#\s*noqa', # Python noqa r'^\s*#\s*pragma', # Pragma directives r'^\s*//\s*TODO:', # TODO comments (acceptable) r'^\s*//\s*FIXME:', # FIXME comments (acceptable) r'^\s*#\s*TODO:', # TODO comments r'^\s*#\s*FIXME:', # FIXME comments r'^\s*///\s*', # Rust doc comments r'^\s*///', # Solidity NatSpec r'^\s*@dev', # Solidity dev comments r'^\s*@param', # Parameter docs r'^\s*@return', # Return docs r'^\s*@notice', # Solidity notice ] def is_valid_comment(line: str) -> bool: """Check if a comment line is a valid/acceptable pattern""" for pattern in VALID_PATTERNS: if re.match(pattern, line, re.IGNORECASE): return True return False def is_comment_line(line: str, ext: str) -> bool: """Check if a line is a comment""" stripped = line.strip() if not stripped: return False if ext in {'.py'}: return stripped.startswith('#') elif ext in {'.ts', '.tsx', '.js', '.jsx', '.java', '.go', '.rs', '.cpp', '.c', '.sol'}: return stripped.startswith('//') or stripped.startswith('/*') or stripped.startswith('*') return False def analyze_content(content: str, file_path: str) -> dict: """Analyze code content for comment ratio""" ext = Path(file_path).suffix.lower() if ext not in CODE_EXTENSIONS: return {"skip": True, "reason": "Not a code file"} lines = content.split('\n') total_lines = len([l for l in lines if l.strip()]) # Non-empty lines if total_lines == 0: return {"skip": True, "reason": "Empty file"} comment_lines = [] flagged_comments = [] for i, line in enumerate(lines, 1): if is_comment_line(line, ext): comment_lines.append(i) if not is_valid_comment(line): flagged_comments.append((i, line.strip())) comment_ratio = len(comment_lines) / total_lines if total_lines > 0 else 0 return { "skip": False, "total_lines": total_lines, "comment_lines": len(comment_lines), "flagged_comments": flagged_comments, "comment_ratio": comment_ratio, "excessive": comment_ratio > MAX_COMMENT_RATIO and len(flagged_comments) > 0 } def main(): # Debug logging to file import os debug_log = os.path.expanduser("~/.claude/hooks/debug.log") try: raw_input = sys.stdin.read() with open(debug_log, "a") as f: f.write(f"\n=== check-comments.py called ===\n") f.write(f"Raw input: {raw_input[:2000]}\n") input_data = json.loads(raw_input) except json.JSONDecodeError as e: with open(debug_log, "a") as f: f.write(f"JSON decode error: {e}\n") sys.exit(0) except Exception as e: with open(debug_log, "a") as f: f.write(f"Error reading stdin: {e}\n") sys.exit(0) with open(debug_log, "a") as f: f.write(f"Parsed input keys: {list(input_data.keys())}\n") tool_name = input_data.get("tool_name", "") tool_input = input_data.get("tool_input", {}) tool_response = input_data.get("tool_response", {}) file_path = tool_input.get("file_path") or tool_input.get("filePath") or tool_input.get("path", "") # Get content based on tool type content = "" if tool_name == "Write": content = tool_input.get("content", "") elif tool_name in ("Edit", "MultiEdit"): # For Edit, read the file after the edit was applied if file_path and Path(file_path).exists(): try: content = Path(file_path).read_text() except Exception as e: with open(debug_log, "a") as f: f.write(f"Error reading file: {e}\n") sys.exit(0) with open(debug_log, "a") as f: f.write(f"Tool: {tool_name}, File: {file_path}, Content length: {len(content)}\n") if not file_path or not content: sys.exit(0) result = analyze_content(content, file_path) with open(debug_log, "a") as f: f.write(f"Analysis result: {result}\n") if result.get("skip"): sys.exit(0) if result.get("excessive"): ratio_pct = f"{result['comment_ratio']:.0%}" # Build warning message warning_lines = [ "", "---", f"**Comment Check Warning**: {ratio_pct} of lines are comments ({result['comment_lines']}/{result['total_lines']})", "", "Flagged comments that may be unnecessary:", ] for line_num, comment in result['flagged_comments'][:5]: # Show max 5 warning_lines.append(f" Line {line_num}: `{comment[:60]}{'...' if len(comment) > 60 else ''}`") if len(result['flagged_comments']) > 5: warning_lines.append(f" ... and {len(result['flagged_comments']) - 5} more") warning_lines.extend([ "", "**Recommendation**: Code should be self-documenting. Consider:", "- Using descriptive variable/fun - hooks/keyword-detector.pyGitHub
Read the script
#!/usr/bin/env python3 """ Keyword Detector Hook for Claude Code Detects special keywords and injects mode-specific context """ import json import sys import re def main(): try: input_data = json.load(sys.stdin) except json.JSONDecodeError: sys.exit(0) prompt = input_data.get("prompt", "").lower() additional_context = None # Ultrawork mode - maximum performance if re.search(r'\b(ultrawork|ulw|ultra\s*work)\b', prompt): additional_context = """ [ULTRAWORK MODE ACTIVATED] Execute with maximum capability: 1. **Parallel Execution**: Launch multiple agents/tools simultaneously 2. **Comprehensive Planning**: Create detailed todo list BEFORE starting 3. **Thorough Verification**: Run diagnostics on all changed files 4. **No Premature Stopping**: Continue until ALL tasks complete 5. **Evidence-Based**: Verify each change works correctly Workflow: - Use Task tool to delegate to specialized agents (explore, oracle) - Launch independent searches in parallel - Create todos for complex multi-step work - Mark todos complete only after verification """ # Search mode - maximized search effort elif re.search(r'\b(search|find|locate|where\s+is)\b', prompt): additional_context = """ [SEARCH MODE ACTIVATED] Maximize search thoroughness: 1. **Parallel Searches**: Launch multiple search operations simultaneously 2. **Multiple Angles**: Search by name, content, pattern, and structure 3. **Cross-Reference**: Verify findings across multiple sources 4. **Exhaustive**: Don't stop at first result - find ALL matches Tools to use in parallel: - Grep for text patterns - Glob for file patterns - LSP for symbol definitions/references - Git for history when relevant Report: - All matching files with absolute paths - Relevance explanation for each match - Confidence level in completeness """ # Analysis mode - deep investigation elif re.search(r'\b(analyze|investigate|debug|diagnose)\b', prompt): additional_context = """ [ANALYSIS MODE ACTIVATED] Deep investigation protocol: 1. **Gather Evidence**: Read all relevant files before forming conclusions 2. **Multi-Phase Analysis**: - Phase 1: Surface-level scan - Phase 2: Deep dive into suspicious areas - Phase 3: Cross-reference and validate 3. **Consult Experts**: Use oracle agent for complex reasoning 4. **Document Findings**: Systematic, evidence-based conclusions For debugging: - Check recent changes (git log, git blame) - Trace data flow through the system - Identify edge cases and error paths - Propose hypothesis and test it """ # Think mode - extended reasoning (note: Claude Code has native ultrathink) elif re.search(r'\b(think\s*(deeply|hard|carefully))\b', prompt): additional_context = """ [EXTENDED THINKING MODE] Take time for thorough reasoning: 1. **Step Back**: Consider the broader context and implications 2. **Multiple Perspectives**: Evaluate different approaches 3. **Trade-off Analysis**: Document pros/cons of each option 4. **Risk Assessment**: Identify potential issues before implementing 5. **Validation Plan**: How will we verify success? Before acting: - State your understanding of the problem - List assumptions being made - Outline the approach with rationale - Identify potential failure modes """ if additional_context: output = { "hookSpecificOutput": { "hookEventName": "UserPromptSubmit", "additionalContext": additional_context.strip() } } print(json.dumps(output)) sys.exit(0) if __name__ == "__main__": main() - hooks/skill-reminder.shGitHub
Read the script
#!/usr/bin/env bash set -euo pipefail jq -n '{"hookSpecificOutput":{"additionalContext":"IMPORTANT: before writing code: invoke any relevant skills using the Skill tool."}}' - hooks/todo-enforcer.shGitHub
Read the script
#!/usr/bin/env bash # # Todo Enforcer - Claude Code Stop Hook # Blocks exit when incomplete todos exist in the session. # # SPDX-License-Identifier: MIT # Copyright (c) 2025 # # Usage: Configure in ~/.claude/settings.json under hooks.Stop # Disable: Set {"enabled": false} in ~/.claude/hooks/todo-enforcer.config.json # shellcheck disable=SC2155 set -euo pipefail readonly CONFIG_FILE="$HOME/.claude/hooks/todo-enforcer.config.json" readonly DEBUG_LOG="$HOME/.claude/hooks/todo-enforcer.log" readonly MAX_CONSECUTIVE_BLOCKS=10 log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] [${2:-INFO}] $1" >> "$DEBUG_LOG" 2>/dev/null || true } die() { log "$1" "ERROR" exit 0 } if ! command -v jq &>/dev/null; then die "jq is required but not installed" fi # Defer to ralph-wiggum when a loop is active [[ -f ".claude/ralph-loop.local.md" ]] && exit 0 load_config() { if [[ -f "$CONFIG_FILE" ]]; then cat "$CONFIG_FILE" 2>/dev/null || echo '{"enabled":true,"block_count":0}' else echo '{"enabled":true,"block_count":0}' fi } save_config() { local temp_file="${CONFIG_FILE}.tmp.$$" echo "$1" > "$temp_file" 2>/dev/null && mv "$temp_file" "$CONFIG_FILE" 2>/dev/null || true } allow_exit() { [[ -n "${1:-}" ]] && log "Allowing exit: $1" exit 0 } HOOK_INPUT=$(cat) log "Hook started" CONFIG=$(load_config) if [[ "$(echo "$CONFIG" | jq -r '.enabled // true')" != "true" ]]; then allow_exit "Disabled via config" fi read -r SESSION_ID TRANSCRIPT_PATH STOP_HOOK_ACTIVE < <( echo "$HOOK_INPUT" | jq -r '[.session_id // "unknown", .transcript_path // "", .stop_hook_active // false] | @tsv' ) log "Session: $SESSION_ID | stop_hook_active: $STOP_HOOK_ACTIVE" if [[ "$STOP_HOOK_ACTIVE" == "true" ]]; then if [[ "$(echo "$CONFIG" | jq -r '.last_block_session // ""')" == "$SESSION_ID" ]]; then CONFIG=$(echo "$CONFIG" | jq '.block_count = 0') save_config "$CONFIG" fi fi [[ -z "$TRANSCRIPT_PATH" ]] && allow_exit "No transcript path" [[ ! -f "$TRANSCRIPT_PATH" ]] && allow_exit "Transcript not found" TODOS_JSON=$(jq -s ' [.[] | .message.content[]? | select(.type == "tool_use" and .name == "TodoWrite") | .input.todos] | last // empty ' "$TRANSCRIPT_PATH" 2>/dev/null || echo "") if [[ -z "$TODOS_JSON" || "$TODOS_JSON" == "null" ]]; then CONFIG=$(echo "$CONFIG" | jq '.block_count = 0') save_config "$CONFIG" allow_exit "No todos found" fi read -r PENDING_COUNT IN_PROGRESS_COUNT < <( echo "$TODOS_JSON" | jq -r '[ [.[] | select(.status == "pending")] | length, [.[] | select(.status == "in_progress")] | length ] | @tsv' ) INCOMPLETE_COUNT=$((PENDING_COUNT + IN_PROGRESS_COUNT)) log "Pending: $PENDING_COUNT, In progress: $IN_PROGRESS_COUNT" if [[ "$INCOMPLETE_COUNT" -eq 0 ]]; then CONFIG=$(echo "$CONFIG" | jq '.block_count = 0') save_config "$CONFIG" allow_exit "All todos completed" fi read -r BLOCK_COUNT LAST_SESSION < <( echo "$CONFIG" | jq -r '[.block_count // 0, .last_block_session // ""] | @tsv' ) if [[ "$LAST_SESSION" == "$SESSION_ID" ]]; then BLOCK_COUNT=$((BLOCK_COUNT + 1)) else BLOCK_COUNT=1 fi if [[ "$BLOCK_COUNT" -ge "$MAX_CONSECUTIVE_BLOCKS" ]]; then log "Safety valve triggered after $MAX_CONSECUTIVE_BLOCKS blocks" "WARN" CONFIG=$(echo "$CONFIG" | jq '.block_count = 0') save_config "$CONFIG" allow_exit "Safety valve triggered" fi CONFIG=$(echo "$CONFIG" | jq --arg sid "$SESSION_ID" --argjson count "$BLOCK_COUNT" \ '.block_count = $count | .last_block_session = $sid') save_config "$CONFIG" log "Blocking (count: $BLOCK_COUNT): $INCOMPLETE_COUNT incomplete" TASK_LIST=$(echo "$TODOS_JSON" | jq -r ' ([.[] | select(.status == "in_progress") | " → [in progress] \(.content)"] + [.[] | select(.status == "pending") | " ○ [pending] \(.content)"]) | join("\n") ') REASON="You have $INCOMPLETE_COUNT incomplete todo(s): $TASK_LIST Complete these tasks before stopping. Mark each task as 'completed' using TodoWrite when done." jq -n --arg reason "$REASON" '{"decision": "block", "reason": $reason}' exit 0
Read the script before you install anything that runs on your machine. This is the one part of a plugin that acts without being asked.
Ships withjarrodwatts-claude-code-config
my personal claude code configuration - mostly not created by me, but sourced from many talented people in the community.
Get the whole plugin
Stats
1,062
Stars
120
Forks
Quiet
Maintenance
JavaScript
Language
9mo ago
Last commit
9mo ago
Created
8d ago
Added

