Files
siftlode/scripts/lint-changed.sh
T
peter d1bbe4524c fix(gate): close the gaps in the plan-code guard, the button lists and the tooling
- planCodes: the quote and slash lookarounds let the commonest form through (a docstring or test
  title opening with a code, a slash pair); the two real exceptions (an episode titled "E1", ruff's
  E4/E7/E9) are now named and cut first, with cases that fail on the old pattern. audit_metrics
  counts by the same definition.
- button-primitive: both file lists live in the rule body, out of sight of the snapshot and the
  register; each is now pinned to its exact set of ui/ files (red when either is widened).
- check.sh --brief shows a short log whole and keeps the line before each hit, so ruff's rule and
  eslint's file path survive the excerpt.
- lint-changed runs prettier over every changed frontend file, as the gate does, not only code.
- CLAUDE.md and gates.md no longer claim the basedpyright baseline was found harmless: it was never
  triaged, and the reportOptional* entries are the likeliest real bugs.
2026-10-05 00:19:50 +02:00

65 lines
2.6 KiB
Bash
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
# The fast lint lanes on the uncommitted files only — ruff (check + format) on Python, eslint on the
# frontend's code and prettier on all of the frontend — for a check after every edit instead of a
# full gate run. It is a convenience, never the gate: scripts/check.sh stays what a merge or a
# release requires.
#
# bash scripts/lint-changed.sh lint what `git status` shows as changed or new
# bash scripts/lint-changed.sh --hook the same, as a Claude Code Stop hook: silent and exit 0 when
# clean; otherwise the findings on stderr and exit 2, which
# hands them back to the agent before it ends its turn
#
# Measured 2026-10-04: one cold eslint run (type-aware) takes 3.4–4.7 s, so this runs once per turn
# (Stop), not after every edit — a warm daemon (eslint_d) would add a dependency and a stray process.
set -u
cd "$(dirname "$0")/.." || exit 2
hook=0
[ "${1:-}" = --hook ] && hook=1
if [ "$hook" = 1 ]; then
# A Stop hook that blocked once is told so on the next stop; let that one through, or an unfixable
# finding would keep the agent in a loop.
grep -q '"stop_hook_active": *true' && exit 0
fi
mapfile -t changed < <(git status --porcelain --untracked-files=all | sed -E 's/^.. //; s/.* -> //' |
while read -r f; do [ -f "$f" ] && printf '%s\n' "$f"; done)
# eslint reads code; prettier reads every file the gate's `prettier --check .` does (a locale JSON,
# a stylesheet), and `--ignore-unknown` passes over the ones it has no parser for.
py=() fe=() pretty=()
for f in "${changed[@]}"; do
case "$f" in
backend/*.py | scripts/*.py) py+=("$f") ;;
esac
case "$f" in
frontend/*.ts | frontend/*.tsx | frontend/*.js | frontend/*.mjs) fe+=("${f#frontend/}") ;;
esac
case "$f" in
frontend/*) pretty+=("${f#frontend/}") ;;
esac
done
report="$(
if [ ${#py[@]} -gt 0 ]; then
python -m ruff check --quiet --config backend/ruff.toml "${py[@]}" 2>&1
python -m ruff format --check --quiet --config backend/ruff.toml "${py[@]}" 2>&1
fi
if [ ${#fe[@]} -gt 0 ]; then
(cd frontend && npx eslint --max-warnings 0 "${fe[@]}" 2>&1)
fi
if [ ${#pretty[@]} -gt 0 ]; then
(cd frontend && npx prettier --check --ignore-unknown --log-level warn "${pretty[@]}" 2>&1)
fi
)"
if [ -z "$report" ]; then
[ "$hook" = 1 ] || echo "lint-changed: clean (${#py[@]} py, ${#pretty[@]} frontend)"
exit 0
fi
if [ "$hook" = 1 ]; then
printf 'lint-changed found problems in the uncommitted files:\n%s\n' "$report" >&2
exit 2
fi
printf '%s\n' "$report"
exit 1