Files

7.2 KiB

issue, issue_title
issue issue_title
14 Set up prek for pre-commit linting (Biome + markdownlint)

Set up prek for pre-commit linting

Problem Statement

During the #13 retro, a Biome version-skew issue surfaced: running npm run lint:fix locally with a different Biome version than the one pinned in package-lock.json produced formatting changes that CI rejected. A pre-commit hook running the project-local linters would catch these mismatches before they reach CI, saving round-trip time on failed builds.

Goals

  • Install and configure prek as the git hook manager.
  • Run npx biome check on staged files in the pre-commit hook so the project-pinned Biome version is always used.
  • Run npx markdownlint-cli2 on staged Markdown files in the pre-commit hook.
  • Document the setup so contributors know how to install hooks and what they enforce.

Non-Goals

  • Adding new lint rules or changing existing Biome/markdownlint configuration — out of scope.
  • Running the full test suite or type-check in the pre-commit hook — too slow for a pre-commit gate; CI handles these.
  • Migrating CI to use prek — CI continues to run npm run lint and npm run lint:md directly.

Background

  • Origin: #13 retro (docs/retro/0013-consolidate-session-start-handlers.md) — a style: commit with wrong Biome formatting caused a CI failure and 2 extra fixup commits.
  • Existing lint scripts: npm run lint (biome check .), npm run lint:md (markdownlint-cli2 '*.md' 'docs/**/*.md' '.pi/prompts/**/*.md').
  • CI: .github/workflows/ci.yml runs npm run lint and npm run lint:md as separate steps.
  • Permission surface: none — this is a developer-tooling concern with no policy semantics.
  • On-disk identity: no impact — prek config lives in .prek.yaml at the repo root, not in the Pi config directory.

Design Overview

prek is a Rust-based git hook manager (drop-in replacement for pre-commit). It reads .prek.yaml at the repo root and installs git hooks that run configured checks on staged files.

Hook configuration

The .prek.yaml file defines two hooks under pre-commit:

  1. Biome — runs npx biome check on staged JS/TS/JSON files. Using npx biome ensures the project-local version from node_modules is used, preventing version skew.
  2. markdownlint — runs npx markdownlint-cli2 on staged .md files.

Both hooks should operate only on staged files (prek handles this via types/files filters and passes matching filenames to the command).

Developer setup

After cloning, a developer runs prek install once to set up the git hooks. This is a one-time step documented in README.md and optionally wired as a prepare npm script so npm install auto-installs hooks.

Module-Level Changes

Added

  • .prek.yaml — prek hook configuration with Biome and markdownlint hooks scoped to relevant file types.

Changed

  • package.json — add a prepare script (prek install) so hooks are installed automatically after npm install. This is the standard convention for git hook managers.
  • README.md — document the pre-commit setup: what it runs, how to install hooks manually if needed, how to skip hooks (git commit --no-verify) in emergencies.
  • .gitignore — no changes needed; prek does not generate files that need ignoring.

Not changed

  • biome.json — no changes; the hook invokes the existing config.
  • .markdownlint-cli2.yaml — no changes; the hook invokes the existing config.
  • .github/workflows/ci.yml — no changes; CI continues to run the full lint suite independently.

TDD Order

This issue is primarily a configuration/tooling change with no application logic, so the "test" cycles are validation-oriented rather than unit-test-oriented.

  1. Verify prek is installable and .prek.yaml is valid. Create .prek.yaml with both hooks. Run prek install and confirm hooks are registered (.git/hooks/pre-commit exists and delegates to prek). Commit: feat: add prek pre-commit config for Biome and markdownlint (#14)

  2. Verify Biome hook catches lint violations on staged files. Stage a file with a deliberate Biome violation, run git commit (or prek run pre-commit), confirm it fails. Stage a clean file, confirm it passes. Commit: test: verify Biome pre-commit hook catches violations (#14) (manual verification, no committed test file)

  3. Verify markdownlint hook catches Markdown violations on staged files. Stage a .md file with a deliberate violation (e.g., trailing spaces), run the hook, confirm failure. Stage a clean .md file, confirm it passes. Commit: test: verify markdownlint pre-commit hook catches violations (#14) (manual verification)

  4. Add prepare script and update README. Wire prek install into package.json's prepare script. Add a "Pre-commit hooks" section to README.md. Commit: docs: document prek pre-commit setup (#14)

Risks and Mitigations

Risk Mitigation
prek not installed on contributor's machine → prepare script fails Document prek installation in README. The prepare script can use prek install with a guard (command -v prek or similar) so it warns but does not block npm install.
Hook runs full-repo lint instead of staged-only → slow commits Configure prek to pass only staged filenames to each command. Verify with a manual test.
Could this silently weaken a permission? No — this change is purely developer tooling. It adds a pre-commit gate; it does not touch any permission surface, policy file, or runtime code.
markdownlint glob mismatch between hook and npm run lint:md Use the same glob patterns in .prek.yaml as in package.json's lint:md script.
npx biome falls back to a global install if local is missing npx resolves from node_modules/.bin first when run inside a project with a lockfile. Document that npm install must be run before committing.

Open Questions

  1. Should prepare silently skip if prek is not installed? A guard like command -v prek >/dev/null 2>&1 && prek install || true avoids blocking npm install for contributors who haven't installed prek yet, but it also means hooks silently won't exist. Decide during implementation.
  2. Should we pin a prek version? prek is installed globally (via cargo install or a binary download), not as an npm dependency. Pinning is not straightforward — defer unless version-specific breakage appears.