Skip to main content

leo-cub

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.

Screenshot

image

Why

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.

Current features

  • Parse and validate .leo XML 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-thin hierarchies and bodies.
  • Resolve ancestor @path directives in the TUI.
  • Browse outlines with a small Ratatui interface.
  • Highlight node bodies with Syntect, using @language, @rst ancestors, or source extensions, including bundled reStructuredText syntax support.
  • Open a derived node's full source file at its sentinel line using $VISUAL or $EDITOR.

Install

The recommended installation method is uv:

uv tool install leo-cub

This installs the cub command. You can also use pip install leo-cub, or download the appropriate archive from the latest GitHub release.

Termux

The PyPI release includes an Android API 24 ARM64 wheel suitable for current 64-bit Termux installations:

uv tool install leo-cub

Installation from source

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-skills

This writes ~/.claude/skills/leo-cub/SKILL.md and overwrites an existing copy, so it is safe to rerun after upgrading.

TUI

cub tui outline.leo

The browser resolves external thin files in memory. Outline headlines highlight Leo directives, external-file names, and section-reference markers. A red * marks each node changed since the outline was loaded or last saved; saving or reloading clears the markers.

TUI keybindings

Browsing and display

Key Action
/ Select next/previous node
Shift-↓ / Shift-↑ Extend or shrink a contiguous multi-node selection
, Enter Expand selected node
Collapse selected node
Home / End Select the first/last visible node
PageUp / PageDown Scroll the selected node's body by one page
f Toggle a full-width body pane
Shift-F Toggle a full-width outline pane
/ in full-width mode Scroll the body vertically by one line
/ in full-width mode Scroll the body horizontally
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

Outline editing

Key Action
Ctrl-I or Tab Insert a new sibling and enter headline editing
Ctrl-H or Backspace Edit the selected headline
c Copy the selected tree
x Cut the selected tree
v Paste an independent copy with fresh node identities
Shift-V Paste as clones, retaining node identities
Ctrl-↑, Ctrl-↓ Move the selected node or multi-selection among siblings
Ctrl-←, Ctrl-→ Promote or demote the selected node or multi-selection
Ctrl-R Reload from disk; press twice to discard unsaved changes
Ctrl-S Save outline changes
q or Esc Quit; press twice to discard unsaved changes

Headline editing

Key Action
Printable characters Replace the initial selection, or insert at the cursor
/ , Home / End Keep the headline and position the cursor
Backspace / Delete Delete the selection or a 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.

Demo flow

Starting in a project containing README.md and a src/ directory, create a new outline with destinations for source code, documentation, and tasks:

cub new project.leo --headline "Project"
cub add project.leo \
  "Project/Source" \
  "Project/Documentation" \
  "Project/Tasks/Backlog"

Import the source tree below Project/Source, preserving its directory structure, then import the README as an editable node below the documentation branch:

cub import project.leo src \
  --recursive --mode auto --paths \
  --parent "Project/Source"
cub import project.leo README.md \
  --mode edit \
  --parent "Project/Documentation"

Finally, inspect the resulting tree and validate the file:

cub inspect project.leo
cub validate project.leo

@auto source nodes are reconstructed from their files when inspected or opened, while the @edit README node stores its text in the outline.

Headless commands

cub new outline.leo
cub new notes.leo --headline "Notes"
cub add outline.leo "Project/Tasks/First task" "Project/Notes"
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 import outline.leo src --recursive --mode auto --paths
cub import outline.leo README.md --mode edit --no-paths
cub import outline.leo README.md --parent "Project/Notes"
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-run

new creates a valid outline with one empty root node. It refuses to overwrite an existing file.

add creates nodes from slash-separated headline paths and reuses shared or existing prefixes. import --parent accepts either an exact GNX or a unique slash-separated headline path. Paths with duplicate matching siblings are rejected as ambiguous.

import creates Leo external-file nodes in auto, edit, or clean mode. Markdown, Python, Rust, C#, Go, JavaScript/JSX, and TypeScript/TSX @auto files are expanded transiently with Tree-sitter when they are loaded by inspect or the TUI; the generated tree is not stored in the .leo file. Unsupported source types remain available as a plain root node. Markdown also supports Leo's @auto-md and @auto-markdown headlines and leo-noheader markers. Directory imports are recursive only with --recursive and preserve their layout with @path nodes by default. Use --no-paths to put all imported files directly below the destination, --parent GNX_OR_PATH to choose that destination, and --dry-run to validate without saving.

