npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

nizm-cli

v0.4.0

Published

Lightweight, zero-config git hooks

Readme

Lightweight, zero-config git 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, linters, and message checks at every git hook stage — pre-commit, commit-msg, prepare-commit-msg, and pre-push. 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 install -g nizm-cli

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

cargo install nizm

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 | Type | Required | Description | | :-------- | :--------------- | :------- | :------------------------------------------------------------------- | | cmd | string | yes | Shell command to run. Use {staged_files} to receive the file list. | | glob | string | list | no | Filter staged files by pattern. | | outputs | string | list | no | Files produced/modified by the hook to auto-stage. | | type | enum | no | Git stage: 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.

Command placeholders

Inside cmd, the following tokens are substituted before the shell runs the command:

| Placeholder | Expands to | | :--------------- | :------------------------------------------------------------------------ | | {staged_files} | Space-separated list of scoped staged files (shell-escaped). | | {1}, {2}, … | Positional arguments forwarded from git (1-based, shell-escaped). |

The positional args mirror what git passes to its hook script. Out-of-range references expand to an empty string. Unknown {name} tokens are left untouched.

| Hook type | {1} | {2} | {3} | | :------------------- | :--------------------- | :-------- | :---- | | pre-commit | (none) | (none) | (none) | | commit-msg | path to message file | (none) | (none) | | prepare-commit-msg | path to message file | source | sha | | pre-push | remote name | remote url | (none) |

[tool.nizm.hooks]
ruff       = { cmd = "ruff check {staged_files}", glob = "*.py" }
commitlint = { cmd = "commitlint --edit {1}", type = "commit-msg" }
audit      = { cmd = "trivy fs --remote {1} .", type = "pre-push" }

Glob syntax

glob and outputs accept either a single pattern string or a list of patterns:

glob = "*.py"                                    # single pattern
glob = ["*.py", "!**/migrations/**"]             # list (! = exclude)
glob = "*.{js,jsx,ts,tsx}"                       # brace alternation
outputs = ["dist/**", "*.min.js"]                # auto-stage generated files

Supported syntax (per pattern):

| Pattern | Meaning | | :------ | :------ | | * | Any chars except / | | ? | Single char except / | | ** | Any number of path segments | | [abc], [a-z], [!abc] | Character classes | | {a,b,c} | Brace alternation (nests allowed) | | !pattern | Exclude prefix (only valid as the first char of an entry) |

Bare patterns without / match at any depth — *.rs is equivalent to **/*.rs. Anchor with a / to restrict depth: src/*.rs matches only direct children of src/.

Excludes always win: if any ! pattern matches, the file is filtered out regardless of include order.

Commands

| Command | What it does | | :---------------------------- | :------------------------------------------------------ | | nizm init | Detect dev-deps and inject hook config | | nizm install | Bake the git hook script into .git/hooks/ | | nizm run | Execute hooks (what the git hook calls) | | nizm ls | Print configured hooks | | nizm doctor | Diagnose hook health | | nizm recover | Restore working tree from rescue snapshot | | nizm uninstall | Remove hook scripts (and optionally config) |

nizm init

Scans dev-dependencies, suggests hooks, and injects them into your manifest. Pass hook names as arguments to skip the interactive picker (nizm init ruff prettier).

$ nizm init
  added clippy       cargo clippy --fix --allow-dirty -- -D warnings
  added rustfmt      cargo fmt

  Cargo.toml — [clippy, rustfmt]
pre-commit hook installed

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 script into .git/hooks/ that calls nizm run. Existing non-nizm hooks are preserved.

| Flag | Description | | :---------------- | :----------------------------------------------------------- | | --config <PATH> | Bake a specific manifest path (repeatable, skips the picker) | | --parallel | Bake the --parallel flag into the hook script | | --force | Overwrite a modified nizm block without prompting |

$ nizm install
scanning for manifests...
  pyproject.toml — [ruff, mypy]
pre-commit hook installed

nizm run

Executes hooks against staged files. Called by the git hook script — you usually don't run this directly. Pass HOOK to run a single hook by name; pass -- ARGS... to forward positional args to {1}, {2}, … in cmd.

| Flag | Description | | :------------------- | :------------------------------------------------------- | | --config <PATH> | Explicit manifest paths (repeatable, skips auto-discovery) | | --hook-type <TYPE> | Hook type to run (default: pre-commit) | | --parallel | Run manifests concurrently | | --all | Run against all tracked files instead of staged |

$ nizm run
nizm: running against 5 staged files
  clippy 5 files (780ms)
  rustfmt 5 files (210ms)
nizm: done in 991ms

nizm ls

Prints every configured hook across all discovered manifests.

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

nizm doctor

Diagnoses hook health — checks hook scripts, config validity, and tool availability.

$ nizm doctor
hooks
  pre-commit (nizm-managed) ✓
  └ Cargo.toml ✓
     ├ clippy (cargo) ✓
     └ rustfmt (cargo) ✓

all 4 checks passed

Exits non-zero if any check fails — safe to wire into CI.


nizm recover

Restores your working tree from the rescue snapshot saved before a failed stash operation.

$ nizm recover
working tree restored from rescue snapshot

If recovery produces conflicts, resolve them manually — the rescue ref (refs/nizm-backup) is cleaned up automatically once the restore succeeds.


nizm uninstall

Removes nizm-managed blocks from .git/hooks/ scripts.

| Flag | Description | | :-------- | :--------------------------------------------------- | | --purge | Also strip [tool.nizm.hooks] blocks 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 to any non-empty value |

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.