Appearance
rector-tidy.sh
.claude/hooks/rector-tidy.sh
Stop
After the agent finishes, standardise then format the PHP it touched: Rector (structural) first, then Pint (formatting), mirroring composer tidy. Scoped to git-dirty PHP files so it stays fast, and a safety net behind the agent's own composer tidy habit + the CI rector:check gate (the real enforcement — see composer.json). FAILS OPEN: no dirty PHP, no rector binary, or any error → exit 0. Never blocks the stop (we only tidy; we never reject), so it can't loop.
Source
bash
#!/usr/bin/env bash
#
# Stop hook — after the agent finishes, standardise then format the PHP it
# touched: Rector (structural) first, then Pint (formatting), mirroring
# `composer tidy`. Scoped to git-dirty PHP files so it stays fast, and a
# safety net behind the agent's own `composer tidy` habit + the CI
# `rector:check` gate (the real enforcement — see composer.json).
#
# FAILS OPEN: no dirty PHP, no rector binary, or any error → exit 0. Never
# blocks the stop (we only tidy; we never reject), so it can't loop.
set -uo pipefail
cd "${CLAUDE_PROJECT_DIR:-.}" || exit 0
[ -x vendor/bin/rector ] || exit 0
# Dirty + untracked PHP files inside Rector's configured roots, existing only
# (skip deletions/renames-away).
mapfile -t files < <(
git status --porcelain -- '*.php' 2>/dev/null \
| sed 's/^...//' \
| grep -E '^(app|bootstrap|config|database|routes|tests)/' \
| while read -r f; do [ -f "$f" ] && echo "$f"; done
)
[ "${#files[@]}" -gt 0 ] || exit 0
vendor/bin/rector process "${files[@]}" --no-progress-bar >/dev/null 2>&1 || true
vendor/bin/pint --dirty >/dev/null 2>&1 || true
exit 0