Reading diff of lock files while upgrading packages can result painful and time draining. Thousands of lines of churn for a handful of real changes. lockdiff is a human-oriented package diff parser. It takes two lockfiles, returns what was added, removed, and bumped.
Works on uv.lock (Python) and package-lock.json (npm).
Read more at Drawing Board
uv:
npm:
Recommended (isolated, on PATH):
pipx install git+https://github.com/Basliel25/lockdiffOr in a venv:
python -m venv .venv && .venv/bin/pip install git+https://github.com/Basliel25/lockdiffFrom a clone, for development:
git clone https://github.com/Basliel25/lockdiff && cd lockdiff
pip install -e .Once installed:
lockdiff old.lock new.lockWithout installing (from the repo's src/ directory):
python -m lockdiff old.lock new.lockThe ecosystem is auto-detected from file contents. Override if needed:
python -m lockdiff --ecosystem npm old.json new.jsonExit codes: 0 if there are no changes, 1 if there are changes, 2 on error.
docker build -t lockdiff .
docker run --rm -v "$PWD:/work" lockdiff /work/old.lock /work/new.locksrc/lockdiff/
├── __main__.py CLI: argparse → detect → parse → diff → render
├── detect.py Sniffs first non-whitespace byte: '{' = npm, else uv
├── Parser.py Parses uv.lock (TOML) → dict[name, Package]
├── Parser_npm.py Parses package-lock.json → dict[name, Package]
├── diff.py Set diff by name; same-name + different-version = bump
└── render.py ANSI-colored boxes and a summary line
Both parsers return the same shape (dict[str, Package]), so diff and render don't care which ecosystem the input came from. npm trees often hold multiple versions of the same package; lockdiff collapses them per name (direct beats transitive, then highest version wins).

