A fast, dependency-free directory-structure exporter — a tree-style CLI that walks a
directory (names and hierarchy only, never file contents) and renders it to 8 formats:
box-drawing tree, Markdown, JSON, YAML, Mermaid, HTML, CSV, and a flat path list.
Where tree stops at plain text, JSON, and XML, arbor adds Markdown, GitHub-rendered
Mermaid, HTML, CSV, and YAML — plus it can build the tree from a path list on stdin
(git ls-files | arbor …), sort names naturally, and treat Windows junctions safely.
$ arbor src
src
├── util
│ ├── log.c
│ └── log.h
├── main.c
└── README.md
1 directory, 4 files
- 8 output formats —
-f tree|markdown|json|yaml|mermaid|html|csv|paths. - Several roots at once —
arbor src tests docsrenders each as its own tree inside one document: the format's header and footer are written once and thetreesummary is a single combined total.jsonis the only format whose shape depends on the count — one root emits the bare object it always has, two or more emit an array of those objects, so existing single-root consumers are unaffected. - Filtering — dotfiles (
-a), glob exclude/include (-e/-i, repeatable, path-aware when the glob contains/;-ikeeps only files — pair with-p/--pruneto drop the directories it empties), depth limit (-L,0= unlimited), directories-only (-d),-p/--pruneto drop empty directories,--filelimit Nto stop descending directories that would show more thanNentries (marked(...)), and--min-size SIZEto drop small files (K/M/Gare powers of 1024). - Detail columns — size (
-s, human or--bytes), mtime (-t), child counts (-c), classify (-F,/for dirs and@for links), and--durecursive directory totals (of the entries shown — filtered-out files are not counted; implies-s).json/yaml/csvcarrysize/mtimeandlink/unreadable/cyclestate too. - Colour —
--color auto|always|never(directories bold blue, unreadable entries red) and--rainbow(a distinct hue per depth level). - Sorting — directories first, then
--sort name|size|mtime|ext|nonewith natural, case-insensitive name order (file2beforefile10);-rto reverse.--sort nonekeeps the order the source produced and skips the directories-first grouping too — "none" means untouched — and--no-dirs-firstdrops that grouping while keeping a sort key. - Symlink-safe — links and directory junctions are leaves by default;
-l/--follow-symlinksopts in, guarded against cycles (resolves reparse points on Windows, symlinks on POSIX). - Path-list input —
--from-file FILE(-= stdin): build the tree from a newline list, e.g.git ls-files | arbor --from-file -. A trailingdirsets the root label and the base for resolving listed paths (… | arbor --from-file - myrepo→ a labelled tree with real sizes). Every format and filter still applies. --print0forxargs -0-friendly paths output.- Provenance —
--metastamps the arbor version, a UTC timestamp, and the root list into the output:jsonwraps the tree as{"arbor": {…}, "tree": …},csvemits#arbor …preamble lines before the header row. Opt-in precisely because both change the shape a consumer parses; without the flag the output is byte-for-byte what it was. - Per-format polish —
mermaidmarks directory nodes with anarborDirectoryclass (a name-only node otherwise cannot be told from a file) and--no-fenceemits the bare graph for a.mmdfile;htmlnames the root in<title>/<h1>and--collapsedrenders every directory folded. - Output control — write to a file (
-o FILE), ASCII connectors (--ascii), plain indent without connectors (--no-lines), and omit the trailing summary (--no-summary).
Run arbor -h/--help for the full flag list, arbor --version for the version.
Mermaid renders live on GitHub:
graph TD
n0["src"]
n0 --> n1
n1["util"]
n1 --> n2
n2["log.c"]
n1 --> n3
n3["log.h"]
n0 --> n4
n4["main.c"]
n0 --> n5
n5["README.md"]
JSON carries size/mtime and link/unreadable/cycle state when requested:
{ "name": "src", "type": "directory", "children": [
{ "name": "util", "type": "directory", "children": [
{ "name": "log.c", "type": "file", "size": 20 },
{ "name": "log.h", "type": "file", "size": 19 }
] },
{ "name": "main.c", "type": "file", "size": 26 },
{ "name": "README.md", "type": "file", "size": 7 }
] }html emits a self-contained page with collapsible (<details>) directories; markdown,
yaml, csv, and paths render the same tree in their respective shapes.
arbor -s -t --sort size -r src # sizes + dates, largest first
arbor -f json -o tree.json src # export JSON to a file
arbor -L 2 -d src # directories only, two levels deep
git ls-files | arbor --from-file - # render tracked files as a treeNo external dependencies — just the C standard library and libm. Requires a C23 compiler
(gcc 14+ / clang 18+; older gcc 13 / clang 16-17 work with make CSTD=-std=c2x).
make # -> ./arbor
make test # build and run the unit suite (18 cases)
# or with CMake
cmake -B build && cmake --build build
(cd build && ctest)On Windows use MinGW — mingw32-make, or make from Git Bash / MSYS2. MSVC is not supported
(no C23). arbor is a single self-contained executable: copy arbor (or arbor.exe) anywhere
on your PATH to install it.
arbor is built on a small, self-contained subset of a larger C framework ("CFW"). Rather than
depend on that framework, the exact modules arbor uses (memory, strings, containers, dir/
file, datetime, argparse, …) are bundled under include/, so this repository builds
standalone. Two headers there carry a tiny local edit to keep the subset free of an unrelated
math/SIMD dependency — see the comments in include/util/memory/memory.h and
include/math/scalar.h.
include/ is a vendored snapshot periodically refreshed from upstream CFW; prefer patches
against src/, and flag any include/ fix so it can be upstreamed rather than lost on the
next sync.
- Tested on Linux (gcc/clang) and Windows (MinGW). The default build compiles out its diagnostic scaffolding for speed; it is fuzzed and stress-tested (see test/TESTING.md).
- The walk/renderers recurse one frame per directory level; depth is bounded (PATH_MAX, the
--from-fileline limit, and a hard 4096-level cap), so arbor is safe on a conventional stack. Running it on a sub-1 MB-stack thread with a pathologically deep tree is unsupported.
MIT — see LICENSE. The bundled include/ sources are covered by the same terms.