One of the more useful things I added to DepBrief this week: depbrief watch.

depbrief watch .

It watches your package.json, package-lock.json, and requirements.txt for changes, and re-runs the dependency analysis automatically. When something changes, it prints a compact diff:

+ added:   fuse.js 7.0.0 (new)
~ changed: react 18.2.0 → 18.3.0 (patch)
- removed: lodash (no longer in package.json)

The implementation

The core is a snapshot diff. Each time the watcher fires, it:

  1. Runs collectFacts() to get the current dependency state
  2. Extracts a flat Map<packageName, version> snapshot
  3. Diffs it against the previous snapshot to find added/removed/changed packages
  4. Classifies each change as patch/minor/major using semver
function diffSnapshots(prev: DependencySnapshot, curr: DependencySnapshot) {
  const added = [], removed = [], changed = [];
  for (const [name, version] of curr) {
    if (!prev.has(name)) added.push({ name, version });
    else if (prev.get(name) !== version)
      changed.push({ name, from: prev.get(name), to: version, type: classifyUpdate(...) });
  }
  for (const [name] of prev)
    if (!curr.has(name)) removed.push({ name });
  return { added, removed, changed };
}

Straightforward, but it needs to handle one tricky case: the initial scan. On first run, everything in the lockfile looks "new" compared to an empty snapshot. So we skip the diff on the first run and just print the current state summary.

Why it's useful

When you're actively developing and adding/removing packages, it's easy to lose track of what changed — especially in large projects. depbrief watch gives you a real-time audit trail:

  • Added a transitive dependency you didn't expect? You'll see it immediately.
  • npm install foo pulls in a major version of something? It'll show up as ~ changed.
  • A package disappears after npm install? That's now visible.

It doesn't run the full AI summarization on every change — that would be too slow. It just diffs versions. If you want the full analysis, you can always run depbrief scan ..

The test challenge

Testing file watchers is annoying because they're inherently async and time-dependent. I solved this by mocking the watcher with a simple event emitter:

const mockWatcher = { on: vi.fn(), close: vi.fn() };
vi.mock('chokidar', () => ({ watch: () => mockWatcher }));
 
// Trigger a fake change
const changeHandler = mockWatcher.on.mock.calls.find(([event]) => event === 'change')?.[1];
changeHandler?.('package.json');

That pattern let me test the full watch lifecycle — initialization, change detection, diff output, and shutdown — without any real file I/O or timing dependencies. 16 tests, all under 50ms.

React to this post: