leo-cub is an experimental Rust library and command-line tool for reading,
validating, browsing, and modifying Leo Editor
outlines.
The installed command is cub; the Rust library namespace is leo.
Leo outlines are not ordinary XML trees. A GNX identifies shared vnode content,
while an outline position identifies one occurrence of that vnode. Cloned nodes
can therefore appear in several places. leo-cub keeps those concepts separate
and exposes transactional operations intended for scripts and AI tools.
- Parse and validate
.leoXML outlines. - Preserve XML outside the rewritten
<vnodes>and<tnodes>sections. - Represent clone identity separately from outline positions.
- Apply atomic JSON operation batches with optional text preconditions.
- Parse Leo 5 thin derived-file sentinels.
- Reconstruct
@file,@thin, and@file-thinhierarchies and bodies. - Resolve ancestor
@pathdirectives in the TUI. - Browse outlines with a small Ratatui interface.
- Highlight node bodies with Syntect, using
@languageor source extensions. - Open a derived node's full source file at its sentinel line using
$VISUALor$EDITOR.
The recommended installation method is uv:
uv tool install leo-cubThis installs the cub command. You can also use pip install leo-cub,
or download the appropriate archive
from the latest GitHub release.
From the repository root, install the cub command with Cargo:
cargo install --path .Install the bundled local agent skill after installing the command:
cub install-skillsThis writes ~/.claude/skills/leo-cub/SKILL.md and overwrites an existing
copy, so it is safe to rerun after upgrading.
cub tui outline.leoThe browser resolves external thin files in memory.
| Key | Action |
|---|---|
j, ↓ / k, ↑ |
Select next/previous node |
l, →, Enter |
Expand selected node |
h, ← |
Collapse selected node |
Home / End |
Select the first/last visible node |
PageUp / PageDown |
Scroll the selected node's body by one page |
Ctrl-P |
Find a headline incrementally; use ↑/↓ to cycle matches |
o |
Edit the node body in $VISUAL/$EDITOR; for derived nodes, open the real source at its sentinel |
y |
Toggle syntax highlighting |
? |
Show command help |
| Key | Action |
|---|---|
Ctrl-I or Tab |
Insert a new sibling and enter headline editing |
Ctrl-H or Backspace |
Edit the selected headline |
Ctrl-↑, Ctrl-↓ |
Move among siblings |
Ctrl-←, Ctrl-→ |
Promote or demote the selected node |
Ctrl-S |
Save outline changes |
q or Esc |
Quit; press twice to discard unsaved changes |
| Key | Action |
|---|---|
| Printable characters | Append to the headline |
Backspace |
Delete the previous character |
Enter |
Accept the headline |
Esc |
Cancel editing; a newly inserted node is removed |
Use --no-derived to display only the hierarchy physically present in the
.leo XML file.
For source navigation, cub recognizes common position arguments for Vim,
Neovim, Nano, Emacs, VS Code, Microsoft Edit, Helix, and Kakoune. Other editors
receive the file path without a line argument.
cub inspect outline.leo
cub inspect outline.leo src/main.rs
cub inspect outline.leo --gnx ekr.20260811210000.1
cub inspect outline.leo --position 0/2/1
cub inspect outline.leo --search 'render_(compact|json)'
cub inspect outline.leo --search TODO --search FIXME
cub inspect outline.leo src/main.rs --format json
cub validate outline.leo
cub sync outline.leo
cub sync outline.leo src/main.rs --dry-run
cub sync outline.leo --gnx ekr.20260811210000.1
cub diff before.leo after.leo
cub inspect-derived path/to/derived.py --summary
cub apply outline.leo operations.json --dry-runinspect uses a compact text format containing position paths, GNXs,
headlines, and bodies. Repeated clone content is shown as =GNX. Use
--format json for structured output in scripts.
--search accepts a Rust regular expression and searches headlines and body
lines. Search results include line-numbered excerpts with two surrounding lines
instead of printing entire matching bodies. Repeat --search to match any of
several expressions. Thin external files are scanned first and reconstructed
only when they may contain a search or GNX match.
An operation batch is a JSON object:
{
"operations": [
{
"op": "set-body",
"node": "ekr.20260811210000.1",
"expected": "old body",
"body": "new body"
}
]
}Operations are applied to a copy and committed only if the complete batch is
valid. expected provides optimistic conflict detection for headline and body
edits.
This project is early and the file format support is incomplete. In particular,
it does not yet write thin derived files, dynamically interpret every
@comment/@delims change, or fully reconstruct all doc-part forms. Keep
backups and use --dry-run when testing write operations on important outlines.
The TUI overlays derived files without modifying either the outline or external
source files. Derived descendants are read-only in the outline editor; use o
to edit their full external source. Unsaved outline changes require a second
q before they are discarded.
MIT