Skip to content

Add prek pre-commit hooks#93

Merged
javdl merged 1 commit intomainfrom
add-prek
Feb 25, 2026
Merged

Add prek pre-commit hooks#93
javdl merged 1 commit intomainfrom
add-prek

Conversation

@javdl
Copy link
Contributor

@javdl javdl commented Feb 24, 2026

Summary

  • Add prek v0.3.3 (Rust pre-commit tool) to flake.nix devShell
  • Replace Go beads with beads-rust binary derivation
  • Add prek.toml with builtin file checks + commit-msg/pre-push hooks
  • Add GitHub Actions CI workflow for prek checks
  • Auto-fix whitespace/EOF issues found by prek

Test plan

  • nix develop --command prek run --all-files passes locally
  • CI prek workflow passes

🤖 Generated with Claude Code

@claude
Copy link

claude bot commented Feb 24, 2026

PR Review: Add prek git hooks

Summary: This PR adds prek (a Rust-based pre-commit alternative) to the Nix dev shell and runs prek install in the shellHook.


Issues

Critical: Missing configuration file

prek install sets up git hooks based on a configuration file (typically .pre-commit-config.yaml or .prek.toml). No such file exists in this repository. Running nix develop will either:

  • Error out during prek install, blocking the dev shell from loading for all developers
  • Silently fail if prek exits non-zero and the shell is lenient

The PR should include the config file defining which hooks to run, otherwise this change has no effect and potentially breaks the dev environment.

Minor: flake.lock not updated

flake.lock is pinned to nixpkgs commit 99dc8785 from September 2024. The PR doesn't update the lock file. If prek wasn't in nixpkgs at that revision, nix develop will fail with an evaluation error. Run nix flake update and commit the updated flake.lock to ensure the pin includes prek.

Minor: No error handling in shellHook

If prek install fails, it will block the entire nix develop session. Consider adding a fallback:

shellHook = ''
  prek install || echo "Warning: prek install failed (missing config file?)"
  ...
'';

Or better, only run it when a config file is present:

shellHook = ''
  if [ -f .pre-commit-config.yaml ]; then
    prek install
  fi
  ...
'';

Suggestions

  • Add .pre-commit-config.yaml defining the hooks you want to enforce (e.g., formatting with nixpkgs-fmt, linting, etc.)
  • Update flake.lock (nix flake update)
  • Document in README or CLAUDE.md what hooks are being enforced and why

Verdict

The intent is good — automating git hook setup via the Nix dev shell is a clean approach. However, the PR is incomplete without the hook configuration file. Merging as-is will likely break nix develop for all contributors.

@claude
Copy link

claude bot commented Feb 24, 2026

Code Review

This is a clean, focused PR that adds prek (a Rust-based reimplementation of pre-commit) as the project's git hook manager. The changes are minimal and purposeful. Overall this looks good to merge with a couple of considerations noted below.


What's good

  • Sensible hook selection: The built-in hooks cover the most impactful low-friction checks - whitespace hygiene, JSON/YAML validity, merge conflict detection, large file guards, and private key detection.
  • detect-private-key: Good security measure that catches accidental credential commits before they ever reach the remote.
  • Automated setup via shellHook: Running prek install in the Nix dev shell means all developers get hooks installed automatically on nix develop - no manual step required.
  • check-added-large-files at 500 KB: A reasonable ceiling for a documentation site.

Issues / Suggestions

Minor: prek install runs on every shell entry

prek install is called unconditionally on every nix develop. While it appears to be idempotent, it adds a small delay and log noise each time. Consider guarding it:

shellHook = ''
  if [ \! -f .git/hooks/pre-commit ]; then
    prek install
  fi
  ...
'';

This avoids the redundant install after the first run.

Minor: Consider adding check-toml

The PR introduces prek.toml but the config does not include a check-toml hook to validate it (or other future .toml files). Since prek has builtin support for it, it is a one-liner addition:

{ id = "check-toml" },

Question: What happens outside the Nix shell?

Developers not using nix develop (e.g., using the standard Bun install path documented in CLAUDE.md) will not have prek installed or hooks set up. This is fine if Nix is the expected standard environment, but worth noting in the README or CLAUDE.md so contributors know they need to run nix develop (or manually install prek) to get the hooks.


Security

The detect-private-key hook is a good addition. Note that this hook detects common private key formats (PEM headers etc.) but won't catch all credential types (e.g., API keys as plain strings, JWTs). It is still valuable as a first line of defence.


Summary

Area Assessment
Code quality Good - minimal, focused changes
Correctness No issues identified
Security Positive addition (detect-private-key)
Performance Minor: unconditional prek install on shell entry
Test coverage N/A (infrastructure change)

