auto-format
PostToolUseA PostToolUse hook that automatically formats code after file edits to maintain consistent code style across your project.
Features
Installation
1. Copy hook to your project
cp -r hooks/auto-format/ /your-project/.claude/hooks/auto-format/
2. Add to settings.json
{
"hooks": {
"PostToolUse": [{
"matcher": "Edit|Write",
"hooks": [{
"type": "command",
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/auto-format/hook.sh"
}]
}]
}
}
3. Make executable
chmod +x .claude/hooks/auto-format/hook.sh
A PostToolUse hook that automatically formats code after file edits to maintain consistent code style across your project.
Features
- Auto-detects file type (JavaScript, TypeScript, Python, Ruby, Go, Rust, etc.)
- Runs appropriate formatter (Prettier, Black, RuboCop, gofmt, rustfmt, etc.)
- Auto-applies formatting changes
- Reports what was formatted
- Supports custom formatter configurations
- Respects .prettierignore, .eslintignore, etc.
- Works with multiple formatters in monorepos
- Configurable formatting rules
- Optional auto-commit of formatting changes
Configuration
Add this to your .claude/settings.json:
{
"hooks": {
"PostToolUse": [
{
"pattern": "^(Edit|Write)$",
"command": "/absolute/path/to/hooks/auto-format/hook.sh",
"description": "Auto-format code after file edits"
}
]
}
}
Installation
1. Install Formatters
Install formatters for your language(s):
# JavaScript/TypeScript - Prettier
npm install --save-dev prettier
# Python - Black
pip install black
# Ruby - RuboCop
gem install rubocop
# Go - gofmt (built-in with Go)
# No installation needed
# Rust - rustfmt (built-in with Rust)
# No installation needed
# PHP - PHP-CS-Fixer
composer require --dev friendsofphp/php-cs-fixer
# Java - google-java-format
# Download from https://github.com/google/google-java-format
# C/C++ - clang-format
# macOS: brew install clang-format
# Linux: apt-get install clang-format
2. Set Up the Hook
# Copy the hook to your project
cp hook.sh /path/to/your/project/.claude/hooks/auto-format.sh
chmod +x /path/to/your/project/.claude/hooks/auto-format.sh
# Update .claude/settings.json with the correct path
3. Configure Formatters (Optional)
Create formatter configuration files:
# Prettier (.prettierrc)
{
"semi": true,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "es5"
}
# Black (pyproject.toml)
[tool.black]
line-length = 88
target-version = ['py39']
# RuboCop (.rubocop.yml)
AllCops:
NewCops: enable
TargetRubyVersion: 3.0
# EditorConfig (.editorconfig)
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
4. Configure Hook Behavior
Create a .formatconfig file in your project root:
# .formatconfig - Auto-format configuration
# Enable auto-formatting
AUTO_FORMAT=true
# Auto-commit formatted files
AUTO_COMMIT_FORMAT=false
# Show formatting changes
SHOW_CHANGES=true
# Formatters to use (comma-separated)
# Leave empty to auto-detect
FORMATTERS=
# File patterns to format (comma-separated)
# Leave empty to format all supported files
FORMAT_PATTERNS=
# File patterns to exclude
EXCLUDE_PATTERNS="*.min.js,*.bundle.js,dist/*,build/*"
# Verbose output
VERBOSE=false
Usage
The hook runs automatically after you edit or create files:
π¨ Auto-Format Running...
ββββββββββββββββββββββββββββββββββββββββββββββββββββ
π Formatting file: src/components/Header.tsx
β
Formatted with Prettier
- Fixed 3 formatting issues
- Added missing semicolons
- Adjusted indentation
ββββββββββββββββββββββββββββββββββββββββββββββββββββ
Changes:
src/components/Header.tsx
- const name = "App"
+ const name = 'App';
ββββββββββββββββββββββββββββββββββββββββββββββββββββ
β¨ Formatting complete!
ββββββββββββββββββββββββββββββββββββββββββββββββββββ
Supported Formatters
JavaScript/TypeScript
- Prettier - Opinionated code formatter
- ESLint - With βfix flag
- Standard - JavaScript Standard Style
Python
- Black - The uncompromising formatter
- autopep8 - PEP 8 compliance
- yapf - Yet Another Python Formatter
- isort - Import sorting
Ruby
- RuboCop - Ruby static code analyzer
- Prettier (Ruby plugin) - Alternative formatter
Go
- gofmt - Official Go formatter
- goimports - Manages imports and formats
Rust
- rustfmt - Official Rust formatter
PHP
- PHP-CS-Fixer - PHP Coding Standards Fixer
- PHP_CodeSniffer - With phpcbf
Java
- google-java-format - Googleβs Java formatter
- prettier-java - Prettier plugin for Java
C/C++
- clang-format - LLVM code formatter
CSS/SCSS/Less
- Prettier - Supports CSS and preprocessors
- stylelint - With βfix flag
HTML
- Prettier - HTML formatting
- js-beautify - Multi-language beautifier
JSON/YAML/Markdown
- Prettier - Supports multiple formats
Customization
Formatter Selection
Override automatic detection:
# In .formatconfig
# Force specific formatters for file types
JS_FORMATTER=prettier
PYTHON_FORMATTER=black
RUBY_FORMATTER=rubocop
Custom Formatting Rules
Each formatter respects its own configuration:
// .prettierrc.js
module.exports = {
printWidth: 100,
tabWidth: 2,
useTabs: false,
semi: true,
singleQuote: true,
quoteProps: 'as-needed',
trailingComma: 'all',
bracketSpacing: true,
arrowParens: 'always',
};
# pyproject.toml
[tool.black]
line-length = 100
target-version = ['py39', 'py310']
include = '\.pyi?$'
exclude = '''
/(
\.git
| \.venv
| build
| dist
)/
'''
Selective Formatting
Format only specific file types:
# In .formatconfig
FORMAT_PATTERNS="*.js,*.ts,*.jsx,*.tsx"
# Or in hook.sh
case "$extension" in
js|ts|jsx|tsx)
format_with_prettier "$file"
;;
py)
format_with_black "$file"
;;
esac
Pre/Post Format Hooks
Add custom logic before or after formatting:
# In hook.sh
pre_format_hook() {
local file=$1
# Custom logic before formatting
# e.g., backup file, run linter, etc.
}
post_format_hook() {
local file=$1
# Custom logic after formatting
# e.g., validate syntax, notify user, etc.
}
Auto-Commit Formatted Changes
Automatically commit formatting changes:
# In .formatconfig
AUTO_COMMIT_FORMAT=true
# Hook will create commits like:
# "chore: auto-format src/components/Header.tsx"
Best Practices
1. Team Consistency
Share formatter configs in repository:
# Commit formatter configs
git add .prettierrc .editorconfig
git commit -m "Add formatter configuration"
# Team members use same formatting rules
2. Editor Integration
Use formatter in your editor too:
// VS Code settings.json
{
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"[python]": {
"editor.defaultFormatter": "ms-python.black-formatter"
}
}
3. Pre-Commit Hooks
Combine with pre-commit for double safety:
# .pre-commit-config.yaml
repos:
- repo: https://github.com/pre-commit/mirrors-prettier
rev: v3.0.0
hooks:
- id: prettier
- repo: https://github.com/psf/black
rev: 23.1.0
hooks:
- id: black
4. Ignore Generated Files
Donβt format generated or minified files:
# .prettierignore
dist/
build/
coverage/
*.min.js
*.bundle.js
node_modules/
5. Format on Save
Configure auto-format to run immediately:
# In .formatconfig
# Run immediately after edit (no delay)
FORMAT_DELAY=0
6. Gradual Adoption
Introduce formatting gradually:
# Phase 1: Format new files only
FORMAT_NEW_FILES_ONLY=true
# Phase 2: Format changed files
FORMAT_CHANGED_FILES=true
# Phase 3: Format entire codebase
npm run format:all
7. Review Changes
Always review formatting changes:
# In .formatconfig
SHOW_CHANGES=true
# Shows diff of formatting changes
# Review before committing
8. Performance Optimization
For large files or projects:
# Format only changed lines (if supported)
# Prettier doesn't support this, but some formatters do
# Cache formatting results
# Skip files that haven't changed
# Run formatters in parallel
PARALLEL_FORMAT=true
Advanced Features
Multi-Formatter Support
Use different formatters in a monorepo:
detect_formatter() {
local file=$1
# Check package.json for formatter
if [[ -f "package.json" ]] && grep -q "prettier" package.json; then
echo "prettier"
elif [[ -f "pyproject.toml" ]] && grep -q "black" pyproject.toml; then
echo "black"
else
# Default by extension
detect_by_extension "$file"
fi
}
Conditional Formatting
Format based on context:
should_format() {
local file=$1
# Don't format files in certain directories
if [[ "$file" =~ (vendor|node_modules|dist)/ ]]; then
return 1
fi
# Don't format during CI
if [[ -n "$CI" ]]; then
return 1
fi
# Don't format generated files
if head -n 1 "$file" | grep -q "auto-generated"; then
return 1
fi
return 0
}
Format Validation
Verify formatting without applying:
# Check if file needs formatting
npx prettier --check src/**/*.js
# Exit code 1 if formatting needed
# Exit code 0 if already formatted
Formatting Metrics
Track formatting statistics:
# Log formatting activity
echo "$(date): Formatted $file" >> .format.log
# Count formatting changes
git diff --numstat | awk '{sum+=$1+$2} END {print sum}'
Smart Formatting
Only format changed lines:
# For Git-aware formatters
# prettier --changed (experimental)
# Or use git diff to identify changed lines
# and format only those sections
Troubleshooting
Formatter Not Found
Install the formatter:
# Check if formatter is installed
which prettier
which black
# Install if missing
npm install -g prettier
pip install black
Configuration Not Respected
Verify config files are:
- In correct location (project root)
- Named correctly (.prettierrc, pyproject.toml, etc.)
- Valid format (JSON, YAML, TOML)
- Not being overridden
Formatting Conflicts
When multiple formatters conflict:
# Choose one formatter per file type
# Disable conflicting formatters
# Or run formatters in specific order
Performance Issues
For slow formatting:
# Format only changed files
# Use faster formatters
# Disable some checks
# Run formatters in parallel
Hook Not Running
Check:
- Hook is in PostToolUse section
-
Pattern matches: β^(Edit Write)$β - Script is executable
- Path is correct
Example Configurations
JavaScript Project
// .prettierrc
{
"semi": true,
"singleQuote": true,
"tabWidth": 2,
"printWidth": 100
}
# .formatconfig
AUTO_FORMAT=true
FORMATTERS=prettier
SHOW_CHANGES=true
Python Project
# pyproject.toml
[tool.black]
line-length = 88
target-version = ['py39']
[tool.isort]
profile = "black"
# .formatconfig
AUTO_FORMAT=true
FORMATTERS=black,isort
SHOW_CHANGES=true
Multi-Language Project
# .formatconfig
AUTO_FORMAT=true
# Auto-detect formatters
FORMATTERS=
SHOW_CHANGES=true
EXCLUDE_PATTERNS="*.min.js,dist/*,vendor/*"
Integration with CI/CD
Verify formatting in CI:
# .github/workflows/format-check.yml
name: Format Check
on: [push, pull_request]
jobs:
format:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Check Formatting
run: |
npx prettier --check "src/**/*.{js,ts}"
black --check .
License
MIT License - Use freely in your projects