Quality checks for different project types.
# React/Next.js/Vite apps .claude/hooks/react-app/ # VS Code extensions .claude/hooks/vscode-extension/ # Node.js TypeScript projects .claude/hooks/node-typescript/ # More coming soon...
Add to .claude/settings.local.json
and customize "command" quality-check.js path according to your project type (react-app, vscode-extension, node-typescript):
{ "hooks": { "PostToolUse": [ { "matcher": "Write|Edit|MultiEdit", "hooks": [ { "type": "command", "command": "node $CLAUDE_PROJECT_DIR/.claude/hooks/react-app/quality-check.js" } ] } ] } }
Hook runs automatically when you edit files.
- Catches errors before runtime
- Uses project-specific TypeScript configurations
- Auto-fixes trivial issues
- Project-aware rules (React hooks allow console.log, VS Code extensions don't)
- Immediate feedback during editing
- Prevents regressions
- Console allowed in components
- 'as any' warnings (not errors)
- JSX support
- Console blocked in extension code
- Console allowed in webview
- Strict TypeScript
- Console allowed (acceptable for CLI tools)
- 'as any' warnings (not errors)
- Optimized for server/client transport code
Each hook has hook-config.json
:
{ "typescript": { "enabled": true, "showDependencyErrors": false, "jsx": "react" // For React hooks }, "eslint": { "enabled": true, "autofix": true }, "prettier": { "enabled": true, "autofix": true }, "rules": { "console": { "severity": "info|warning|error", "allowIn": { "paths": ["src/components/"], "fileTypes": ["component", "test"] } } } }
# Disable specific checks export CLAUDE_HOOKS_PRETTIER_ENABLED=false # Enable debug mode export CLAUDE_HOOKS_DEBUG=true # Show dependency errors (not recommended) export CLAUDE_HOOKS_SHOW_DEPENDENCY_ERRORS=true
# Test manually echo '{"tool_name":"Edit","tool_input":{"file_path":"src/App.tsx"}}' | node .claude/hooks/react-app/quality-check.js # Run with debug output export CLAUDE_HOOKS_DEBUG=true echo '{"tool_name":"Edit","tool_input":{"file_path":"src/App.tsx"}}' | node .claude/hooks/react-app/quality-check.js
- Copy existing hook folder
- Edit
hook-config.json
- Customize rules as needed
-
Exit 0: All checks passed
- File quality verified
- Auto-fixes applied if enabled
- Hook output suppressed by Claude Code
-
Exit 2: Issues found
- TypeScript compilation errors
- ESLint errors that couldn't be auto-fixed
- Prettier issues (if auto-fix disabled)
- Full error report shown to agent
When running via Claude Code hooks:
- Exit 0: Silent success (no output shown)
- Exit 2: Full error report shown to user
When testing manually:
- All
[INFO]
,[OK]
,[WARN]
messages visible - Useful for debugging hook behavior
graph TD A[File Edit Detected] --> B[Hook Triggered] B --> C{Parse Tool Input} C --> D[Extract File Path] D --> E[Load Configuration] E --> F[Smart Cache Check] F --> G{Config Changed?} G -->|Yes| H[Rebuild TS Mappings] G -->|No| I[Use Cached Mappings] H --> J[Run Quality Checks] I --> J J --> K[TypeScript Check] J --> L[ESLint Check] J --> M[Prettier Check] J --> N[Common Issues Check] K --> O{Has Errors?} L --> O M --> O N --> O O -->|Yes| P[Exit Code 2 - Block] O -->|No| Q[Exit Code 0 - Pass]
The hook uses a caching system to handle complex TypeScript configurations:
1. Config Discovery
- Finds all
tsconfig*.json
files in your project - Sorts by specificity (specific configs like
tsconfig.webview.json
take precedence) - Builds a mapping of file patterns to configs
2. SHA256 Change Detection
// Each config file gets a SHA256 hash const hash = crypto.createHash('sha256').update(configFileContent).digest('hex');
This creates a unique fingerprint for each config:
tsconfig.json
:c70342f6265640de2ba06a522870b4dc...
tsconfig.webview.json
:55e05e47dc57fbdab2a2d30704f9ab1f...
3. Cache Structure
{ "hashes": { "tsconfig.json": "c70342f6265640de2ba06a522870b4dc1a4737818abe862c41108014cf442735", "tsconfig.webview.json": "55e05e47dc57fbdab2a2d30704f9ab1f6d9f312ee7c14e83b7a3613e73b4a230" }, "mappings": { "src/webview/**/*": { "configPath": "tsconfig.webview.json", "excludes": ["node_modules", "test"] }, "src/protocol/**/*": { "configPath": "tsconfig.webview.json", "excludes": ["node_modules", "test"] }, "src/**/*": { "configPath": "tsconfig.test.json", "excludes": ["node_modules", "gui"] } } }
4. Cache Lifecycle
graph LR A[Hook Starts] --> B{Cache Exists?} B -->|No| C[Build Cache] B -->|Yes| D[Load Cache] D --> E{Verify SHA256 Hashes} E -->|Changed| F[Rebuild Affected Mappings] E -->|Same| G[Use Cached Mappings] F --> H[Update Cache File] C --> H G --> I[Run Checks] H --> I
5. Performance & Benefits
- First run: ~100-200ms to build cache
- Subsequent runs: <5ms to verify hashes using SHA256
- Config change: Only rebuilds affected mappings
- Result: 95%+ faster on repeated runs
- SHA256 ensures cache validity (cryptographically secure, collision resistant)
- Each hook maintains its own cache
- Skip expensive file system operations
Why manual hashing vs TypeScript project references?
Manual SHA256 hashing provides <5ms cache lookups compared to 100-500ms for tsc
incremental checks. The cache maintains reference maps to actual config paths while delivering superior performance for real-time editing scenarios.
1. TypeScript Compilation
- Uses the correct config for the edited file
- Only shows errors for the edited file (not dependencies)
- Respects JSX settings from config
2. ESLint Integration
- Auto-fixes issues when possible
- Re-runs after fixes to verify
- Respects project ESLint config
3. Prettier Formatting
- Auto-formats on save
- Silent when successful
- Uses project Prettier config
4. Common Issues Detection
- Console usage (configurable per project type)
as any
usage (error vs warning)- TODO/FIXME comments (informational)
Cache issues?
# Clear the hook's cache rm .claude/hooks/react-app/tsconfig-cache.json
Hook not running?
# Check if executable chmod +x .claude/hooks/*/quality-check.js
Check individual hook folders for more details.