Skip to content

Instantly share code, notes, and snippets.

@dgnsrekt
Last active August 16, 2025 22:57
Show Gist options
  • Select an option

  • Save dgnsrekt/884ff14296c9ae03bdf2d8bd0f3281c2 to your computer and use it in GitHub Desktop.

Select an option

Save dgnsrekt/884ff14296c9ae03bdf2d8bd0f3281c2 to your computer and use it in GitHub Desktop.
Claude hooks I can't live without.
#!/usr/bin/env python3
"""
PreToolUse hook to block hook bypassing and enforce Go quality standards.
This hook prevents bypassing code quality checks and ensures proper Go
development practices are followed. Sends security notifications when
bypass attempts are detected.
"""
import json
import sys
# Import shared notification utilities
from strategic_notifications import (
log,
get_project_context,
send_strategic_notification,
play_audio
)
HOOK_NAME = "block-skip-hooks"
def send_security_alert(
violation_type: str,
command: str,
session_id: str,
cwd: str,
details: str
) -> None:
"""
Send a high-priority security notification about hook bypass attempts.
Args:
violation_type: Type of security violation (e.g., "SKIP bypass", "no-verify bypass")
command: The blocked command
session_id: Claude Code session ID
cwd: Current working directory
details: Additional details about the violation
"""
try:
project_info = get_project_context(cwd, HOOK_NAME)
message = f"""Security Alert: Hook Bypass Attempt
**Violation**: {violation_type}
**Command**: {command}
**Details**: {details}
This attempt was BLOCKED to maintain code quality standards.
All hook bypass attempts are logged and monitored."""
success = send_strategic_notification(
message=message,
title="🚨 Strategic Core: Security Alert",
priority="high",
tags=["rotating_light", "warning", "security", "strategic-core"],
topic_type="alerts",
project_info=project_info,
session_id=session_id,
hook_name=HOOK_NAME
)
if success:
log("Security alert notification sent", HOOK_NAME)
# Play gutter-trash audio for security violations
play_audio("gutter-trash.mp3", HOOK_NAME)
else:
log("Failed to send security alert notification", HOOK_NAME)
except Exception as e:
log(f"Error sending security alert: {e}", HOOK_NAME)
def main() -> None:
"""Check and block inappropriate git and development commands."""
try:
# Read the hook input from stdin
hook_input = json.loads(sys.stdin.read())
# Extract tool information
tool_input = hook_input.get("tool_input", {})
session_id = hook_input.get("session_id", "unknown")
cwd = hook_input.get("cwd", "")
# Get the command being executed
command = tool_input.get("command", "")
# Block git commits with SKIP environment variable
if "git commit" in command and "SKIP=" in command:
error_message = (
"🚫 Blocked: Using SKIP to bypass pre-commit hooks is not allowed.\n\n"
"STOP and explain why you're trying to bypass quality checks:\n"
"- What specific linting errors are you facing?\n"
"- Have you tried fixing the actual issues?\n"
"- Why do you think bypassing is necessary?\n\n"
"Instead of skipping, fix the real issues:\n"
"1. 🔧 Fix gofmt formatting (run: gofmt -w .)\n"
"2. 📝 Add periods to comment endings\n"
"3. 🚀 Address golangci-lint warnings properly\n"
"4. 🔍 Fix go vet concerns\n\n"
"Quality standards exist to maintain Strategic Core v2's excellence."
)
# Send security alert
send_security_alert(
violation_type="SKIP environment variable bypass",
command=command,
session_id=session_id,
cwd=cwd,
details="Attempted to use SKIP= to bypass pre-commit hooks"
)
sys.stdout.write(
json.dumps({"decision": "block", "message": error_message}) + "\n"
)
return
# Block --no-verify git commits
if "git commit" in command and (" -n" in command or "--no-verify" in command):
bypass_flag = "-n" if " -n" in command else "--no-verify"
error_message = (
f"🚫 Blocked: Using {bypass_flag} to bypass hooks is not allowed.\n\n"
f"STOP and explain why you're trying to bypass quality checks:\n"
f"- What specific linting errors are you facing?\n"
f"- Have you tried fixing the actual issues?\n"
f"- Why do you think bypassing is necessary?\n\n"
f"Instead of using {bypass_flag}, fix the real issues:\n"
f"1. 🔧 Fix gofmt formatting (run: gofmt -w .)\n"
f"2. 📝 Add periods to comment endings\n"
f"3. 🚀 Address golangci-lint warnings properly\n"
f"4. 🔍 Fix go vet concerns\n\n"
f"Git hooks ensure Go code quality for Strategic Core v2."
)
# Send security alert
bypass_flag = "-n" if " -n" in command else "--no-verify"
send_security_alert(
violation_type=f"Git {bypass_flag} bypass",
command=command,
session_id=session_id,
cwd=cwd,
details=f"Attempted to use {bypass_flag} flag to bypass git hooks"
)
sys.stdout.write(
json.dumps({"decision": "block", "message": error_message}) + "\n"
)
return
# Block modification of linting/quality config files
config_files = [".golangci.yml", ".pre-commit-config.yaml"]
for config_file in config_files:
if any(pattern in command for pattern in [
f"Edit.*{config_file}",
f"Write.*{config_file}",
f"MultiEdit.*{config_file}",
f"vim {config_file}",
f"nano {config_file}",
f"echo.*{config_file}",
f"sed.*{config_file}",
]):
error_message = (
f"🚫 Blocked: Modification of {config_file} is not allowed.\n\n"
f"Instead of modifying quality control files, you should:\n"
f"1. 🔧 Fix the actual code issues (gofmt, periods in comments, etc.)\n"
f"2. 📝 Address linting warnings properly\n"
f"3. 🚀 Write better code that passes quality checks\n\n"
f"If you're trying to bypass quality checks, STOP and explain:\n"
f"- What specific linting issues are you facing?\n"
f"- Why do you think modifying {config_file} is necessary?\n"
f"- What's the proper way to fix the underlying code issues?\n\n"
f"Quality standards exist to maintain Strategic Core v2's excellence."
)
# Send security alert for config modification attempts
send_security_alert(
violation_type="Config file modification attempt",
command=command,
session_id=session_id,
cwd=cwd,
details=f"Attempted to modify {config_file} instead of fixing code issues"
)
sys.stdout.write(
json.dumps({"decision": "block", "message": error_message}) + "\n"
)
return
# Block potentially dangerous Go commands
dangerous_patterns = [
("go mod edit -replace", "🚫 Blocked: Direct go.mod replacement editing"),
(
"go clean -modcache",
"🚫 Blocked: Clearing module cache without confirmation",
),
("rm -rf vendor", "🚫 Blocked: Direct vendor directory removal"),
]
for pattern, message in dangerous_patterns:
if pattern in command:
# Send security alert for dangerous Go commands
send_security_alert(
violation_type="Dangerous Go command",
command=command,
session_id=session_id,
cwd=cwd,
details=f"Attempted to execute potentially harmful command: {pattern}"
)
sys.stdout.write(
json.dumps(
{
"decision": "block",
"message": f"{message}\n\nUse proper Go module commands instead.",
}
)
+ "\n"
)
return
# Warn about potential Go anti-patterns (no alerts, just warnings)
warning_patterns = [
("go get -u all", "⚠️ Warning: Updating all dependencies at once"),
("go build -tags", "⚠️ Warning: Using build tags"),
]
for pattern, message in warning_patterns:
if pattern in command:
log(message, HOOK_NAME)
# Allow all other commands
sys.stdout.write(json.dumps({"decision": "approve"}) + "\n")
except Exception as e:
# On error, allow the command but log the issue
log(f"Hook error: {e}", HOOK_NAME)
log(f"Exception type: {type(e).__name__}", HOOK_NAME)
sys.stdout.write(json.dumps({"decision": "approve"}) + "\n")
if __name__ == "__main__":
main()
#!/usr/bin/env python3
"""
PostToolUse hook to format Go files after Write, Edit, or MultiEdit operations.
This hook automatically runs gofmt and goimports on Go files to ensure
consistent code formatting and imports organization.
"""
import json
import subprocess
import sys
from pathlib import Path
def log(message: str) -> None:
"""Print a message to stderr to avoid interfering with hook output."""
print(f"[format-go-hook] {message}", file=sys.stderr)
def main() -> None:
"""Format Go files after tool operations."""
try:
# Read the hook input from stdin
hook_input = json.loads(sys.stdin.read())
# Extract tool information
tool_name = hook_input.get("tool_name", "")
tool_input = hook_input.get("tool_input", {})
log(f"Processing {tool_name} tool")
# Get the file path based on the tool
file_path = None
if tool_name in ["Write", "Edit", "MultiEdit"]:
file_path = tool_input.get("file_path", "")
if not file_path:
log("No file path found, skipping")
return
# Check if it's a Go file
if not file_path.endswith(".go"):
log(f"Skipping non-Go file: {file_path}")
return
# Get the project directory from environment
project_dir = hook_input.get("cwd", ".")
# Convert to Path object for easier handling
file_path = Path(file_path)
if not file_path.is_absolute():
file_path = Path(project_dir) / file_path
# Check if file exists
if not file_path.exists():
log(f"File does not exist: {file_path}")
return
log(f"Formatting Go file: {file_path}")
# Run gofmt formatter
try:
log("Running gofmt formatter...")
result = subprocess.run(
["gofmt", "-w", str(file_path)],
cwd=project_dir,
capture_output=True,
text=True,
check=False,
)
if result.returncode == 0:
log(f"✓ gofmt formatted {file_path.name}")
else:
log(
f"✗ gofmt failed: {result.stderr.strip() if result.stderr else 'Unknown error'}"
)
except Exception as e:
log(f"✗ gofmt not available: {e}")
# Run goimports for import organization
try:
log("Running goimports...")
result = subprocess.run(
["goimports", "-w", str(file_path)],
cwd=project_dir,
capture_output=True,
text=True,
check=False,
)
if result.returncode == 0:
log(f"✓ goimports organized imports for {file_path.name}")
else:
log(
f"✗ goimports failed: {result.stderr.strip() if result.stderr else 'Unknown error'}"
)
except Exception as e:
log(
f"✗ goimports not available (install: go install golang.org/x/tools/cmd/goimports@latest): {e}"
)
# Run go vet for basic correctness checks
try:
log("Running go vet...")
result = subprocess.run(
["go", "vet", str(file_path)],
cwd=project_dir,
capture_output=True,
text=True,
check=False,
)
if result.returncode == 0:
log(f"✓ go vet: {file_path.name} passed")
else:
log(f"⚠ go vet found issues: {result.stderr.strip()}")
except Exception as e:
log(f"✗ go vet not available: {e}")
log(f"Completed formatting for {file_path.name}")
except Exception as e:
log(f"Hook error: {e}")
if __name__ == "__main__":
main()
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "python3 $CLAUDE_PROJECT_DIR/.claude/hooks/block-skip-hooks.py"
}
]
}
],
"PostToolUse": [
{
"matcher": "Write|Edit|MultiEdit",
"hooks": [
{
"type": "command",
"command": "python3 $CLAUDE_PROJECT_DIR/.claude/hooks/format-go-hook.py"
}
]
}
]
}
}
#!/usr/bin/env python3
"""
Strategic Core notification utilities for Claude Code hooks.
Shared functions for sending ntfy notifications and getting project context
across all Strategic Core Claude hooks.
"""
import os
import subprocess
import sys
from datetime import datetime
from pathlib import Path
from typing import Optional, List
def log(message: str, hook_name: str = "strategic-notifications") -> None:
"""Print a message to stderr to avoid interfering with hook output."""
print(f"[{hook_name}] {message}", file=sys.stderr)
def play_audio(audio_file: str, hook_name: str = "strategic-notifications") -> bool:
"""
Play an audio file using system audio player.
Args:
audio_file: Name of the audio file (e.g., "toasty.mp3", "gutter-trash.mp3")
hook_name: Name of the calling hook (for logging)
Returns:
True if audio played successfully, False otherwise
"""
try:
# Get the path to the assets directory relative to this script
script_dir = Path(__file__).parent
assets_dir = script_dir / "assets"
audio_path = assets_dir / audio_file
if not audio_path.exists():
log(f"Audio file not found: {audio_path}", hook_name)
return False
# Try different audio players based on the system
audio_players = [
["afplay"], # macOS
["mpg123", "-q"], # Linux (quiet mode)
["mpv", "--no-video", "--really-quiet"], # Cross-platform
["ffplay", "-nodisp", "-autoexit", "-loglevel", "quiet"], # ffmpeg
["paplay"], # PulseAudio
]
for player_cmd in audio_players:
try:
# Check if the player is available
result = subprocess.run(
["which", player_cmd[0]],
capture_output=True,
timeout=2
)
if result.returncode == 0:
# Player is available, try to play the audio
cmd = player_cmd + [str(audio_path)]
subprocess.run(
cmd,
capture_output=True,
timeout=5, # Don't let audio play longer than 5 seconds
check=False # Don't raise exception on non-zero exit
)
log(f"♪ Played audio: {audio_file}", hook_name)
return True
except (subprocess.TimeoutExpired, subprocess.CalledProcessError, FileNotFoundError):
continue
log(f"No suitable audio player found for: {audio_file}", hook_name)
return False
except Exception as e:
log(f"Error playing audio {audio_file}: {e}", hook_name)
return False
def send_ntfy_notification(
server_url: str,
topic: str,
message: str,
title: Optional[str] = None,
priority: str = "default",
tags: Optional[List[str]] = None,
hook_name: str = "strategic-notifications"
) -> bool:
"""
Send notification to ntfy server.
Args:
server_url: The ntfy server URL (e.g., "http://localhost:2586")
topic: The topic to send to
message: The notification message
title: Optional notification title
priority: Notification priority (default, low, high, max)
tags: Optional list of tags for the notification
hook_name: Name of the calling hook (for logging)
Returns:
True if notification was sent successfully, False otherwise
"""
try:
headers = []
if title:
headers.extend(["-H", f"Title: {title}"])
if priority != "default":
headers.extend(["-H", f"Priority: {priority}"])
if tags:
headers.extend(["-H", f"Tags: {','.join(tags)}"])
# Add timestamp header
headers.extend(["-H", f"X-Timestamp: {datetime.now().isoformat()}"])
# Construct curl command
cmd = ["curl", "-s", "-f"] + headers + ["-d", message, f"{server_url}/{topic}"]
result = subprocess.run(cmd, capture_output=True, text=True, timeout=10)
if result.returncode == 0:
log(f"✓ Notification sent successfully to {topic}", hook_name)
return True
else:
log(f"✗ Failed to send notification: {result.stderr}", hook_name)
return False
except subprocess.TimeoutExpired:
log("✗ Notification timeout - server may be unreachable", hook_name)
return False
except Exception as e:
log(f"✗ Error sending notification: {e}", hook_name)
log(f"✗ Exception type: {type(e).__name__}", hook_name)
log(f"✗ Exception details: {str(e)}", hook_name)
return False
def get_project_context(cwd: str, hook_name: str = "strategic-notifications") -> dict:
"""
Get context about the current project.
Args:
cwd: Current working directory path
hook_name: Name of the calling hook (for logging)
Returns:
Dictionary with project information including name, type, and git status
"""
try:
project_path = Path(cwd)
project_name = project_path.name
# Check if it's a Go project
is_go_project = (project_path / "go.mod").exists()
# Check if it's a Python project
is_python_project = any((project_path / f).exists() for f in ["pyproject.toml", "setup.py", "requirements.txt"])
# Check if it's a Node.js project
is_node_project = (project_path / "package.json").exists()
# Determine project type
if is_go_project:
project_type = "Go project"
elif is_python_project:
project_type = "Python project"
elif is_node_project:
project_type = "Node.js project"
else:
project_type = "project"
# Check git status if available
git_status = "unknown"
try:
result = subprocess.run(
["git", "status", "--porcelain"],
cwd=cwd,
capture_output=True,
text=True,
timeout=5
)
if result.returncode == 0:
changes = len(result.stdout.strip().split('\n')) if result.stdout.strip() else 0
git_status = f"{changes} changes" if changes > 0 else "clean"
except (subprocess.TimeoutExpired, subprocess.CalledProcessError, Exception) as e:
log(f"⚠ Git status check failed: {type(e).__name__}: {e}", hook_name)
# Get current git branch if available
git_branch = "unknown"
try:
result = subprocess.run(
["git", "branch", "--show-current"],
cwd=cwd,
capture_output=True,
text=True,
timeout=5
)
if result.returncode == 0:
git_branch = result.stdout.strip() or "main"
except (subprocess.TimeoutExpired, subprocess.CalledProcessError, Exception):
pass
return {
"project_name": project_name,
"project_type": project_type,
"git_status": git_status,
"git_branch": git_branch,
"path": str(project_path),
"is_go_project": is_go_project,
"is_python_project": is_python_project,
"is_node_project": is_node_project
}
except Exception as e:
log(f"✗ Error getting project context: {e}", hook_name)
log(f"✗ Exception type: {type(e).__name__}", hook_name)
log(f"✗ Exception details: {str(e)}", hook_name)
return {
"project_name": "unknown",
"project_type": "project",
"git_status": "unknown",
"git_branch": "unknown",
"path": cwd,
"is_go_project": False,
"is_python_project": False,
"is_node_project": False
}
def get_strategic_core_config() -> dict:
"""
Get Strategic Core specific configuration.
Returns:
Dictionary with ntfy server settings and topics
"""
return {
"ntfy_server": "http://nas1-oryx:2586",
"dev_topic": "strategic-core-dev",
"notification_topic": "strategic-core-notifications",
"alert_topic": "strategic-core-alerts"
}
def format_message_header(project_info: dict, session_id: str) -> str:
"""
Format a standard message header for Strategic Core notifications.
Args:
project_info: Project context from get_project_context()
session_id: Claude Code session ID
Returns:
Formatted header string
"""
return f"""**Project**: {project_info['project_name']} ({project_info['project_type']})
**Branch**: {project_info['git_branch']}
**Git Status**: {project_info['git_status']}
Session ID: {session_id[:8]}...
Path: {os.path.basename(project_info['path'])}
Time: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"""
def send_strategic_notification(
message: str,
title: str,
priority: str = "default",
tags: Optional[List[str]] = None,
topic_type: str = "dev",
project_info: Optional[dict] = None,
session_id: str = "unknown",
hook_name: str = "strategic-notifications"
) -> bool:
"""
Send a Strategic Core notification with standard formatting.
Args:
message: The main notification message
title: Notification title
priority: Notification priority (default, low, high, max)
tags: Optional list of tags
topic_type: Type of topic (dev, notifications, alerts)
project_info: Project context (if None, will be detected from cwd)
session_id: Claude Code session ID
hook_name: Name of the calling hook
Returns:
True if notification was sent successfully, False otherwise
"""
config = get_strategic_core_config()
# Select topic based on type
topic_map = {
"dev": config["dev_topic"],
"notifications": config["notification_topic"],
"alerts": config["alert_topic"]
}
topic = topic_map.get(topic_type, config["dev_topic"])
# Add strategic-core tag if not present
if tags is None:
tags = ["strategic-core"]
elif "strategic-core" not in tags:
tags.append("strategic-core")
# Format message with header if project info is provided
if project_info:
header = format_message_header(project_info, session_id)
formatted_message = f"Strategic Core v2 Development\n\n{header}\n\n**Message**: {message}"
else:
formatted_message = f"Strategic Core v2: {message}"
return send_ntfy_notification(
server_url=config["ntfy_server"],
topic=topic,
message=formatted_message,
title=title,
priority=priority,
tags=tags,
hook_name=hook_name
)
@dgnsrekt
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment