# Pre-commit Hook Setup

Automated linting and validation before every commit.

## Installation

### 1. Install pre-commit

**macOS/Linux (via pip):**

```bash
pip install pre-commit
# Or with pipx (recommended)
pipx install pre-commit
```text

**macOS (via Homebrew):**

```bash
brew install pre-commit
```text

**Verify installation:**

```bash
pre-commit --version
```text

### 2. Install the hooks

From the project root:

```bash
pre-commit install
```text

This creates `.git/hooks/pre-commit` that runs automatically on `git commit`.

## What Gets Checked

The pre-commit hook runs these checks **automatically before every commit**:

### General Checks

- **Trailing whitespace** - Removes trailing spaces (except in `.md` files)
- **End-of-file fixer** - Ensures files end with newline
- **YAML/TOML/JSON validation** - Syntax checking
- **Large files** - Prevents files >1MB
- **Merge conflicts** - Detects conflict markers
- **Line endings** - Normalizes to LF

### Language-Specific Linting

#### Rust

- **`cargo fmt`** - Code formatting check
- **`cargo clippy`** - Linting with warnings as errors

#### Shell Scripts

- **`shellcheck`** - Bash/shell script analysis

#### Markdown (docs/ only)

- **`markdownlint-cli2`** - Syntax validation (MD rules)
- **`vale`** - Prose quality (weasel words, etc.)

#### Nickel

- **`nickel typecheck`** - Type checking for `.ncl` files

#### Nushell

- **`nu --check`** - Script validation for `.nu` files

### Security

- **`cargo audit`** - Dependency vulnerability scan (manual stage only)

## Usage

### Automatic (Recommended)

Pre-commit runs automatically on `git commit`:

```bash
git add docs/README.md
git commit -m "Update README"
# Pre-commit hooks run automatically
```text

### Manual Run

Run all hooks on all files:

```bash
pre-commit run --all-files
```text

Run specific hook:

```bash
pre-commit run cargo-fmt --all-files
pre-commit run markdownlint-cli2 --all-files
pre-commit run vale --all-files
```text

Run hooks on staged files only:

```bash
pre-commit run
```text

### Skip Hooks (Emergency Only)

To bypass hooks (use sparingly):

```bash
git commit --no-verify -m "Emergency fix"
```text

**Note**: CI will still run all checks, so bypassing locally doesn't skip validation.

## Hook Behavior

### Blocking vs Non-Blocking

**Blocking (commit fails if check fails):**

- cargo fmt
- cargo clippy
- markdownlint-cli2
- shellcheck
- nickel typecheck
- nushell check

**Non-Blocking (warnings logged, commit succeeds):**

- vale (prose quality warnings don't block commits)

### Vale Configuration

Vale is configured to be **informative but not blocking**:

- **0 errors** → Commit succeeds
- **Warnings** → Logged but commit proceeds
- Encourages better prose without being disruptive

To make Vale blocking, edit `.pre-commit-config.yaml`:

```yaml
# Change this
entry: bash -c 'vale docs/ || exit 0'
# To this
entry: bash -c 'vale docs/'
```text

## Updating Hooks

Update to latest hook versions:

```bash
pre-commit autoupdate
```text

This updates the `rev:` fields in `.pre-commit-config.yaml`.

## Troubleshooting

### Hook fails: "command not found"

**Problem**: Tool not installed (e.g., `cargo`, `vale`, `nickel`, `nu`)

**Solution**: Install the missing tool:

```bash
# Rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

# Vale
brew install vale  # macOS
# See https://vale.sh for other platforms

# Nickel
cargo install nickel-lang-cli

# Nushell
cargo install nu
```text

### Hooks are slow

**Problem**: `cargo clippy` takes 30+ seconds

**Solution**: Use `--no-verify` for quick commits, let CI handle full validation:

```bash
git commit --no-verify -m "WIP: quick fix"
# Later, when ready
pre-commit run --all-files
```text

### Markdownlint fails on valid Markdown

**Problem**: False positive from markdownlint

**Solution**: Update `.markdownlint-cli2.jsonc` to disable the rule:

```json
{
  "config": {
    "default": true,
    "MD013": false,  // Example: disable line length
    "MD033": false   // Example: allow HTML
  }
}
```text

### Vale warnings are too noisy

**Problem**: Too many prose quality warnings

**Solution**: Adjust `.vale.ini` to disable specific rules:

```ini
[*.md]
write-good.Weasel = NO  # Disable weasel word warnings
```text

Or set higher alert level:

```ini
MinAlertLevel = error  # Only show errors, not warnings
```text

## Best Practices

### 1. Run Before First Commit

After installing hooks, test them:

```bash
pre-commit run --all-files
```text

Fix any issues before committing.

### 2. Keep Commits Small

Smaller commits = faster hook execution.

### 3. Stage Incrementally

Stage related changes together:

```bash
git add docs/README.md docs/build.md
git commit -m "Update build docs"
# Only checks staged files
```text

### 4. Use --no-verify Sparingly

Reserve `--no-verify` for emergencies:

- Hotfixes
- Reverts
- Time-critical patches

CI will catch issues anyway.

### 5. Update Hooks Regularly

Monthly:

```bash
pre-commit autoupdate
pre-commit run --all-files
```text

## Integration with CI

Pre-commit hooks mirror CI checks:

**Local (pre-commit)**:

- Fast feedback before commit
- Catches obvious issues early
- Optional (can bypass with `--no-verify`)

**CI (GitHub Actions)**:

- Comprehensive validation
- Required for PRs
- Cannot be bypassed

Both run the same checks, so passing locally = passing CI.

## Disabling Hooks Temporarily

Disable all hooks:

```bash
pre-commit uninstall
```text

Re-enable:

```bash
pre-commit install
```text

Disable specific hook (edit `.pre-commit-config.yaml`):

```yaml
- id: cargo-clippy
  # Add this to disable:
  stages: [manual]
```text

Then run manually with:

```bash
pre-commit run cargo-clippy --all-files --hook-stage manual
```text

## Summary

Pre-commit hooks provide:

- ✅ **Automated quality checks** before every commit
- ✅ **Fast feedback** (catch issues in seconds, not minutes)
- ✅ **Consistent standards** across all contributors
- ✅ **Optional but recommended** (can bypass if needed)

Install once, benefit forever:

```bash
pip install pre-commit
pre-commit install
```text

---

**See also**:

- [.pre-commit-config.yaml](../.pre-commit-config.yaml) - Hook configuration
- [development.md](development.md) - Development workflow
- [CI configuration](../.github/workflows/ci.yml) - GitHub Actions