The PR is ready to merge as-is. The suggestions above are minor improvements rather than blockers.

@claude
Copy link

claude bot commented Feb 24, 2026

PR Review: Add prek git hooks

This PR adds two git hook tools — prek (pre-commit file checks) and lefthook (commit-msg and pre-push hooks). The overall goal is good: standardizing commit quality. Here are the findings:


Bugs / Logic Issues

1. lefthook.ymlsingle-line-subject check is broken

head -1 already extracts a single line, so wc -l will always return 1 for any input. The condition comparing the result to > 1 can never be true. This check is effectively a no-op. To properly enforce a single-line subject, the hook should check that the second line in the commit file is blank (the conventional blank-line separator between subject and body).

2. lefthook.ymltext-width body check may silently pass on failures

The exit 1 inside a pipe's subshell (the while read loop) does not propagate to the outer script in POSIX sh. On bash without set -o pipefail, this silently passes even when lines exceed 72 chars. Use a flag variable or restructure to avoid the pipe subshell.

3. lefthook.ymltext-width uses wc -c (bytes) not characters

wc -c counts bytes. Multi-byte Unicode characters (accented letters, etc.) inflate the count. wc -m counts characters and is more correct here.


Conflict with CLAUDE.md Conventions

4. github-issue hook conflicts with Beads issue reference format

CLAUDE.md explicitly states:

Include issue IDs in commits — Use format like "Fix bug (bd-abc)" in commit messages

The github-issue hook requires #[0-9]+ (a GitHub issue number). Claude agent commits using Beads bd-abc format will fail this check. The regex should be updated to also accept Beads IDs, e.g. matching either #123 or bd-abc patterns.


Design Considerations

5. Parallel commit-msg hooks produce interleaved output

parallel: true on commit-msg hooks means all hooks run simultaneously. When multiple fail, error messages interleave and are harder to read. Sequential execution is the common default for diagnostic clarity on commit-msg hooks.

6. Subject length limit of 50 characters is quite strict

The 50-char limit follows the conventional recommendation, but it's restrictive in practice. Looking at recent commits in this repo ("Add lefthook commit-msg and pre-push hooks" = 42 chars, "Add prek to nix dev shell packages" = 34 chars), descriptive commit messages can easily approach the limit. Consider raising to 72 characters or clearly documenting the expectation for contributors.

7. Consider TypeScript/Astro-aware pre-commit hooks in prek.toml

The current prek.toml only has generic file checks. Given this is a TypeScript/Astro project, consider adding astro check for type checking or a linting step if ESLint/Biome is configured. These would catch errors earlier than CI.


Minor Notes

8. Two hook managers is fine, but worth a note in the README

The comment at the top of lefthook.yml explaining prek handles pre-commit is helpful. A brief note in the README or CLAUDE.md explaining the two-tool setup would help onboarding contributors.

9. Confirm prek availability in nixpkgs-unstable

prek is relatively new. Worth confirming it's available in the nixpkgs revision being used to avoid nix develop failures.


Summary

Item Severity
single-line-subject logic broken (always passes) Medium
exit 1 in pipe subshell silently ignored Medium
github-issue conflicts with CLAUDE.md Beads format Medium
wc -c vs wc -m for Unicode correctness Low
Parallel commit-msg hooks — interleaved output Low
Missing TS/Astro linting hooks in prek.toml Suggestion

The protected-branch pre-push hook and prek file checks (trailing whitespace, private key detection, large file check, etc.) are well-implemented. The main items to address before merge are the github-issue / Beads format conflict and the broken single-line-subject check.

@claude
Copy link

claude bot commented Feb 25, 2026

PR Review: Add prek git hooks

Overall this is a solid addition — automating git hook setup via nix develop is good DX, and prek's zero-dependency Rust approach is a sensible choice. Below are my observations.


flake.nix

Positive:

  • Platform hashes are correctly pinned for all 4 targets — reproducible and secure.
  • The custom derivation pattern is appropriate since prek isn't in nixpkgs yet.
  • Using forAllSystems correctly to derive the platform target from pkgs.system.

Issues:

  1. prek install runs on every nix develop — This is likely idempotent, but it runs unconditionally every shell entry, adding noise. Consider guarding it:

    [ -f .git/hooks/pre-commit ] || prek install
  2. installPhase assumes a specific archive structure — The path prek-${prekTargets.${pkgs.system}}/prek is hardcoded. If upstream changes the archive layout, this silently breaks. A safer alternative:

    installPhase = ''
      mkdir -p $out/bin
      find . -name "prek" -type f -exec install -m755 {} $out/bin/prek \;
    '';

