Metadata-Version: 2.4
Name: leo-cub
Version: 0.1.1
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Apache Software License
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Rust
Classifier: Topic :: Text Editors
Classifier: Topic :: Utilities
License-File: LICENSE
Summary: Automation-safe library and CLI for Leo outline files
Keywords: leo-editor,outline,cli,tui,automation
Author-email: Ville Vainio <vivainio@gmail.com>
License: MIT OR Apache-2.0
Requires-Python: >=3.10
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
Project-URL: Homepage, https://github.com/vivainio/leo-cub
Project-URL: Repository, https://github.com/vivainio/leo-cub

# leo-cub

`leo-cub` is an experimental Rust library and command-line tool for reading,
validating, browsing, and modifying [Leo Editor](https://leo-editor.github.io/leo-editor/)
outlines.

The installed command is `cub`; the Rust library namespace is `leo`.

## Screenshot

<img width="1836" height="933" alt="image" src="https://github.com/user-attachments/assets/2ff24163-62a2-487b-aaf5-a9ec8c2beb73" />

## 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` or source extensions.
- Open a derived node's full source file at its sentinel line using `$VISUAL` or
  `$EDITOR`.

## Install

The recommended installation method is [`uv`](https://docs.astral.sh/uv/):

```sh
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](https://github.com/vivainio/leo-cub/releases/latest).

### Installation from source

From the repository root, install the `cub` command with Cargo:

```sh
cargo install --path .
```

Install the bundled local agent skill after installing the command:

```sh
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

```sh
cub tui outline.leo
```

The browser resolves external thin files in memory.

## TUI keybindings

### Browsing and display

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

### Outline editing

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

### Headline editing

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

## Headless commands

```sh
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-run
```

`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:

```json
{
  "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

