Skip to content

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