prek.toml

Positive:

  • Good selection of builtin hooks; detect-private-key and check-merge-conflict are particularly valuable.
  • check-added-large-files at 500 KB is a reasonable guard.
  • Pre-push protection for main, master, and development covers the right set of branches.

Issues:

  1. github-issue-ref conflicts with the beads workflowCLAUDE.md documents beads for issue tracking with commit format "Fix bug (bd-abc)". This hook requires #[0-9] in the subject line, so every Claude Code-generated commit following the beads convention will fail. Either widen the pattern:

    grep -qE '(#[0-9]+|bd-[a-z0-9]+)' "$1"

    or remove this hook if beads is the primary tracker.

  2. subject-max-length wc-m discrepancywc -m includes the trailing newline, so the check <= 51 effectively enforces 50 chars, which matches the error message. It's correct, but the magic 51 is confusing. A short comment explaining the +1 for the newline would help maintainability.

  3. capitalized-subject incompatible with conventional commits — If the team ever adopts feat:, fix:, chore: prefixes, all those commits fail. Low risk now, but worth tracking.

  4. Complex body-max-length awk escaping is fragile — Multi-level shell escaping inside a TOML string is hard to maintain and debug. Consider extracting this to a small shell script checked into the repo.

  5. no-branch-push in detached HEADgit symbolic-ref exits non-zero in detached HEAD, so the hook silently passes. This is the correct safe default — just worth being aware of.


Minor Nits

  • meta.platforms is missing from the prek derivation — not blocking but conventional for nixpkgs-style derivations.
  • check-yaml will validate .github/ workflow files too — good incidental coverage.

Summary

# Severity Item
3 Medium github-issue-ref conflicts with beads bd-* commit convention
1 Low prek install runs unconditionally on every shell entry
2 Low installPhase binary path assumes upstream archive structure
4 Low subject-max-length magic 51 needs a comment
5 Info capitalized-subject incompatible with conventional commits

Issue #3 is the most important to resolve before merging — it will block Claude Code's automated commits that use the beads bd-* reference format instead of GitHub #123 references.

@claude
Copy link

claude bot commented Feb 25, 2026

PR Review: Add prek git hooks

Good addition of code quality tooling — prek is a solid choice for fast, Rust-based pre-commit hooks. The Nix derivation approach for pinning a specific binary version is well-executed. Here are some observations:


Security

GitHub Actions pinned to @main (prek.yml, lines 21–22)

Using @main for third-party actions is a supply-chain risk — a compromised upstream can affect CI without a PR. Pin to a specific commit SHA or a tagged release:

- uses: DeterminateSystems/nix-installer-action@v14  # or a specific SHA
- uses: DeterminateSystems/magic-nix-cache-action@v8

Correctness

github-issue-ref hook would block the commits in this very PR

The commits in this PR ("Add prek git hooks", "Add prek.toml config", etc.) don't contain #[0-9] in their subjects. If this hook had been active when they were created, they would have been rejected. Worth considering:

  • Is this rule appropriate for a documentation site where not every commit maps to a tracked issue?
  • Co-authored Claude Code commits often follow patterns like "Fix typo in README" with no issue reference.
  • Consider making this a warning rather than a hard failure, or document that all commits must reference a Beads or GitHub issue.

prek install runs unconditionally on every nix develop (flake.nix, shellHook)

This is likely idempotent if prek handles re-installs gracefully, but it adds noise on every shell entry. A small guard would help:

prek install 2>/dev/null || true

Minor Issues

Off-by-one in subject-max-length (prek.toml, line 20)

wc -m includes the trailing newline in its count, so <= 51 is equivalent to <= 50 visible characters. The logic is correct but the threshold looks wrong at a glance — a comment explaining the +1 for the newline would help readers.

Complex inline awk/sh in prek.toml

The body-max-length awk command and commit-msg hooks are correct but hard to audit as one-liners. If the number of hooks grows, consider moving complex validators to small scripts under scripts/hooks/ and referencing them with entry = "scripts/hooks/...". Fine for now.


What's Good

  • Nix derivation for prek is clean — per-platform hashes, proper installPhase, and meta attributes.
  • detect-private-key and check-added-large-files (500KB cap) are practical guards.
  • no-branch-push protecting main/master/development is a solid safety net.
  • concurrency config in the workflow correctly cancels stale runs.
  • permissions: contents: read follows least-privilege principle.
  • AGENTS.md and README.md updates are concise and useful.

Summary

Main actionable items before merging:

  1. Pin GitHub Actions to specific versions/SHAs (security concern)
  2. Reconsider github-issue-ref enforcement strictness — the PR's own commits would fail this check
  3. Optionally suppress prek install output in shellHook if already installed

Overall a clean, well-scoped PR.

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8f76160152

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

{ id = "no-trailing-period", name = "No trailing period", language = "system", entry = "sh -c 'head -1 \"$1\" | grep -q \"\\\\.$\" && { echo \"Commit subject must not end with a period\"; exit 1; } || true' --", stages = ["commit-msg"] },
{ id = "github-issue-ref", name = "GitHub issue reference", language = "system", entry = "sh -c 'head -1 \"$1\" | grep -q \"#[0-9]\" || { echo \"Commit subject must reference a GitHub issue (e.g. #123)\"; exit 1; }' --", stages = ["commit-msg"] },
{ id = "max-message-length", name = "Max 500 chars total", language = "system", entry = "sh -c 'chars=$(wc -c < \"$1\"); [ \"$chars\" -le 500 ] || { echo \"Commit message too long ($chars chars, max 500)\"; exit 1; }' --", stages = ["commit-msg"] },
{ id = "no-branch-push", name = "No push to main/master", language = "system", entry = "sh -c 'branch=$(git symbolic-ref --short HEAD 2>/dev/null); case \"$branch\" in main|master|development) echo \"Direct push to $branch is not allowed\"; exit 1;; esac' --", stages = ["pre-push"] },

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Validate pre-push target branch, not current branch

The no-branch-push hook only inspects the local branch name via git symbolic-ref --short HEAD, so it blocks git push only when your current branch is main/master/development but still allows git push origin HEAD:main from any other branch. That means the rule labeled “No push to main/master” is bypassable and will not reliably prevent accidental pushes to those remote branches.

Useful? React with 👍 / 👎.

{ id = "subject-max-length", name = "Subject max 50 chars", language = "system", entry = "sh -c 'len=$(head -1 \"$1\" | wc -m); [ \"$len\" -le 51 ] || { echo \"Commit subject too long ($len chars, max 50)\"; exit 1; }' --", stages = ["commit-msg"] },
{ id = "body-max-length", name = "Body lines max 72 chars", language = "system", entry = "sh -c 'grep -v \"^#\" \"$1\" | tail -n +3 | awk \"length > 72 { print NR\\\": \\\"\\$0; found=1 } END { if (found) { print \\\"Body lines must be 72 chars or less\\\"; exit 1 } }\"' --", stages = ["commit-msg"] },
{ id = "no-trailing-period", name = "No trailing period", language = "system", entry = "sh -c 'head -1 \"$1\" | grep -q \"\\\\.$\" && { echo \"Commit subject must not end with a period\"; exit 1; } || true' --", stages = ["commit-msg"] },
{ id = "github-issue-ref", name = "GitHub issue reference", language = "system", entry = "sh -c 'head -1 \"$1\" | grep -q \"#[0-9]\" || { echo \"Commit subject must reference a GitHub issue (e.g. #123)\"; exit 1; }' --", stages = ["commit-msg"] },

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Allow documented Beads IDs in commit subject rule

This check hard-requires #[0-9] in every commit subject, but the repository guidance requires Beads IDs in commit messages (for example Fix bug (bd-abc)). With hooks installed, commits that follow the documented Beads format are rejected during commit-msg, which creates a workflow-breaking mismatch between the enforced hook and the project’s stated commit convention.

Useful? React with 👍 / 👎.

@javdl javdl changed the title Add prek git hooks Add prek pre-commit hooks Feb 25, 2026
@javdl javdl merged commit 7870886 into main Feb 25, 2026
5 of 6 checks passed
@javdl javdl deleted the add-prek branch February 25, 2026 14:23
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 80431e2900

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines 99 to +101
shellHook = ''
if ! command -v bd &> /dev/null; then
echo "Installing beads (bd) for AI agent task tracking..."
go install github.com/steveyegge/beads/cmd/bd@latest
fi
echo " Beads: $(bd --version 2>/dev/null || echo 'run: go install github.com/steveyegge/beads/cmd/bd@latest')"
prek install
echo " Beads: $(br --version 2>/dev/null || echo 'not available on this platform')"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Provide bd command in Nix shell

This shell setup no longer installs or exposes bd, and now only checks br, but repo automation still requires bd (for example loop.sh’s beads_check exits when command -v bd fails at lines 534-536). In a clean nix develop environment, core task-tracking workflows now fail immediately unless contributors manually install bd outside the declared dev shell.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant