nizm-cli
v0.3.1
Published
Lightweight, zero-config pre-commit hooks
Downloads
963
Maintainers
Readme
Lightweight, zero-config pre-commit hooks
Quick Start · Installation · Configuration · Commands · How It Works
nizm (from Arabic nizam — system/order) is a fast, native CLI that runs your formatters and linters at the git pre-commit stage. It reads hook definitions straight from your existing project manifests — no .yaml files, no managed environments. Unlike pre-commit, nizm doesn't install tools for you; it trusts the ones already in your dev-dependencies and local PATH.
$ nizm run
nizm: running against 3 staged files
ruff 3 files (120ms)
mypy 3 files (340ms)
nizm: done in 461msFeatures
- Zero config — hooks live in your existing manifest files
- Fast — native Rust binary, no Python/Node runtime overhead
- Partial staging — stashes unstaged changes, runs hooks on staged content only, restores cleanly
- Scope filtering — each hook only sees files matching its glob pattern
- Monorepo-ready — per-directory CWD isolation, multiple manifests, parallel execution
- Auto-add — files modified by formatters are automatically re-staged
- Smart init — scans dev-dependencies, suggests hooks it already knows about
- Self-diagnosing —
nizm doctorverifies your setup and suggests fixes
Quick Start
npm install -g nizm-cli # or: cargo install nizm
nizm init # scans dev-deps, injects hooks, installs git hookThat's it. Your next git commit runs your hooks automatically.
Installation
npm install -g nizm-cliPlatform-native binary — zero Node.js overhead at runtime.
cargo install nizmDownload the latest archive for your platform from GitHub Releases, extract, and place the binary somewhere on your PATH.
Configuration
nizm discovers hooks from your project manifests. No separate config file needed.
pyproject.toml
[tool.nizm.hooks]
ruff = { cmd = "ruff check --fix {staged_files}", glob = "*.py" }
black = { cmd = "black {staged_files}", glob = "*.py" }
mypy = { cmd = "mypy {staged_files}", glob = "*.py" }package.json
{
"nizm": {
"hooks": {
"prettier": { "cmd": "prettier --write {staged_files}" },
"eslint": {
"cmd": "eslint --fix {staged_files}",
"glob": "*.{js,jsx,ts,tsx}"
}
}
}
}Cargo.toml
[package.metadata.nizm.hooks]
clippy = { cmd = "cargo clippy --fix --allow-dirty -- -D warnings", glob = "*.rs" }
rustfmt = { cmd = "cargo fmt", glob = "*.rs" }.nizm.toml
Standalone config for projects that don't use any of the above, or for repo-root overrides:
[hooks]
check = { cmd = "make lint" }
test = { cmd = "make test" }Hook fields
| Field | Required | Description |
| :----- | :------- | :-------------------------------------------------------------------- |
| cmd | yes | Shell command to run. Use {staged_files} to receive the file list. |
| glob | no | Filter staged files by pattern (*.py, *.{js,ts}, src/**/*.rs). |
| type | no | Git hook type: pre-commit (default), pre-push, commit-msg, prepare-commit-msg. |
[!TIP] If
{staged_files}is omitted, the command runs unconditionally when any file in scope is staged.
Commands
nizm init
Scans dev-dependencies, suggests hooks, and injects them into your manifest.
$ nizm init
added clippy cargo clippy --fix --allow-dirty -- -D warnings
added rustfmt cargo fmt
Cargo.toml — [clippy, rustfmt]
pre-commit hook installedPass hook names directly for non-interactive use: nizm init ruff prettier
Known tools: ruff · black · mypy · prettier · eslint · biome · rustfmt · clippy
[!TIP] For Rust projects,
rustfmtandclippyare suggested automatically when a[package]section exists — no dev-dependency needed.
nizm install
Writes a git hook into .git/hooks/ that calls nizm run. Existing hooks are preserved.
$ nizm install
scanning for manifests...
pyproject.toml — [ruff, mypy]
pre-commit hook installed| Flag | Description |
| :--- | :---------- |
| --config <path> | Bake specific manifests (repeatable, skips interactive picker) |
| --parallel | Bake --parallel flag into the hook script |
| --force | Overwrite modified nizm blocks without prompting |
nizm run
Runs hooks against staged files. This is what the git hook calls.
$ nizm run
nizm: running against 5 staged files
clippy 5 files (780ms)
rustfmt 5 files (210ms)
nizm: done in 991ms| Flag | Description |
| :--- | :---------- |
| HOOK | Run a single hook by name |
| --config <path> | Explicit manifest paths (repeatable, skips auto-discovery) |
| --parallel | Run manifests concurrently |
| --all | Run against all tracked files instead of staged |
| --hook-type <TYPE> | Hook type to run (default: pre-commit) |
nizm doctor
Diagnoses hook health — checks hook files, config validity, and tool availability.
$ nizm doctor
hooks
pre-commit (nizm-managed) ✓
└ Cargo.toml ✓
├ clippy (cargo) ✓
└ rustfmt (cargo) ✓
all 4 checks passednizm ls
Lists all configured hooks across discovered manifests.
$ nizm ls
Cargo.toml
clippy cargo clippy --fix --allow-dirty -- -D warnings *.rs
rustfmt cargo fmt *.rsnizm recover
Restores your working tree from a rescue snapshot after a failed stash recovery.
$ nizm recover
working tree restored from rescue snapshotIf recovery produces conflicts, resolve them manually — the rescue ref is cleaned up automatically.
nizm uninstall
Removes nizm hook blocks from .git/hooks/. Use --purge to also strip hook config from manifests.
$ nizm uninstall --purge
pre-commit hook removed
cleaned Cargo.tomlEnvironment variables
| Variable | Description |
| :---------- | :--------------------------------------------------- |
| NIZM_SKIP | Comma-separated hook names to skip (e.g. mypy,ruff) |
| NO_COLOR | Disable colored output when set |
How It Works
git commit
│
▼
.git/hooks/pre-commit ← baked by `nizm install`
│
▼
nizm run --config pyproject.toml --config package.json
│
├─ detect partially staged files
├─ stash unstaged changes (StashGuard)
│
├─ for each manifest:
│ ├─ cd to manifest directory
│ ├─ for each hook:
│ │ ├─ scope-filter staged files by glob
│ │ └─ execute cmd with {staged_files}
│ └─ next hook
│
├─ auto-add files modified by hooks
├─ restore unstaged changes from stash
└─ exit 0 (pass) or exit 1 (fail → commit blocked)Partial staging
When you stage only part of a file (git add -p), nizm stashes the unstaged changes before running hooks. This ensures formatters and linters see exactly what will be committed — not your working tree. After hooks complete, unstaged changes are restored cleanly.
A rescue ref is saved at refs/nizm-backup before every stash operation. If anything goes wrong:
nizm recoverParallel execution
With --parallel, each manifest's hooks run in a separate thread. Hooks within a single manifest stay sequential (so your formatter runs before your linter). Output is captured per-manifest and printed in order — no interleaving.
Monorepo support
nizm uses git ls-files to discover manifests, respecting .gitignore at all levels. Each manifest's hooks run with cwd set to that manifest's directory, so tools resolve paths correctly:
repo/
├── pyproject.toml ← ruff, mypy run here
├── frontend/
│ └── package.json ← prettier, eslint run here
└── services/api/
└── package.json ← separate hooks, separate cwdBuilding from source
git clone https://github.com/viperadnan-git/nizm.git
cd nizm
cargo build --release
# Binary at target/release/nizmRunning checks
cargo fmt -- --check
cargo clippy -- -D warnings
cargo testLicense
Built with Rust. No runtime dependencies. Just fast hooks.
