diffier 0.1.0

Live diff feed for Claude Code edits, fed by hooks, rendered with delta
Documentation
# Contributing

## Setup

Requirements: a Rust toolchain (1.88 or later, edition 2024), `jq`, and
optionally [delta](https://github.com/dandavison/delta).

```sh
git clone https://github.com/spencerjireh/diffier
cd diffier
cargo test
```

`jq` is a hard requirement for the test suite. `tests/hook.rs` runs the real
hook script through `sh`, and those tests fail without it.

## Before you open a pull request

Run what CI runs:

```sh
cargo fmt --all --check
cargo clippy --all-targets --all-features -- -D warnings
cargo test --all-features --locked
```

CI also runs `cargo deny check` and builds the docs with warnings denied. All of
it must pass.

## Tests

There are three layers:

- Unit tests in `#[cfg(test)] mod tests` next to the code they cover.
- `tests/replay.rs` — a golden test. It copies `tests/fixtures/tree` into a
  temporary directory, substitutes `{{CWD}}` in `tests/fixtures/spool.jsonl`,
  and asserts the rendered cards against an `insta` snapshot.
- `tests/hook.rs` — behavioral tests of `hook/diffier.sh`, including its
  resistance to shell injection through hook payload fields.

After an intended change to rendering, refresh the snapshot and read the diff
before you commit it:

```sh
INSTA_UPDATE=always cargo test
git diff tests/snapshots/
```

## Changing the hook script

`hook/diffier.sh` is `include_str!`-ed into `src/install.rs`, so it ships inside
the binary. Two rules:

- **It must never exit non-zero.** Exit code 2 on `PreToolUse` blocks Claude's
  tool call, and any other non-zero code puts a notice in the user's session.
  Every failure path ends in `exit 0`.
- **Payload fields must stay constrained to strings** before they reach the
  shell. The `jq ... | strings` filters exist so that an array- or object-valued
  `file_path` cannot expand into extra words that `eval` would run as a command.
  `tests/hook.rs` covers both cases; keep them passing.

The script is POSIX `sh`, not bash. It runs on both GNU and BSD userland, which
is why CI tests on Linux and macOS.

## Commits

Use [Conventional Commits](https://www.conventionalcommits.org): `feat:`,
`fix:`, `docs:`, `test:`, `refactor:`, `chore:`. Keep the subject under 72
characters.

## Releases

Maintainers tag `vX.Y.Z` on `main`. That triggers `.github/workflows/release.yml`,
which cross-builds five targets and attaches the tarballs and checksums to a
GitHub Release. Bump `version` in `Cargo.toml` in the same commit as the tag.