chore: add Claude Code PostToolUse syntax-check hook

.claude/settings.json — hook fires after every Edit/Write:
- .js files  → node --check
- .py files  → py_compile.compile (doraise=True)
- exit 2 on failure → Claude sees the error immediately and fixes it

Scripts:
- .claude/hooks/syntax_check.ps1  (Windows, primary)
- .claude/hooks/syntax_check.sh   (bash fallback)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
wasrusgen 2026-05-16 11:01:34 +03:00
parent f64a64e834
commit 2479ac05cf
3 changed files with 118 additions and 0 deletions

View File

@ -0,0 +1,41 @@
# ================================================================
# Claude Code — PostToolUse hook (Windows PowerShell fallback)
# Syntax check after every Edit / Write tool call.
# Exit code 2 = Claude sees the error and can fix immediately.
# ================================================================
param()
# Read file_path from CLAUDE_TOOL_INPUT env var (JSON)
$filePath = ""
try {
$input_json = $env:CLAUDE_TOOL_INPUT | ConvertFrom-Json -ErrorAction Stop
$filePath = $input_json.file_path
} catch {}
if (-not $filePath -or -not (Test-Path $filePath)) { exit 0 }
$ext = [System.IO.Path]::GetExtension($filePath).TrimStart('.')
switch ($ext) {
"js" {
$result = & node --check $filePath 2>&1
if ($LASTEXITCODE -ne 0) {
Write-Output "❌ JS SYNTAX ERROR: $filePath"
Write-Output $result
exit 2
}
}
"py" {
$escaped = $filePath -replace "'", "''"
$result = & python -c "import py_compile,sys; py_compile.compile('$escaped', doraise=True)" 2>&1
if ($LASTEXITCODE -ne 0) {
Write-Output "❌ PY SYNTAX ERROR: $filePath"
Write-Output $result
exit 2
}
}
default { exit 0 }
}
exit 0

View File

@ -0,0 +1,62 @@
#!/usr/bin/env bash
# =============================================================
# Claude Code — PostToolUse hook: syntax check after Edit/Write
# Reads tool input JSON from stdin, extracts file_path,
# runs language-appropriate syntax check.
# Exit 2 → Claude sees the error and can fix it immediately.
# =============================================================
set -euo pipefail
# Parse file_path from JSON stdin (jq if available, else python fallback)
if command -v jq &>/dev/null; then
FILE_PATH=$(echo "$CLAUDE_TOOL_INPUT" | jq -r '.file_path // empty' 2>/dev/null || true)
else
FILE_PATH=$(python3 -c "
import sys, json
try:
d = json.loads(sys.stdin.read())
print(d.get('file_path', ''))
except Exception:
pass
" <<< "$CLAUDE_TOOL_INPUT" 2>/dev/null || true)
fi
# Also try env var set by Claude Code
FILE_PATH="${FILE_PATH:-${CLAUDE_TOOL_INPUT_FILE_PATH:-}}"
if [[ -z "$FILE_PATH" || ! -f "$FILE_PATH" ]]; then
exit 0
fi
EXT="${FILE_PATH##*.}"
case "$EXT" in
js)
if ! OUTPUT=$(node --check "$FILE_PATH" 2>&1); then
echo "❌ JS SYNTAX ERROR: $FILE_PATH"
echo "$OUTPUT"
exit 2
fi
;;
py)
if ! OUTPUT=$(python3 -c "
import py_compile, sys
try:
py_compile.compile('$FILE_PATH', doraise=True)
except py_compile.PyCompileError as e:
print(e)
sys.exit(1)
" 2>&1); then
echo "❌ PY SYNTAX ERROR: $FILE_PATH"
echo "$OUTPUT"
exit 2
fi
;;
*)
# Остальные типы файлов — пропускаем
exit 0
;;
esac
exit 0

15
.claude/settings.json Normal file
View File

@ -0,0 +1,15 @@
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "powershell -NonInteractive -File \".claude\\hooks\\syntax_check.ps1\""
}
]
}
]
}
}