Skip to content

Instantly share code, notes, and snippets.

@jamesaphoenix
Created August 6, 2025 09:12
Show Gist options
  • Select an option

  • Save jamesaphoenix/b669f068b65cb19f4611775a6f383027 to your computer and use it in GitHub Desktop.

Select an option

Save jamesaphoenix/b669f068b65cb19f4611775a6f383027 to your computer and use it in GitHub Desktop.
#!/bin/bash
set -e
# Claude Code PostToolUse hook for formatting and linting
# Reads JSON from stdin and runs formatting on edited files
INPUT=$(cat)
# Debug: show what we received
echo "Received input: $INPUT" >&2
# Check if input is valid JSON
if ! echo "$INPUT" | jq empty 2>/dev/null; then
echo "Error: Invalid JSON input" >&2
exit 1
fi
TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // empty')
# Security: Validate file paths to prevent path traversal and skip sensitive files
is_safe_file() {
local FILE="$1"
# Check for path traversal
if [[ "$FILE" == *".."* ]]; then
echo "Skipping potentially unsafe path: $FILE" >&2
return 1
fi
# Skip sensitive files
case "$FILE" in
*.env*|*.key|*.pem|*.p12|*.pfx|.git/*|node_modules/*|*/node_modules/*)
echo "Skipping sensitive file: $FILE" >&2
return 1
;;
*)
return 0
;;
esac
}
# Helper function to format a single file
format_file() {
local FILE="$1"
if ! is_safe_file "$FILE"; then
return 0
fi
if [[ ! -f "$FILE" ]]; then
echo "File does not exist: $FILE" >&2
return 0
fi
echo "Formatting file: $FILE" >&2
# Run formatting based on file type
case "$FILE" in
*.ts|*.tsx|*.js|*.jsx|*.json|*.md|*.yaml|*.yml)
pnpm run lint:format:fix >/dev/null 2>&1 || {
echo "Warning: Failed to format $FILE" >&2
return 0
}
;;
*)
echo "Skipping unsupported file type: $FILE" >&2
;;
esac
}
# Process different tool types
case "$TOOL_NAME" in
"Write"|"Edit"|"MultiEdit")
echo "Processing $TOOL_NAME operation..." >&2
# Extract file path(s) from different tool structures
if [[ "$TOOL_NAME" == "MultiEdit" ]]; then
# MultiEdit has multiple files in an array
echo "$INPUT" | jq -r '.tool_input.files[]?.file_path // empty' | while read -r FILE; do
if [[ -n "$FILE" ]]; then
format_file "$FILE"
fi
done
else
# Write/Edit have single file_path
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // .tool_input.target_file // empty')
if [[ -n "$FILE_PATH" ]]; then
format_file "$FILE_PATH"
fi
fi
;;
*)
echo "Skipping non-file operation: $TOOL_NAME" >&2
;;
esac
echo "Format and lint hook completed" >&2
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment