notebook

Read and edit a notebook canvas from the command line via the `notebook` CLI (crates/notebook_cli). Use when the user wants to view the canvas, add/move/edit/recolor/delete nodes, connect or disconnec

SKILL.md

GitHub

Notebook canvas CLI

notebook is a CLI over a running notedeck's embedded relay. It keeps its own nostrdb cache, reconciles with the relay each run (NIP-77 negentropy, falling back to NIP-01, or fully offline against the cache), folds the canvas locally with the same reducer the egui app uses, and forwards edits back so the running app sees them. Source: crates/notebook_cli; the cache/sync/relay/key plumbing is shared with headway via crates/relay_sync.

Running it

Prefer a built binary; fall back to cargo:

# build once, then call the binary directly (fast, no rebuild per command)
cargo build -p notebook_cli           # produces target/debug/notebook
target/debug/notebook <command>

# or, one-off:
cargo run -q -p notebook_cli -- <command>

In examples below, notebook means whichever form you're using.

Logging in

The CLI reads and writes your own canvas once you're logged in. The author whose canvas is loaded comes from --author <pk>, else the signing key's own pubkey — so to read or edit without --author you need a key configured. Relay defaults to ws://127.0.0.1:6677 (notedeck's embedded relay); override with --relay <url> or $NOTEBOOK_RELAY. If no relay answers, the CLI works offline against its cache and edits reach the app on the next connected run.

If a command fails because no key is configured (need --nsec to sign, or --author to read a canvas), ask the user to run notebook login. Don't handle the key yourself.

The golden rule: show before you edit

Each node has a friendly reference — the notebook: scheme then three BIP-39 words encoding the leading bits of its event id, e.g. notebook:maple-river-canyon. It's a sayable rendering of the node's real identity (its 64-char nostr event id), the notebook sibling of headway's headway:<board>/… cards — single-segment (no canvas), because a node's identity is its event id, not the canvas it happens to sit on. show prints it muted at the end of each node line; quote it in chat or commits, and pass it back to any command that takes a <node>.

A <node> argument resolves, in order, as:

  • the full 64-char hex id, or any unique hex prefix — canonical, best for scripted edits where you've pulled the id from show --json
  • a notebook:<word-id> reference, e.g. notebook:maple-river-canyon. The notebook: scheme is required — a bare maple-river-canyon is not a reference and won't resolve.

Edges are addressed by their edge id (<from-hex>-<to-hex>, shown muted by show) or a unique prefix. Always run show first to read the current ids, then act on what you actually see — never assume an id or that a node is where you expect.

notebook show              # human-readable: title, nodes, edges, pending
notebook show --json       # machine-readable, for parsing (each node has `id` + `ref`)
notebook show <node>...    # print only the given nodes (hex id, notebook:<word-id>, or prefix)
notebook show <node> --json

show prints the canvas title (with [open] if open), then each node as <first line of text> (x,y w×h) notebook:<word-id> with the geometry and reference muted, then edges as <from-ref> → <to-ref> <edge-id>, then any pending nodes (proposals on a closed canvas). If there's no canvas yet it tells you to run notebook seed; with --json it prints null.

If a selector doesn't resolve, errors are explicit: an ambiguous prefix says ambiguous node prefix / ambiguous edge prefix, an unknown one no node matching / no edge matching — re-read show and retry with a longer prefix, the notebook:<word-id> reference, or the full id.

Which form to use: refer to a node by its notebook:<word-id> reference when talking to a human (it's sayable, they'll recognise it, and it renders as a live inline chip in notes/chat). When you script an edit, the hex id from show --json is canonical and can never be ambiguous; the reference resolves too and is fine for one-offs.

Commands

Command What it does
show [nodes...] [--json] Print the canvas, or only the given nodes
seed [title...] Create the canvas if none exists (default title Notebook)
add <text...> [-x -y -w --height] Add a text node, optionally placed/sized
move <node> [-x -y -w --height] Move/resize a node (unset fields keep current geometry)
edit <node> <text...> Replace a node's text
color <node> [color] Recolor a node (none/-/empty clears; --color also works)
restack <node> <index> Restack a node to a display index (0 = bottom)
delete <node> Remove a node (reversible tombstone)
connect <from> <to> [--from-side <s>] [--to-side <s>] Draw an edge between two nodes
disconnect <edge> Remove an edge (id from show)
rename <title...> Rename the canvas
login <nsec> Store a signing key so later runs just work
logout Forget the stored signing key

show, seed, login, and logout aside, every command edits the canvas and needs a signing key. Re-drawing the same connect <from> <to> updates that edge rather than stacking a duplicate (edge ids are stable per ordered pair, latest-wins).

Geometry flags

add and move take geometry in canvas pixels: -x/--x, -y/--y for position and -w/--w, --height for size (height has no -h short — that's --help). add fills any unset field from defaults (a new text node is 250×120 at the origin); move fills unset fields from the node's current geometry, so move <node> -x 400 slides it horizontally without touching its size or y.

notebook add "ship the thing" -x 100 -y 200    # new node at (100,200)
notebook move 1a2b3c4d -x 400 -y 50            # reposition, keep size

Other flags: --author <pk> (read someone else's canvas), --canvas <id> (non-default canvas, default notebook), --db <path> (cache dir), --nsec <nsec> (key for this run; $NOTEBOOK_NSEC takes precedence over the stored key), -h/--help.

Typical workflow

Add a node, then connect it to an existing one:

notebook show                                          # read the canvas + word-ids
notebook add "new idea" -x 300 -y 100                  # create the node
notebook show                                          # read back both word-ids
notebook connect maple-river-canyon glide-amber-mesa   # draw an edge between them
notebook show                                          # verify the edge landed

For scripted edits where ambiguity must be impossible, pull the hex id from notebook show --json and pass that instead of the word-id.

Notes

  • Edits print ok (N events); offline edits append — offline, not forwarded to the app, meaning they're cached but haven't reached the running notedeck yet. seed prints seeded canvas '<id>' (N events).
  • seed errors if a canvas already exists; that's expected — just show instead. An edit that resolves to no change prints action produced no events (unknown node or edge?).
  • The cache lives at <data-dir>/notebook-cli unless --db overrides it. The CLI and the running app converge through the relay, so either side's edits show up on the other after a reconcile.</content> </invoke>