inspect 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.

Status and safety

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.

License

MIT

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

leo_cub-0.3.0-py3-none-win_amd64.whl (3.3 MB view details)

Uploaded Python 3Windows x86-64

leo_cub-0.3.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (3.7 MB view details)

Uploaded Python 3manylinux: glibc 2.17+ x86-64

leo_cub-0.3.0-py3-none-macosx_11_0_arm64.whl (3.6 MB view details)

Uploaded Python 3macOS 11.0+ ARM64

leo_cub-0.3.0-py3-none-macosx_10_12_x86_64.whl (3.6 MB view details)

Uploaded Python 3macOS 10.12+ x86-64

leo_cub-0.3.0-py3-none-android_24_arm64_v8a.whl (3.7 MB view details)

Uploaded Android API level 24+ ARM64 v8aPython 3

File details

Details for the file leo_cub-0.3.0-py3-none-win_amd64.whl.

File metadata

  • Download URL: leo_cub-0.3.0-py3-none-win_amd64.whl
  • Upload date:
  • Size: 3.3 MB
  • Tags: Python 3, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for leo_cub-0.3.0-py3-none-win_amd64.whl
Algorithm Hash digest
SHA256 cbea23207cd02d057e3c4c9d76bfa33e8565817c84fd06cc39c369a7673362d6
MD5 7f85f310201a4b6649c26657517db66b
BLAKE2b-256 cc20b0f6afcd3c38b570fe34046e23f4652c5a50fa1679e46780df841f459868

See more details on using hashes here.

Provenance

The following attestation bundles were made for leo_cub-0.3.0-py3-none-win_amd64.whl:

Publisher: release.yml on vivainio/leo-cub

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file leo_cub-0.3.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for leo_cub-0.3.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 722841cec6b4bf03c5052069821244e4ff804abd48ae300247f01b521b82e1ec
MD5 976fa4fda38aadfc529f3974327f62d6
BLAKE2b-256 5861bb22b5942cc77d3f649c076234012294516cba2adea7312674e725275471

See more details on using hashes here.

Provenance

The following attestation bundles were made for leo_cub-0.3.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: release.yml on vivainio/leo-cub

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file leo_cub-0.3.0-py3-none-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for leo_cub-0.3.0-py3-none-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 744f907456cec51e1c508e65bdb0717500b5de138805b45fbdec9b652c20b34b
MD5 6cac4b60202b6c82c456d1395f643efb
BLAKE2b-256 3e3ff68db55dcf0ddd3b8bb79baeb2ed01a8d4a83f346422265b6b2686820a48

See more details on using hashes here.

Provenance

The following attestation bundles were made for leo_cub-0.3.0-py3-none-macosx_11_0_arm64.whl:

Publisher: release.yml on vivainio/leo-cub

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file leo_cub-0.3.0-py3-none-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for leo_cub-0.3.0-py3-none-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 fd9000091add5a36071f31a634ef2f0bf888e4d93c7ac5de8ebbe86ad106bc2d
MD5 c96a64ae791539d1402ab1caada9d7de
BLAKE2b-256 2ebc9dc025f657d90380f07108324c54668809b1e1f553f59f386d4b650e0985

See more details on using hashes here.

Provenance

The following attestation bundles were made for leo_cub-0.3.0-py3-none-macosx_10_12_x86_64.whl:

Publisher: release.yml on vivainio/leo-cub

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file leo_cub-0.3.0-py3-none-android_24_arm64_v8a.whl.

File metadata

File hashes

Hashes for leo_cub-0.3.0-py3-none-android_24_arm64_v8a.whl
Algorithm Hash digest
SHA256 00a7257d1b4db88d87d7452d6fde0e3179472566ecb651b6ff22feb29a0fe50b
MD5 a7d0765857e171912120b099e77bd5fe
BLAKE2b-256 4ab953bfb05d05ced3f812a311f2e37ebf64253b414673ea77f4e58ca6f4316c

See more details on using hashes here.

Provenance

The following attestation bundles were made for leo_cub-0.3.0-py3-none-android_24_arm64_v8a.whl:

Publisher: release.yml on vivainio/leo-cub

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

0.3.0 This release

5 files

0.2.2

5 files

0.2.1

4 files

0.1.1

4 files

0.1.0

4 files

0.0.2

4 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page