Translate between ASCII 'tree' text, JSON/YAML tree specs, and real directory structures.
Project description
🪵 fs-tree-spec (tree_adapter)
Translate between ASCII “tree” text, JSON/YAML tree specs, and real directory structures.
This tiny Python utility helps you describe, version, and reproduce directory layouts in a portable, human-readable way.
✨ Features
- Parse ASCII "tree" text (like
treecommand output) → nested Python structure - Scan existing directories (
--from-fs) → generate tree specs - Serialize / deserialize JSON and YAML tree specs
- Render a Unicode tree from structured data
- Validate naming rules (strict enforcement, no invalid characters)
- Compute a dry-run plan (
--plan) of what would be created - Apply (
--apply) to actually build the filesystem - Smart defaults: ignores
__pycache__,.git,node_modules, etc. - Optional YAML support via
pip install fs-tree-spec[yaml]
🧩 Installation
pip install fs-tree-spec
# or with YAML support:
pip install fs-tree-spec[yaml]
After installation, the CLI command fs-tree-spec becomes available.
🧭 Usage (CLI)
Basic
# Plan creation from an ASCII spec
fs-tree-spec --from-ascii second_brain_tree.txt --root ./second-brain --plan
# Apply (actually create directories/files)
fs-tree-spec --from-ascii second_brain_tree.txt --root ./second-brain --apply
# Print as normalized ASCII and JSON
fs-tree-spec --from-ascii second_brain_tree.txt --print-tree --print-json
From JSON or YAML
fs-tree-spec --from-json my_tree.json --plan
fs-tree-spec --from-yaml my_tree.yaml --apply
From Existing Directory
Generate a tree spec by scanning an existing directory structure:
# Generate ASCII tree from current directory
fs-tree-spec --from-fs . --print-tree
# Generate JSON spec from a project
fs-tree-spec --from-fs ./my-project --print-json
# Generate YAML spec
fs-tree-spec --from-fs ./src --print-yaml
# Exclude root directory name from output
fs-tree-spec --from-fs ./my-project --print-tree --no-root-label
Default ignore patterns: __pycache__, *.pyc, .git, .venv, node_modules, .pytest_cache, .mypy_cache, .ruff_cache, *.egg-info, .DS_Store
# Custom ignore patterns
fs-tree-spec --from-fs . --print-tree --ignore "*.log" "tmp/" "build/"
# Disable default ignores (include everything)
fs-tree-spec --from-fs . --print-tree --ignore
📜 Example Input
ASCII tree text
second-brain/
├─ .env # API keys + config (not committed)
├─ pyproject.toml
├─ README.md
├─ data/
│ ├─ raw/
│ │ └─ apple_notes_export/
│ ├─ attachments/
│ │ └─ ...
│ ├─ processed/
│ │ ├─ notes.jsonl
│ │ ├─ attachments.jsonl
│ │ └─ urls.jsonl
└─ notebooks/
└─ exploration.ipynb
Equivalent JSON
{
"second-brain": {
".env": null,
"pyproject.toml": null,
"README.md": null,
"data": {
"raw": { "apple_notes_export": {} },
"attachments": {},
"processed": {
"notes.jsonl": null,
"attachments.jsonl": null,
"urls.jsonl": null
}
},
"notebooks": {
"exploration.ipynb": null
}
}
}
Equivalent YAML
second-brain:
.env: null
pyproject.toml: null
README.md: null
data:
raw:
apple_notes_export: {}
attachments: {}
processed:
notes.jsonl: null
attachments.jsonl: null
urls.jsonl: null
notebooks:
exploration.ipynb: null
🧠 Naming Rules (Enforced)
- Names are single path segments (no
/or\). - Names must be non-empty after trimming.
- Names cannot be
.,..,..., or…. - Names cannot contain tree/box characters: `├└│─|+```
- Inline comments begin with
" #"and are ignored. "..."and"…"lines are treated as placeholders only.- Conflicts between file vs. directory at same level raise an error.
🧪 Python API Example
Create from spec
from tree_adapter import parse_ascii_tree, plan_fs_from_tree, create_fs_from_tree
ascii_spec = Path("second_brain_tree.txt").read_text()
tree = parse_ascii_tree(ascii_spec)
# Inspect
print(plan_fs_from_tree(tree, "./second-brain"))
# Create directories and files
create_fs_from_tree(tree, "./second-brain", create_files=True)
Generate from existing directory
from tree_adapter import from_fs, to_ascii_tree, to_json, to_yaml
# Scan a directory
tree = from_fs("./my-project", include_root_label=True)
# Convert to different formats
print(to_ascii_tree(tree))
print(to_json(tree))
print(to_yaml(tree)) # requires PyYAML
# Custom ignore patterns
tree = from_fs("./src", ignore_patterns=["*.log", "tmp/"])
# Include everything (no filtering)
tree = from_fs("./src", ignore_patterns=[])
🛠️ Development
# Clone and set up development environment
git clone https://github.com/swoodeng/fs-tree-spec.git
cd fs-tree-spec
# Install dependencies with uv
uv sync --all-extras --dev
# Run tests
uv run pytest
# Run linters
uv run ruff check .
uv run black --check .
uv run mypy tree_adapter.py
Run locally:
uv run fs-tree-spec --from-ascii second_brain_tree.txt --plan
📦 Packaging
This project uses PEP 621 metadata and can be built with:
python -m build
⚖️ License
MIT License © 2025 Stephen Wood
💡 Inspiration
treecommand output formatting- JSON/YAML for reproducible data models
- Cross-tool “infrastructure as text” patterns
🪴 Example Workflows
Spec → Filesystem
- Write a tree spec in a
README.mdfor documentation. - Extract the snippet and feed it to
fs-tree-spec --from-ascii. - Plan or apply to generate the directory structure for new projects.
- Optionally export the same spec to JSON or YAML for automation.
Filesystem → Spec
- Scan an existing project with
fs-tree-spec --from-fs ./project --print-tree. - Document by copying the output into your README.
- Export to JSON/YAML for programmatic use or version control.
- Recreate the structure elsewhere with
--apply.
"Describe once. Recreate anywhere."
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 fs_tree_spec-0.2.0.tar.gz.
File metadata
- Download URL: fs_tree_spec-0.2.0.tar.gz
- Upload date:
- Size: 11.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5c5cf56f938ee3c53665ff049920f0e183846268f9cea415b2f7a5698911d9af
|
|
| MD5 |
e945a435980f4ec6e7594a27a867b5a7
|
|
| BLAKE2b-256 |
fb518e1e1e3bebed5ca14fe9f854d473b71b2cc5b1821a4708de207b0036f9c0
|
File details
Details for the file fs_tree_spec-0.2.0-py3-none-any.whl.
File metadata
- Download URL: fs_tree_spec-0.2.0-py3-none-any.whl
- Upload date:
- Size: 11.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1e34af3178bbf021207768bfb456df6e14d2cbfaeb4478eea5f0aadf1502d63d
|
|
| MD5 |
f96aa4f88825fac7c9d9748833115403
|
|
| BLAKE2b-256 |
ef8b2edb6a17860477e7129591acf92a8ff45d6ed9cdc1c429b66d753c77da38
|