Welcome — here's how to land your first PR in under an hour.
Issues · Pull Requests · Changelog · Docs
# 1. Clone and build
git clone https://github.com/leonardomso/betterhook && cd betterhook
cargo build --workspace
# 2. Run the test suite (~600 tests, ~30s on a modern machine)
cargo test --workspace
# 3. Make your change. Then:
cargo clippy --workspace --all-targets -- -D warnings
cargo fmt --all -- --check
# 4. Commit with a Conventional-Commits message
git commit -m "fix(runner): drop lock when timeout fires"
# 5. Push and open a PR against masterIf all four checks above pass and your commit follows Conventional Commits, CI will be green and a maintainer will review.
| Tool | Version | Why |
|---|---|---|
| Rust | 1.86+ (edition 2024) | Workspace MSRV. Install via rustup. |
| git | 2.30+ | Worktree semantics we rely on. |
| bun | 1.x | Mintlify docs site only — skip if you're not touching apps/docs/. |
apps/betterhook/ library crate — config parser, runner, cache, daemon, builtins
apps/cli/ CLI binary (thin clap wrapper over the library)
apps/docs/ Mintlify documentation site
xtask/ benchmarks, stress harness, fuzz runner
recipes/ drop-in betterhook.toml configs for common stacks
packaging/ Homebrew formula + npm wrapper scaffolds
cargo build --workspace # compile everything
cargo test --workspace # ~600 tests, ~30 seconds
cargo clippy --workspace --all-targets -- -D warnings # lint (pedantic, deny warnings)
cargo fmt --all -- --check # formatting checkAll four must pass before pushing. CI runs the same matrix on Ubuntu and macOS.
The docs site:
cd apps/docs && bun install
bun run dev # local Mintlify previewcargo build --release -p betterhook-cli
PATH="$PWD/target/release:$PATH" cargo xtask bench-monorepoGenerates a synthetic 10,000-file repo and writes target/bench-results.md comparing betterhook against lefthook and hk (whichever are on PATH). Run before any change touching the runner, dispatch, or config loader to catch perf regressions.
Fuzzing
cargo build --release -p xtask
./target/release/xtask fuzz --duration 30 # 30s per target, ~3 min total
./target/release/xtask fuzz-smoke # fast seed-corpus checkThe fuzz harness covers the config parser (TOML/YAML/KDL/JSON), the importer (lefthook/husky/hk/pre-commit), and the bincode wire protocol. Add a target by dropping a fuzz_target_<name>.rs into apps/betterhook/fuzz/ and registering it in xtask/src/fuzz.rs.
Mutation testing
We use cargo-mutants to find logic that's reached by tests but not meaningfully asserted. Start narrow:
cargo install --locked cargo-mutants
cargo mutants -p betterhook -f apps/betterhook/src/dispatch.rs -o /tmp/mutants-dispatch
# add tests for survivors, then:
cargo mutants -p betterhook -f apps/betterhook/src/dispatch.rs --iterate -o /tmp/mutants-dispatchUse survivors to drive test additions, rerun with --iterate until the remaining survivors are intentional or impractical.
Conventional Commits, lowercase types, imperative summary, scope when useful:
feat(runner): add DAG-aware parallel scheduler
fix(cache): move blocking I/O off the executor thread
docs(readme): restructure for scannability
test: e2e test for builtin diagnostic pipeline
refactor(builtins): extract shared parse helpers
chore(deps): bump fs4 to 1.1.0
ci: pin actions to exact latest versions
perf(dispatch): hook_for_match returns Cow<Hook>
Never add Co-Authored-By or AI-attribution lines.
- Fork and branch from
master. - Make your change. One logical change per commit. Test additions live in the same commit as the behavior they cover.
- Run the four checks locally (build, test, clippy, fmt).
- Open a PR against
master. CI runs on Ubuntu + macOS. - A maintainer reviews and merges.
Good PR descriptions include:
- Summary — what the change does in one sentence
- Why — context and motivation
- Validation — exact commands run, outcomes
- Tests — added or updated, and why
| Do | Don't |
|---|---|
| Confirm assumptions when ambiguous — open an issue first | Guess CLI behavior or config semantics |
| Follow existing module boundaries and naming patterns | Refactor unrelated code in the same PR |
| Keep changes small, focused, reversible | Ship behavior without tests |
| Add regression tests for bug fixes | Bypass failing tests, lint, or hooks |
| Stream subprocess output | Buffer subprocess output in memory |
| Test from a linked worktree, not just the primary checkout | Break worktree isolation |
betterhook is pre-release (v0.x). The library API, config schema, and CLI flags may change between minor versions without deprecation cycles. Pin to an exact version if you depend on the library crate directly.
By contributing, you agree your contributions will be licensed under the MIT License.
