#linter #git #formatter

app nizm

Lightweight, zero-config alternative to pre-commit

9 releases

Uses new Rust 2024

0.3.1 Mar 18, 2026
0.3.0 Mar 1, 2026
0.2.2 Mar 1, 2026
0.1.3 Feb 28, 2026

#595 in Command line utilities

MIT license

115KB
2.5K SLoC


nizm

nizm

Lightweight, zero-config pre-commit hooks

Crates.io npm CI License

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 461ms

Features

  • 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-diagnosingnizm doctor verifies 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 hook

That's it. Your next git commit runs your hooks automatically.

Installation

npm / bun / pnpm / yarn
npm install -g nizm-cli

Platform-native binary — zero Node.js overhead at runtime.

Cargo (from source)
cargo install nizm
Prebuilt binaries

Download 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 installed

Pass 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, rustfmt and clippy are 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 passed

nizm ls

Lists all configured hooks across discovered manifests.

$ nizm ls
Cargo.toml
  clippy   cargo clippy --fix --allow-dirty -- -D warnings  *.rs
  rustfmt  cargo fmt                                        *.rs

nizm recover

Restores your working tree from a rescue snapshot after a failed stash recovery.

$ nizm recover
working tree restored from rescue snapshot

If 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.toml

Environment 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 recover

Parallel 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 cwd

Building from source

git clone https://github.com/viperadnan-git/nizm.git
cd nizm
cargo build --release
# Binary at target/release/nizm

Running checks

cargo fmt -- --check
cargo clippy -- -D warnings
cargo test

License

MIT


Built with Rust. No runtime dependencies. Just fast hooks.

Dependencies

~5.5–9MB
~168K SLoC