- 🤖 AI-friendly — hierarchical JSON with size, category, and freshness
- 🖥️ Human-friendly — browser UI with treemap, sunburst, and list views
- ⚡ Fast — parallel directory scanning powered by rayon
- 🛡️ Read-only by design — never deletes, never recommends deletions
- 🌐 Cross-platform — macOS, Linux, Windows
duvis (pronounced /ˈduːvɪs/, like "doo-vis") is a fast, read-only disk usage analyzer that helps both AI agents and humans understand what's filling their disk. Point it at any directory and it gives you two ways to look at it:
- A CLI that emits a structured JSON tree, a category summary, or a colorized terminal tree — easy to pipe into agents, scripts, or
jq. - A browser UI (React + d3) with treemap, sunburst, and list views, color-coded by category, so you can drill in visually.
Every entry is auto-tagged by category — cache, build, log, vcs, media, ide — so "what's eating my disk?" shows up at a glance. duvis only shows you the picture; deleting is your call to make with your own tools.
Install
Or build from source:
Usage
# Tree view of the current directory
# Limit depth and show top N entries
# Category-aware summary (cache / build / log / media / vcs / ide / other)
# Structured JSON output (for AI agents and scripts)
# Open browser UI with an interactive treemap
Options
| Flag | Description |
|---|---|
-d, --depth <N> |
Maximum depth to display |
-n, --top <N> |
Show only the top N entries by size |
--json |
Output as JSON |
--analyze |
Show a per-category size summary |
--ui |
Open browser UI with treemap visualization |
--port <PORT> |
Port for UI server (default: 7515, see below). Falls back to a free port if busy. |
--sort <size|name> |
Sort order (default: size) |
--reverse |
Reverse sort order |
--hardlinks <count-once|count-each> |
How to attribute bytes to hardlinked files (default: count-once, matches du). |
--json / --analyze / --ui are mutually exclusive; pass at most one. With none, the default tree view is shown.
Output examples
Tree
project (438.9 MB)
├── target/ [build] 438.8 MB
├── .git/ [vcs] 54.7 KB
├── src/ 24.5 KB
└── Cargo.toml 418 B
Analyze
Total: 438.9 MB
Category Summary:
build 438.9 MB 100% 1 items
JSON
Categories
duvis classifies entries into a small core vocabulary that always
appears in the legend, plus a handful of extended categories that show
up only when something matches them. Core stays compact for typical
project trees; extended pops into view when relevant (e.g. a model_cache
row appears on an AI dev machine, an installer row appears in
~/Downloads).
Core
cache— package and tool caches (node_modules/,.cache/,__pycache__/,.cargo/, ...)build— build artifacts (target/,dist/,build/,.next/, ...)log— log files (*.log,logs/, ...)media— images, video, audiovcs— version control metadata (.git/, ...)ide— IDE/editor metadata (.idea/,.vscode/, ...)other— everything else
Extended (shown only when present)
archive— compressed bundles (*.zip,*.tar.gz,*.7z,*.zst, ...)installer— install packages (*.dmg,*.pkg,*.exe,*.deb,*.AppImage, ...)vm_image— virtual machine disk images (*.vdi,*.vmdk,*.qcow2, OrbStack'sdata.img.raw, ...)model_cache— local AI model stores (.ollama/,.lmstudio/,.huggingface/)backup— backups (Time Machine,*.bak,*.backup,*.old)
Categories are assigned by directory or file name. Once a directory is classified
as anything other than other, everything inside it inherits that category —
because that directory is the natural grouping unit (a node_modules is a
single thing, not a heap of unrelated files). The outermost named ancestor
wins, so a project root that happens to contain a giant node_modules is
not itself classified as cache.
How sizes are measured
On Unix, duvis reports the bytes a file actually occupies on disk
(st_blocks × 512, the same default as du). Sparse files like VM images —
for example OrbStack's data.img.raw — show their real footprint, not the
multi-terabyte logical size you'd get from ls -l.
Hardlinked files are counted once per inode by default, also matching du —
a 1 GB file with two hardlinks contributes 1 GB to the total, not 2 GB. The
first path to reach the inode reports the bytes; later links report 0.
Pass --hardlinks count-each to opt out and have every link contribute
its full size (matching tools that don't dedupe by inode).
Windows falls back to apparent size for now and does not dedupe hardlinks.
Why port 7515?
In the spirit of Vite's 5173 (SITE/VITE written
with Roman numerals + leet — V=5, I=1, T=7, E=3), duvis defaults to
7515: drop D (it's basically a closed O), tilt U on its side and
it's a 7, V=5 (Roman), I=1, S=5 (leet). Not assigned to anything in
the IANA registry, far enough from the 8080/3000/5173 clash zones,
and if it's still busy on your machine duvis quietly falls back to a free
OS-assigned port.
License
MIT © Kazuki Yamada