AGENTS.md
You are an expert at working with Rust CLI tools and have been onboarded to the pullhook codebase.
Project Overview
pullhook is a Rust CLI tool (binary: pullhook) that runs commands when files change after git pull.
It is a drop-in compatible replacement for the npm package git-pull-run, with bounded concurrency,
resilient diff base fallback, and safer command execution.
Development Commands
Build
cargo build
cargo build --release
cargo install --path .
Test
cargo nextest run
cargo nextest run -E 'test(test_name)'
cargo nextest run --no-tests=pass
Lint and Quality (run before pushing)
cargo fmt --all --check
cargo clippy --all-targets -- -D warnings
cargo audit
cargo deny check
cargo shear
Lint and Quality (always run before finishing a task)
bash ./scripts/pre-commit.sh
Benchmarks
cargo bench
JS Tooling (formatting and commit lint support)
bun install
bun x --bun lint-staged
Architecture
End-to-End Data Flow
CLI parse -> validate -> (--install? detect PM) -> git repo root
-> resolve diff base -> get changed files -> compile glob pattern
-> match files -> build task list -> run_parallel (rayon) -> report results
Module Responsibilities (src/)
main.rs- Entry point. Sets up tracing and signal handling (ctrlc), validates args, and orchestrates the full flow.cli.rs-clapderive-based CLI parsing and validation.--installimplies--onceand conflicts with--pattern/--command.git.rs- Git operations: repo root discovery, changed files (diff --name-only), and diff base resolution.matcher.rs- Glob matching viaglobsetwith a shim for bash extglob syntax.pm.rs- Package manager detection using lock files first, then config files, with ambiguity checks.runner.rs- Bounded parallel command execution with rayon, shell-word parsing, and optional--shellviash -c.error.rs- Domain error types viathiserror;anyhow::Resultis only used at the top level.
Diff Base Resolution Chain
- Explicit
--baseflag HEAD@{1}(reflog)ORIG_HEAD(merge/pull)HEAD~1(fallback for shallow clones)
Pattern and Matching Notes
Supported extglob forms:
+(a|b)- one or more*(a|b)- zero or more?(a|b)- zero or one@(a|b)- exactly one!(a|b)- none of
Nested extglobs are not supported and should error. Extglobs are expanded into separate GlobMatcher instances.
Package Manager Detection Notes
Two-phase detection in pm.rs:
- Check lock files first and error on ambiguity.
- Fall back to config files and default to npm when needed.
Parallel Execution Notes
- Uses
rayon::ThreadPoolBuilderwith default jobsmin(CPUs, 8). - Uses an
AtomicBoolshutdown flag checked before each task. - Captures output per task to avoid interleaving.
- Returns results in task order (not completion order).
Conventions
Error Handling
- Prefer typed errors in
error.rsusingthiserror. - Use
anyhow::Resultonly at the top level (main).
Linting and Safety
clippy::all,clippy::pedantic, andclippy::nurseryare enabled as warnings.unsafe_codeis forbidden.
Formatting
- Rust formatting is configured in
rustfmt.toml(hard tabs, 120 width, 2024 formatting rules). - JSON/JSONC formatting uses Biome.
- TOML formatting uses
tombi. - Markdown formatting uses
rumdl.
Toolchain and Build Performance
- Rust is pinned to
1.93.1viarust-toolchain.toml(edition 2021). sccacheis configured as the rustc wrapper in.cargo/config.toml.- Incremental compilation is disabled (sccache is the primary cache strategy).
- No custom linker is configured by default.
Commit Style
- Conventional commits are enforced by commitlint.
Git Hooks (Lefthook)
Pre-commit hooks run in parallel:
bun x --bun lint-stagedgitleakscargo clippy --all-targets -- -D warningscargo auditcargo deny checkcargo shear
Pre-push hook:
cargo nextest run --no-tests=pass
CI
Required checks in .github/workflows/ci.yaml:
fmtclippynextestauditdenysheargitleaksdocs
Informational (PR-only, non-blocking):
cargo-bloatbinary size reportcargo-zigbuildcross-compile smoke tests
Key Dependencies
clap- CLI parsing with derive macrosglobset- glob matchingrayon- parallel executionshell-words- command parsingtracing/tracing-subscriber- loggingctrlc- signal handlingthiserror- typed domain errorsanyhow- top-level error handling
Important Configuration Files
| File | Purpose |
|---|---|
rust-toolchain.toml | Pin Rust version |
rustfmt.toml | Rust formatting rules |
.cargo/config.toml | Build optimizations and linker config |
deny.toml | Dependency license/advisory policy |
lefthook.yaml | Git hook configuration |
dist-workspace.toml | cargo-dist release configuration |
Release
Releases are managed by cargo-release for versioning and cargo-dist for distribution.
See RELEASE.md for release workflow details.
Install cargo-release to automate releases:
cargo install cargo-release
Run a release:
# Update CHANGELOG.md first, then:
cargo release 0.2.0
# or cargo release minor
Artifacts are published to GitHub Releases, Homebrew (howmanysmall/pullhook), npm (@pobammer/pullhook),
and shell/powershell installers.
Supported targets include aarch64 and x86_64 for macOS, Linux, and Windows.