Tree-shaped Python CLI framework for building discoverable toolboxes you can hand to agents.
Allow writing discoverable CLI toolboxes with only <tool> -j in the skills that use the tools.
The skill names the binary. It does not copy the command schema. The agent runs -j for the current tree (commands, arguments, types, defaults, choices, nargs) and invokes from that output — never from a pasted copy. After install, upgrade, or a parse error, rediscover. Humans still get the tree (-h / --hv); --stub writes the purpose-only blurb that points at -j.
| Feature | argparse | Click | Typer | Treeparse |
|---|---|---|---|---|
| Tree-structured help | No | Partial | Partial | Yes |
| JSON CLI export | No | No | No | Yes |
| Explicit structural model | No | No | No | Yes |
| Signature validation | Minimal | No | Partial | Yes |
pip install treeparse
from treeparse import cli, command, argument
def greet(name: str):
print(f"Hello {name}")
greet_cmd = command(
name="greet",
callback=greet,
arguments=[argument(name="name", arg_type=str)],
)
app = cli(name="demo", help="Demo CLI", commands=[greet_cmd])
if __name__ == "__main__":
app.run()$ python app.py greet Alice
Hello Alice
$ python app.py --help
Usage: demo ... (--json, -j, --help, -h, --hv, --stub)
Description: Demo CLI
demo Demo CLI
└── greet <NAME, str>
$ python app.py --json
{
"name": "demo",
"type": "cli",
"commands": [
{
"name": "greet",
"type": "command",
"arguments": [
{"name": "name", "arg_type": "str", ...}
]
}
]
}
1. Build tool 1
# ink.py
ink = cli(name="ink", help="Annotate figures with Inkscape.")
ink.commands.append(command(
name="new", help="Open a new blank SVG.", callback=new,
arguments=[argument(name="name", arg_type=str)],
options=[option(flags=["--notes-dir", "-d"], arg_type=str, default="notes/draw", help="Directory to save SVGs")],
))2. Build tool 2
# mind.py
mind = cli(name="mind", help="Build mind maps in Minder.")
mind.commands.append(command(
name="create", help="Create a new mind map.", callback=create,
arguments=[argument(name="title", arg_type=str)],
))3. Plug into a toolbox
# toolbox.py
from treeparse import cli
from ink import ink
from mind import mind
toolbox = cli(name="toolbox", help="Creative toolbox.", subgroups=[ink, mind])
if __name__ == "__main__":
toolbox.run()4. Teach the LLM
The skill that uses the toolbox needs only <tool> -j — not a copy of the schema. --stub writes that recipe:
toolbox --stub > skill.md
toolbox — Creative toolbox.
This is a CLI toolbox. Discover its commands and
full schema on demand:
toolbox -h # command tree
toolbox -j # machine-readable JSON schema
The agent runs -j when it needs the schema, then invokes from that output. A wrong invoke reprints the rediscovery commands (toolbox -j, and toolbox <path> -h for the subtree) so the agent rediscovers instead of retrying from memory.
Human-readable tree
toolbox --help
Usage: toolbox ... (--json, -j, --help, -h, --hv, --stub)
Description: Creative toolbox.
toolbox Creative toolbox.
├── ink Annotate figures with Inkscape.
│ └── new <NAME, str> Open a new blank SVG.
│ └── --notes-dir, -d: str Directory to save SVGs (default: notes/draw)
└── mind Build mind maps in Minder.
└── create <TITLE, str> Create a new mind map.
Two discovery channels: tree for humans, JSON for machines.
| Flag | Audience | Output |
|---|---|---|
--help, -h |
Human | Rich tree, branch-pruned per subcommand |
--hv |
Human | Verbose rich tree (callback docstrings) |
--json, -j |
Machine | Full CLI structure as plain JSON (no ANSI, no rich wrapping) |
--stub |
Agent skill file | Purpose-only blurb — points to -h/-j for the schema |
--version, -V |
Either | Auto-detected from package metadata, or set with version= on cli |
The examples/ directory contains 22 executable demonstrations covering every Treeparse feature (themes, group-level arguments/options, chaining, flat sub-cli toolbox composition, nargs="*"| "+", boolean flags, validation errors, root options, JSON export, custom sort/fold, etc.). They are living documentation and the primary reference for users and LLMs.
They are not installed as part of the package. After pip install treeparse only the core library and the treeparse console script are available.
# Clone and set up (once)
git clone https://github.com/wr1/treeparse.git
cd treeparse
uv sync --dev
uv run pre-commit install # ruff check --fix + ruff format on every commit
# Run any example with an editable install (no need to touch PYTHONPATH)
uv run --with-editable . python examples/demo.py --help
uv run --with-editable . python examples/all_themes_demo.py --help
python examples/validation_error_demo.py --help # after the uv command above
# Manual lint/format (same as CI and pre-commit)
uv run ruff check --fix .
uv run ruff format .The test suite (tests/test_examples.py and test_demo_execution.py) loads the examples via importlib.util.spec_from_file_location and will continue to pass without any changes to packaging.
from treeparse import cli, command, group, argument, option
from treeparse.models.chain import chain| Model | Purpose |
|---|---|
cli |
Root — reusable as a subgroup in another cli; a flat cli (callback, no commands) acts as a single command when nested |
group |
Namespace with optional fold=True to collapse in help, or default="cmd" to route unknown tokens to a child command |
command |
Executable action with a callback |
chain |
Runs multiple commands in sequence |
argument |
Positional — <ARG> required, [ARG] optional (nargs="?"/"*") |
option |
Named flag, with optional inheritance to child commands |
- Folding:
group(fold=True)collapses togroup [...]— drill in withtoolbox ink --help - Default subcommand:
group(default="open")routes a bare group, an option flag, or an unknown token to that child command (toolbox ink foo→toolbox ink open foo); explicitly-named subcommands always win - Inheritance:
option(inherit=True)propagates to all child commands - Validation: callback param names and types checked against CLI definition at startup
- YAML config:
cli(yml_config=Path("config.yml"))overrides defaults at runtime - Themes:
theme="github"/"monokai"/"mononeon"/"monochrome" - Testing:
cli_runnerfor pytest integration
Use Treeparse if you need:
- Structured CLI composition
- Discoverable CLI toolboxes — skills that use them need only
<tool> -j - Machine-readable CLI definitions (orchestration, docs pipelines)
- Complex nested command hierarchies
Avoid if you only need a simple single-script CLI.
Hosted docs: https://wr1.github.io/treeparse/
To work on the docs site locally, see docs/README.md.
MIT
