vissue-cli 0.6.0

vissue command line: plain-text issue tracking over per-project orgmode files
vissue-cli-0.6.0 is not a library.

vissue

CI crates.io docs.rs MSRV license docs

A plan is a directed acyclic graph of org headings. One file per project stores the nodes. :PARENT: groups work under a plan. :BLOCKED_BY: is the partial order. ready is the frontier any agent can pick up. claim is the lock so two agents do not take the same node.

An issue is a top-level org heading. The file is the database: no SQLite, no second store. A command parses the files it needs. An optional vissue serve caches a parse and pushes change notifications; crashing it loses nothing, and every verb still works with it down. Every mutation rewrites one file under a lock, so the tracker diffs, merges, and greps like the rest of the repository it lives in. A CLI and a Model Context Protocol server share the same library.

The store is an Org file [1]. The graph has several justifications, and they are not interchangeable. :PARENT: plus :TYPE: and tags store the output of hierarchical task-network decomposition [3], [4]: the agent is the planner, the file is the network. :BLOCKED_BY: is a least-commitment partial order [2]. ready and claim are a work-stealing ready deque [9] over that order, which is also how a rebuild DAG exposes dirty sources [8]. related is a named neighborhood over declared edges, the same discipline as a citation graph [12], [14], [17] and the opposite of an extracted memory graph [24], [25], [26]. OokCite is how those papers were found and checked, not a second reading list to copy.

<root>/Software/<project>/issues.org

Software is the default prefix and is configurable. <root> comes from --root, the VISSUE_ROOT environment variable, or the current directory.

A user-level ~/.config/vissue/config.toml can send named projects to a different checkout. Those routes win over --root. --no-route keeps the process on a single layout. See the reference.

A plan on the board

A feature becomes one parent issue and five tagged children. This is the board an orchestrator shows after that split. Only the catalog is workable. Everything else waits on an explicit blocker, so two agents cannot start the terminal UI and the example file at the same time.

Id State What
keys-e0pl TODO Epic: Colemak leader sequence
keys-cata TODO, ready Catalog of bindable actions
keys-toml BLOCKED on catalog keys.toml schema and key names
keys-tuih BLOCKED on overlay Terminal UI set_keymap and overlay on_key
keys-lead BLOCKED on wire Leader plus examples/keys/colemak.toml
flowchart LR
    epic["keys-e0pl epic"]
    catalog["keys-cata catalog"]
    schema["keys-toml keys.toml"]
    overlay["keys-ovly overlay"]
    wire["keys-wire wire"]
    tui["keys-tuih terminal UI"]
    example["keys-lead colemak example"]
    epic --> catalog
    epic --> schema
    epic --> overlay
    epic --> wire
    epic --> tui
    epic --> example
    catalog -->|"blocks"| schema
    schema -->|"blocks"| overlay
    overlay -->|"blocks"| tui
    overlay -->|"blocks"| wire
    wire -->|"blocks"| example

Solid parent edges are containment. The blocks edges are :BLOCKED_BY: and are what ready reads. A topological sort is the order the graph would fire in if every node ran; ready is the cheaper question of which sources are still open. Adding a blocker that would close a cycle is rejected.

Build that board from an empty directory:

$ mkdir -p /tmp/keys && cd /tmp/keys
$ vissue create --project keys --type plan "Epic: Colemak leader sequence"
keys-e0pl  TODO  [#C]  Epic: Colemak leader sequence
$ vissue create --project keys --type task --parent keys-e0pl --tags catalog \
    "Catalog of bindable actions"
keys-cata  TODO  [#C]  Catalog of bindable actions
$ vissue create --project keys --type task --parent keys-e0pl --tags config \
    "keys.toml schema and key names"
keys-toml  TODO  [#C]  keys.toml schema and key names
$ vissue update keys-toml --block keys-cata
keys-toml: state TODO -> BLOCKED (auto on block), blocked_by += keys-cata

Repeat for the overlay, the terminal UI, the wire, and the example file, each --parent keys-e0pl and each --block on the node it cannot start without. Then:

$ vissue ready --project keys
keys-cata              TODO      [#C]  Catalog of bindable actions
$ vissue tree keys-e0pl
keys-e0pl TODO      [#C]  Epic: Colemak leader sequence
  keys-cata TODO      [#C]  Catalog of bindable actions
  keys-toml BLOCKED   [#C]  keys.toml schema and key names
    * blocked-by keys-cata

Two workers share that frontier by claiming, not by editing a checklist. A common pairing is one implementer and one reviewer per node: the reviewer is a child issue blocked on the implementation issue, so it becomes ready only when the implementer closes. An identity is an opaque string, so it can name a person, a machine, or a script; set VISSUE_AGENT to something stable enough to recognise later.

$ VISSUE_AGENT=impl vissue claim keys-cata
claimed keys-cata by impl (TODO -> STARTED)
$ vissue create --project keys --type task --parent keys-cata --tags review \
    "Review the bindable-action catalog"
$ vissue update keys-revw --block keys-cata
$ VISSUE_AGENT=review vissue ready --project keys
# empty: the only open work is claimed or blocked
$ vissue claims --project keys
keys-cata              STARTED   [#C]    0d  impl  Catalog of bindable actions (keys)

A claim says who is working. It does not say what to work from, nor what came of it. show --org writes the heading out whole, dispatch note included, and that file goes to the worker. append records the result back under the heading, dated and attributed, where the next reader looks. note keeps its job: one line in the logbook, the audit trail of what happened to the issue.

$ vissue show --org keys-cata > ISSUE.org
$ vissue append keys-cata --file SUMMARY.md
keys-cata: appended 12 line(s)

Markdown is safe in both directions, and --file - reads stdin.

The tracker does not invent the children. Whoever plans the work splits it [2], [3], [4]; vissue stores the resulting directed graph, names the ready set [8], [9], and refuses a cyclic edit [5], [6].

related asks what else in the corpus is a neighbor, and why. Explicit :PARENT:, :BLOCKED_BY:, :DISCOVERED_FROM:, and Org body links outrank shared tags and rare terms [22]. The command prints the evidence (blocked_by, org_link, term:keymap) and writes nothing back [24], [25], [26].

Terminal board and HUD

vissue tui is a ratatui board over ready, list, claims, agenda, and search. It paints from the files first. Unless --offline, it then attaches to vissue serve, starting serve when the socket is free. A socket bound to another root stays on the files so a claim cannot hit the wrong vault. q quits; ? lists the keys.

vissue hud opens on the project list. Opening a project shows that project's ready forest, then List / Claims / Agenda / Search inside it. The window is an icedtea overlay: undecorated and always-on-top. On Sway it floats itself over the IPC socket (SWAYSOCK); there is no compositor include to install. Chrome is icedtea: chips, cards, markdown, and the type scale. The selected row has show / excerpt / tree / related / notes. n opens the logbook and writes a note. Keys come from a catalog; ~/.config/vissue/keys.toml (or VISSUE_KEYS) remaps them. --rofi is the seat dmenu picker.

$ vissue tui
$ vissue tui --offline
$ vissue hud
$ vissue hud --rofi
$ vissue hud --rofi --mode new

Install

$ cargo install vissue-cli
$ cargo install vissue-hud   # summonable overlay, optional
$ cargo install vissue-mcp   # the MCP server, same version

The workspace publishes seven crates at one version: vissue-cli, vissue-mcp, vissue-hud, and the libraries vissue-core, vissue-control, vissue-serve, vissue-tui. Tagged releases carry prebuilt archives (glibc and musl Linux for the CLI and MCP; the HUD is glibc only) and a shell installer; see the releases page. To take unreleased main, name the repository:

$ cargo install --git https://github.com/HaoZeke/vissue vissue-cli

Completions and a manual page come out of the binary, so they cannot drift from it:

$ vissue completions zsh > ~/.zfunc/_vissue
$ vissue man > ~/.local/share/man/man1/vissue.1

Documentation

Full documentation is at vissue.rgoswami.me.

Page What it answers
Getting started An empty directory to a backlog two workers share
How-to One task at a time: filter, share, watch, fold, validate
Reference Commands, properties, config, export schema, exit statuses
Control Unix socket protocol, framing, and serve / tui / hud
Explanation Why the file is the database, and what the citations justify
Emacs The agenda, tag search, and id: links, with nothing installed
Org syntax Org 9.8 mapped onto an issues.org
Org ecosystem ELPA / MELPA names the tracker owns, reads, or preserves

The sources are Org under docs/orgmode/; bash docs/build.sh renders the site.

A minute of it

$ vissue create --project parser "Reject a manifest with no header"
parser-k29f  TODO  [#C]  Reject a manifest with no header

$ vissue create --project parser --priority A "Publish the release notes"
parser-3xq7  TODO  [#A]  Publish the release notes

$ vissue update parser-3xq7 --block parser-k29f
parser-3xq7: state TODO -> BLOCKED (auto on block), blocked_by += parser-k29f

$ vissue ready
parser-k29f            TODO      [#C]  Reject a manifest with no header

ready is the open frontier, claim is the lock that stops two agents taking the same node, and the file underneath is ordinary Org that Emacs reads without help.

Emacs

A tracker is an ordinary Org file, so Emacs reads it with nothing installed: deadlines and scheduled dates sit on the planning line, tags in the heading's own tag run, #+CATEGORY: names the project, and :ID: resolves through org-id. org-lint has nothing to say about a file vissue wrote.

flowchart LR
    corpus["Software/&lt;project&gt;/issues.org"]
    vissue["vissue: CLI and MCP"]
    emacs["Emacs: agenda, tag search, id links"]
    corpus <--> vissue
    corpus <--> emacs

The traffic goes both ways: C-c C-d, C-c C-s, C-c C-q, and marking an issue DONE under org-log-done all work on an issue heading, and vissue reads back what they write. tests/org_interop.sh drives a real Emacs in CI to keep that true. See Emacs for the details, including what any other tool writing the same file has to respect.

Contributing

Conventional commits, cargo fmt, and cargo clippy -- -D warnings. Run prek install for the hooks. The minimum supported Rust version is 1.89. See CONTRIBUTING.md for the interop checks a change to the command surface or the on-disk shape has to pass.

Citation

See CITATION.cff.

License

MIT. See LICENSE.