See your project structure clearly — a tree generator with gitignore-compatible exclusion, multi-format output, and AI-ready summaries.
Project description
tree2guide
Understand your project's structure in seconds.
Generate clean project trees, AI-ready summaries, and reusable documentation from any directory — all with zero dependencies.
Author: Lawrence Roble (@law4percent) Open-source, MIT licensed. Contributions, issues, and pull requests are welcome.
Why this exists
Many developers start building before they’ve had time to study every architectural principle in depth. tree2guide exists to make project structure easier to see, understand, and improve.
It provides a quick way to visualize folder hierarchies, spot signs of architectural drift, and evaluate whether a codebase is maintaining clear separation of concerns — or simply accumulating complexity over time.
What began as a tool for checking project structure quickly became useful for documentation, onboarding, PR descriptions, AI prompts, and general project analysis. The goal, however, remains the same: help developers catch structural problems early, before they become expensive to untangle.
tree2guide starts as a project structure generator, but its long-term vision is to become the foundation for project understanding — providing structured representations that can power documentation, analysis, automation, and intelligent developer tools.
Why not just use tree?
| Feature | tree |
tree2guide |
|---|---|---|
| Markdown output | ❌ | ✅ |
| HTML output (collapsible) | ❌ | ✅ |
| JSON output | ❌ | ✅ |
| YAML output | ❌ | ✅ |
| AI-ready summary | ❌ | ✅ |
.tree2ignore (gitignore-style) |
❌ | ✅ |
| Python library API | ❌ | ✅ |
| Cross-platform | ⚠️ | ✅ |
| Zero dependencies | N/A | ✅ |
Example Output
Running tree2guide src --stdout:
src/
├── tree2guide/
│ ├── renderers/
│ │ ├── __init__.py
│ │ ├── html.py
│ │ ├── json_renderer.py
│ │ ├── llm.py
│ │ ├── markdown.py
│ │ ├── text.py
│ │ └── yaml_renderer.py
│ ├── __init__.py
│ ├── cli.py
│ ├── ignore.py
│ ├── llm.py
│ └── scanner.py
Running tree2guide . --llm --stdout:
============================================================
PROJECT STRUCTURE SUMMARY: tree2guide
============================================================
DETECTED STACK / LANGUAGE:
- Python (pyproject.toml)
SIZE:
Files : 18
Directories: 4
TOP-LEVEL LAYOUT:
Directories:
src/
tests/
docs/
Files:
pyproject.toml
README.md
LICENSE
NOTABLE FLAGS:
- Tests directory present
- GitHub Actions / workflows present
- LICENSE file present
- CHANGELOG present
Tested Environments
Verified on Windows 11, macOS 26.2, and Ubuntu via CI. See Tested Environments for full details and known platform-specific issues.
Philosophy
tree2guide follows a few simple principles:
- One scan, many outputs. The filesystem is walked once; any renderer can consume the result.
- Zero third-party dependencies. Always.
pip install tree2guideis instant. - Human-readable first. Markdown is the default because humans read it everywhere.
- AI-ready by design. The
--llmflag produces structured output optimised for AI context windows. - CLI and library. Every feature available on the command line is also available as a Python API.
- Never reads file contents. Only structure and filenames — your code stays on your machine.
Privacy
tree2guide operates entirely on your local machine.
It does:
- ✅ Read directory structure and filenames
- ✅ Apply ignore rules
- ✅ Generate output locally
It does not:
- ❌ Read file contents
- ❌ Upload anything
- ❌ Require an internet connection
- ❌ Collect telemetry
- ❌ Send data to any AI service
Installation
pip install tree2guide
Requires Python 3.9+, no other dependencies. Installs a tree2guide command directly — no python prefix needed.
Development setup (for contributors)
macOS / Linux
cd tree2guide
python3 -m venv venv
source venv/bin/activate
pip install -e ".[dev]"
Windows
cd tree2guide
python -m venv venv
venv\Scripts\activate
pip install -e ".[dev]"
Quick Start
# Markdown output (default) — writes docs_tree.md
tree2guide docs
# Plain text
tree2guide . --format text
# JSON
tree2guide . --format json
# Self-contained HTML with collapsible folders
tree2guide . --format html
# AI-ready project summary (no network call, no API key)
tree2guide . --llm
# Print to stdout instead of writing a file
tree2guide . --stdout
# Pipe to clipboard
tree2guide . --stdout | pbcopy # macOS
tree2guide . --stdout | clip # Windows
Command Reference
tree2guide <target_folder> [options]
| Flag | Description |
|---|---|
-o, --output |
Output file path (default: <folder>_tree.<ext> based on format) |
--exclude-file |
Use a specific exclude file instead of auto-detecting .tree2ignore |
--title |
Add a heading above the tree |
--no-footer |
Omit the author/license footer (markdown and html only) |
--stdout |
Print to stdout instead of writing a file |
--format {markdown,text,json,yaml,html,llm} |
Output format (default: markdown) |
--llm |
Shorthand for --format llm — AI-friendly project summary |
--max-depth N |
Limit recursion depth |
--dirs-only |
Only show directories |
--files-only |
Only show files (directories shown as scaffolding only) |
--no-hidden |
Skip dotfiles/dotfolders without needing .tree2ignore entries |
--sort {dirs-first,files-first,alpha} |
Sort order within each folder (default: dirs-first) |
--no-progress |
Suppress scan progress and completion telemetry (stderr only) on large directories |
--version |
Print the installed version and exit |
Symlinks are rendered as name -> target and never followed — avoids infinite loops.
On large directories, tree2guide prints periodic progress (Scanning... N files, N dirs) and a final Scan complete. summary to stderr — never stdout — so piping (--stdout | your-tool) is unaffected. Fast scans print nothing extra. Use --no-progress to suppress this entirely.
Default output filenames
| Format | Output file |
|---|---|
markdown |
<folder>_tree.md |
text |
<folder>_tree.txt |
json |
<folder>_tree.json |
yaml |
<folder>_tree.yaml |
html |
<folder>_tree.html |
llm |
<folder>_llm.txt |
The .tree2ignore file
The #1 rule: where it goes
.tree2ignore must sit directly inside the folder you pass as the target.
Patterns are written relative to that folder — never include the target folder's name as a prefix.
# .tree2ignore lives at docs/.tree2ignore
tree2guide docs
# correct — relative to docs/
api
_drafts
# wrong — only correct if you ran: tree2guide .
docs/api
docs/_drafts
Pattern syntax (real .gitignore rules)
| Pattern | Meaning |
|---|---|
build |
Matches a file or folder named build at any depth |
build/ |
Same, but only matches directories |
/build |
Matches build only at the root of the target (anchored) |
*.log |
Matches all files ending in .log, anywhere |
**/android/key.properties |
Matches that exact path at any depth |
!keep-this |
Negates — re-includes something an earlier rule excluded |
# comment |
Ignored |
Patterns evaluate top to bottom — later rules override earlier ones, same as Git.
Whitelist example
Show only admin/, hide everything else:
*
!admin
!admin/**
Copy your .gitignore directly
The matching engine reimplements Git's rules exactly — copy your .gitignore into .tree2ignore and it will behave identically.
Defaults (always excluded, no setup needed)
.git.tree2ignore__pycache__*.pyc.DS_Store
Library API
tree2guide works as an importable library, not just a CLI:
import tree2guide
from pathlib import Path
root = Path("./my-project").resolve()
patterns = tree2guide.load_exclude_patterns(root / tree2guide.EXCLUDE_FILENAME)
matcher = tree2guide.ExcludeMatcher(patterns)
# Build the tree once
tree = tree2guide.build_node_tree(root, matcher)
# Render to any format
print(tree2guide.render_markdown(tree, title="My Project"))
print(tree2guide.render_text(tree))
print(tree2guide.render_json(tree))
print(tree2guide.render_yaml(tree))
print(tree2guide.render_html(tree, title="My Project"))
print(tree2guide.render_llm(tree, title="My Project"))
# Get the heuristic summary directly
summary = tree2guide.analyze(tree)
print(summary.detected_stack)
print(summary.file_count, summary.dir_count)
print(summary.notable_flags)
Public API surface
| Name | Description |
|---|---|
build_node_tree(root, matcher, options=None, on_progress=None) |
Scan filesystem → TreeNode. on_progress(files, dirs), if given, is called at most once per second during the walk — pure instrumentation, None by default with zero effect |
build_tree(root, matcher, options=None) |
Backward-compatible wrapper → list[str] |
TreeNode |
Internal tree model (dataclass) |
TreeOptions |
Scanner options (max_depth, dirs_only, sort, etc.) |
ExcludeMatcher |
Evaluates .tree2ignore patterns |
GitignoreRule |
Single compiled pattern rule |
load_exclude_patterns(path) |
Parse a .tree2ignore file |
analyze(node) |
Heuristic analysis → LlmSummary |
LlmSummary |
Detected stack, file/dir counts, notable flags |
render_markdown(tree, ...) |
Markdown string |
render_text(tree, ...) |
Plain text string |
render_json(tree) |
JSON string |
render_yaml(tree) |
YAML string |
render_html(tree, ...) |
Self-contained HTML string |
render_llm(tree, ...) |
LLM-optimised plain text string |
Project Structure
tree2guide/
├── .github/
│ └── workflows/
│ └── ci.yml
├── docs/ # GitHub Pages docs site
│ ├── _config.yml
│ ├── index.md
│ ├── installation.md
│ ├── quickstart.md
│ ├── tree2ignore.md
│ ├── cli.md
│ ├── examples.md
│ ├── api.md
│ └── contributing.md
├── src/
│ └── tree2guide/
│ ├── __init__.py # public API surface
│ ├── cli.py # argparse + entry point
│ ├── ignore.py # GitignoreRule, ExcludeMatcher
│ ├── llm.py # analyze(), LlmSummary
│ ├── scanner.py # build_node_tree(), TreeNode, TreeOptions
│ └── renderers/
│ ├── __init__.py
│ ├── html.py
│ ├── json_renderer.py
│ ├── llm.py
│ ├── markdown.py
│ ├── text.py
│ └── yaml_renderer.py
├── tests/
│ ├── test_ignore.py
│ ├── test_llm.py
│ ├── test_markdown_renderer.py
│ ├── test_renderers.py
│ └── test_scanner.py
├── CHANGELOG.md
├── LICENSE
├── README.md
└── pyproject.toml
Architecture: Scanner → Tree Model → Renderer. The scanner builds a TreeNode once; each renderer transforms the same object into a different format. Adding a new output format means writing a new renderer — no changes to the scanner or CLI needed.
Troubleshooting
"No exclude file found at .../.tree2ignore"
→ .tree2ignore must be directly inside the folder you passed as the target.
Patterns aren't excluding anything
→ Check for a stray prefix. If you ran tree2guide docs, write api not docs/api.
I want to exclude everything except one folder
*
!keepme
!keepme/**
--stdout produces garbled characters on Windows
→ Pipe directly to clipboard instead: tree2guide . --stdout | clip
Contributing
Pull requests, feature ideas, and bug reports are welcome. See CONTRIBUTING for the full guide, architecture walkthrough, and how to add a new renderer or stack signal.
Open stretch goals:
- Browser playground (Pyodide/WASM)
- Interactive HTML viewer from JSON output
--format csv— flat path list with type column- File size annotations
- Character class patterns in
.tree2ignore([abc],[!abc]) - Windows CI in the test matrix
License
MIT — see LICENSE for details.
Built and maintained by Lawrence Roble.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file tree2guide-1.2.0.tar.gz.
File metadata
- Download URL: tree2guide-1.2.0.tar.gz
- Upload date:
- Size: 35.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
782e2fc6424eca4ee79118619397e5bee168786f8324d9cc7b2625eea66a06f3
|
|
| MD5 |
2a5cd2a0c56e5272b96a4f8f3a12853b
|
|
| BLAKE2b-256 |
090193d85bc5f36e33c016b22141f4890ced69aa171279ce1deb0af426f5b7c2
|
File details
Details for the file tree2guide-1.2.0-py3-none-any.whl.
File metadata
- Download URL: tree2guide-1.2.0-py3-none-any.whl
- Upload date:
- Size: 27.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b856d25428ff207189da6ec9c05b490640e99831ba2a34f559c2d9cb97b1d0fb
|
|
| MD5 |
dc65783b1a1f1b309e0163fe32286324
|
|
| BLAKE2b-256 |
3bb7a73ed9edf1908b9ba2791925d2fa4c8380791f5a549b77a945080a3703a4
|