diff --git a/.claude/settings.json b/.claude/settings.json index b94e87b..5cd38a8 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -5,5 +5,18 @@ "Bash(env)", "mcp__outline__read_document" ] + }, + "hooks": { + "PostToolUse": [ + { + "matcher": "Edit|Write|MultiEdit", + "hooks": [ + { + "type": "command", + "command": "f=$(jq -r '.tool_input.file_path // empty'); case \"$f\" in *.rs) rustfmt --edition 2024 \"$f\" 2>/dev/null || true;; esac" + } + ] + } + ] } } diff --git a/.githooks/pre-commit b/.githooks/pre-commit index d3969e8..dd96bb3 100755 --- a/.githooks/pre-commit +++ b/.githooks/pre-commit @@ -1,7 +1,13 @@ #!/bin/sh -# Refuses a commit whose Rust is not as rustfmt writes it. Clippy is left to the checks before a -# push (CLAUDE.md, Tests): it is a build, and Tower's builds go one at a time. +# Refuses a commit whose Rust is not as rustfmt writes it, or that clippy warns about. Clippy only +# checks and never links, so it is light beside a test build (CLAUDE.md, Tests); with the line-tables +# profile it takes about a minute after a change and seconds otherwise. git diff --cached --name-only --diff-filter=ACM | grep -q '\.rs$' || exit 0 -cargo fmt --all --check >/dev/null 2>&1 && exit 0 -echo "pre-commit: the Rust is not formatted; run cargo fmt --all" >&2 -exit 1 +if ! cargo fmt --all --check >/dev/null 2>&1; then + echo "pre-commit: the Rust is not formatted; run cargo fmt --all" >&2 + exit 1 +fi +if ! cargo clippy --workspace --all-targets -q -- -D warnings; then + echo "pre-commit: clippy has warnings; fix them, or allow one where it is wrong, with a comment saying why" >&2 + exit 1 +fi diff --git a/CLAUDE.md b/CLAUDE.md index ffe1b77..6e6e8c5 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -209,8 +209,9 @@ session was stopped. So: line tables and dependencies no debug info; keep it that way, and look at `MemAvailable` beside the load. -The Rust is formatted by rustfmt (`rustfmt.toml`) and kept free of clippy's warnings. A clone -turns on the hook once: `git config core.hooksPath .githooks`; and `git config blame.ignoreRevsFile +The Rust is formatted by rustfmt (`rustfmt.toml`) and kept free of clippy's warnings. Claude Code +formats each `.rs` file it edits (a hook in `.claude/settings.json`), and the pre-commit hook refuses +a commit touching Rust that is unformatted or has a clippy warning. A clone turns on the hook once: `git config core.hooksPath .githooks`; and `git config blame.ignoreRevsFile .git-blame-ignore-revs` keeps blame past the commit that first formatted everything. A clippy suggestion is not always the same code: on adopting it, `fold(false, |any, id| remove(id) || any)` was offered as `any(...)`, which stops at the first and would have removed one feed of many. A lint