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

crazydisk

v1.0.2

Published

Terminal disk usage analyzer

Readme

crazydisk

A terminal disk-usage analyzer inspired by DaisyDisk: scan a directory, see its contents as proportional bars, drill down, and delete straight from the UI (safely, to Trash).

See idea.md for the full spec and v1 scope.

Requirements

  • macOS
  • Node.js 18+ (developed against Node 23)

Install

npm install

Usage

npm start [path]

path defaults to the current directory. Example:

npm start ~/Downloads

Keybindings

| Key | Action | | --------------- | ---------------------------------------- | | / | Move selection | | / Enter | Open the selected folder | | / Backspace| Go up one level | | / | Filter entries by name (Enter/Esc to exit) | | d / x | Delete the selected entry (with confirm) | | y / n | Confirm / cancel a pending delete | | u | Rescan the current tree (picks up changes made outside the CLI) | | q / Ctrl+C | Quit |

Deleted files are moved to ~/.Trash, not permanently removed.

Long directory listings are capped at 15 entries at a time; select the …N more… row and press /Enter to reveal the next 15.

Development

npm test         # run the BDD (Gherkin) and UI test suites
npm run typecheck
npm run build    # compile to dist/, producing the crazydisk bin

Tests live under tests/:

  • tests/features/*.feature + tests/steps/*.steps.ts — Gherkin specs for the scanner, cache, sort/filter, and navigation logic (via jest-cucumber).
  • tests/scan-cache-integration.test.ts — proves the cache actually skips filesystem work: no readdir calls on an unchanged re-scan or a deep content-only change, and exactly one readdir for the directory whose entries actually changed.
  • tests/app-smoke.test.tsx — renders the real Ink UI (via ink-testing-library) against a temp directory and drives the golden path: scan → sort → drill down → filter → delete.

How the cache works

scanDirectory(path, { useCache: true }) (used by the CLI) persists the scanned tree to ~/.cache/crazydisk/ (overridable via CRAZYDISK_CACHE_DIR), keyed by path with each entry's mtime attached. On a re-scan, a directory whose mtime still matches its cached value is known — per POSIX/APFS semantics — to have the same set of immediate entries, so readdir is skipped for it and the cached child list is reused. Cached file entries under such a directory are trusted outright (no lstat at all); cached directories are still recursed into, so structural changes anywhere deeper in the tree are always detected correctly.

Trade-off: a directory's mtime only changes when an entry is added, removed, or renamed — not when an existing file's content changes in place. So editing a file without touching its siblings won't have its new size picked up until something else invalidates that directory (e.g. a sibling file being added/removed, or the cache being cleared). This favors speed on large, mostly-static trees (e.g. node_modules, ~/Projects) over catching every possible in-place edit.

The cache file itself (a full-tree JSON blob) is only rewritten when something in the tree actually changed, so a no-op re-scan skips the readdir calls, the per-file lstat calls, and the cache write.

Known limitations / not yet implemented

  • Scanning is single-threaded. The original spec called for worker threads to parallelize the walk; the current scanDirectory walks recursively on the main thread.
  • Whole-disk scanning, a treemap/sunburst view, and cross-platform support remain out of scope for v1 (see idea.md).