---
name: folder-graph
description: Render the contents of a folder as an interactive D3 force-directed graph (Obsidian-style) in a single self-contained HTML file. Nodes are files and folders; edges show parent-child containment. Use when the user asks to "visualize this folder", "graph this directory", "make an obsidian-style graph of X", "show this folder as a graph", "render directory tree as a graph", "d3 graph of these files", or wants a clickable HTML map of a folder's structure. The output is one HTML file that opens in any browser with zoom/pan, drag, search, hover labels, and a color legend by file extension.
---

# Folder Graph

## What this does

One command turns a folder into a single self-contained HTML file containing an Obsidian-style D3 force graph:

- **Nodes** = every file and folder under the target directory (after ignore filters).
- **Edges** = parent → child containment only (no link parsing).
- **UI** = zoom + pan, drag nodes, hover labels, search box, color legend by extension, click to focus a node + its neighbors.
- **D3 v7** loaded from CDN; no other runtime deps. The HTML works offline once D3 is cached.

## Usage

Requires Node ≥ 18.3 (no npm install needed — stdlib only).

From the project root (or anywhere — pass an absolute or relative path):

```bash
node .claude/skills/folder-graph/scripts/build_graph.mjs <folder>
```

Default output is `<folder>/folder-graph.html`. Override with `-o`:

```bash
node .claude/skills/folder-graph/scripts/build_graph.mjs ./docs -o /tmp/docs-graph.html
```

Flags:

- `-o, --output <path>` — output HTML path (default `<folder>/folder-graph.html`).
- `--ignore <name>` — extra directory/file name to skip (repeatable). Adds to defaults.
- `--no-default-ignores` — drop the built-in skip list (`.git`, `node_modules`, `dist`, `.next`, `__pycache__`, etc.).
- `--max-files <N>` — cap file nodes for usability (default 5000; folders aren't capped).
- `-h, --help` — show usage.

The script automatically excludes the output HTML itself from the walk, so re-runs over the same folder stay clean.

Open the result:

```bash
open <folder>/folder-graph.html
```

The HTML is a result artifact — preview it through the user's hot-reload preview per `~/.agents/docs/artifact-preview.md` (`tmux:servers` window `artifact-preview`, default port `4174`) rather than `open`ing it directly when the user is around to look at it.

## What gets ignored by default

The script skips these directories so big repos stay readable:

`.git`, `.hg`, `.svn`, `node_modules`, `.next`, `.nuxt`, `.turbo`, `.vercel`, `dist`, `build`, `out`, `.output`, `.cache`, `.parcel-cache`, `__pycache__`, `.venv`, `venv`, `env`, `.tox`, `.pytest_cache`, `.mypy_cache`, `.ruff_cache`, `.idea`, `.vscode`, `.DS_Store`, `target`, `coverage`, `.nyc_output`, `vendor`.

Use `--ignore <name>` to add more, or `--no-default-ignores` to keep everything.

## Layout

```
folder-graph/
├── SKILL.md
├── scripts/
│   └── build_graph.mjs       # walks folder, injects nodes/links into the template
└── assets/
    └── graph.template.html   # D3 force-graph template with {{TITLE}} and {{DATA_JSON}} placeholders
```

The script reads the template, substitutes the JSON payload + title, and writes the output HTML. To tweak the look (colors, force parameters, legend, hint text), edit `assets/graph.template.html` directly — no build step.

## Customization knobs in the template

Inside `assets/graph.template.html`:

- `palette` — color map keyed by extension (`md`, `ts`, `py`, …) and the special `folder` / `other` keys.
- `radiusFor(node)` — node size by kind and depth.
- The `d3.forceSimulation` block — link distance, charge strength, collision radius.
- Default label visibility rule: folders and depth-1 nodes show their name; deeper files only show on hover. Adjust the `display` attr on the node `text` selection to change this.

## Known limits

- Folder containment only — no markdown/wiki-link or code-import edges (by design; the user picked this scope).
- Very large trees (>5000 files) get truncated; raise `--max-files` if needed but expect slowdowns past ~10k nodes.
- D3 is loaded from `cdn.jsdelivr.net`. If the user opens the HTML fully offline before the CDN has ever been cached, the graph won't render — vendor D3 locally if that becomes a real constraint.
