summonr

Panes, mail, and theme

Live coordination between parallel sessions and different models with @ messaging, plus panes, themes, and Herdr.

Interface

summonr --gui opens the session in a desktop window (a build with --features gui). The window is built on Sigil. The left rail starts with one row of buttons that switch the Files, Changes, Worktrees, Jobs, Peers, and Ledger panes, then lists this workspace's sessions: + starts a new session, the branch button starts one in its own git worktree (merge or discard it from the Worktrees pane), and a right-click on a session offers Rename and, on a past session, Delete. A live session is named by /name, otherwise by the first line of its first prompt. The rail's footer names the provider and opens the provider list; with none set up it reads Add a provider. Below 900 px the rail shows icons only. The main column holds the transcript and the composer card. The composer shows the @ mentions in the draft as removable chips, and has an Attach button, the model, effort, and approval chips (pick a model, open the effort strip, or toggle approval), and Send, which waits for text or an image. With no provider set up, sending opens the provider list and keeps the draft. The title bar shows Changes +N −M while the workspace has changes, and ··· opens a menu with Hotkeys, Help, and Settings. The right side shows the open pane with real widgets: clickable file rows, +/− chips that open a file's hunks, an Edit/Save file view. Clicking outside a dialog closes it. The same App and layouts drive the terminal and the window. The window's keys differ: tab and shift+tab move focus instead of changing effort and approval, cmd+k opens the command list, cmd+p starts an @ file mention, cmd+, opens Settings, cmd+n starts a new session, and cmd+1 to cmd+6 open the Files, Changes, Worktrees, Jobs, Peers, and Ledger panes. Letters typed while a button has focus go to the composer, and a pane takes the keyboard only after a click inside it. In the window the command list shows one line of help for each built-in command.

The screen is a set of panes on one session. ctrl+x e (or /pane files) opens the repository tree beside the chat; ctrl+t the plan ledger; ctrl+x b hides or restores the pane. In the tree: ctrl+→ focuses it, ↑↓ move, ←→ collapse or expand, PgUp/PgDn/Home/End jump, Enter opens a directory or attaches @path to your prompt, and a right-click (or the mouse) opens a menu: attach, open in $EDITOR, insert path. ctrl+← or esc returns to the composer. With the composer focused, an empty draft, no open overlay, and no running turn, esc closes the open side pane. /pane off closes the pane. /pane focus, run, and review are presets, /pane "chat | files 30%" sets the split, and ctrl+shift+←/→ resizes it. The layout persists in ~/.summonr/config.toml as layout. ctrl+o opens the settings modal, where every hotkey is editable; the choice is stored under [hotkeys] in the same file. ctrl+x n folds every thinking block, then every tool output (tools = "hidden"), then shows them all again; a click still opens one block.

ctrl+x d (or /pane diff, /diff) shows the changes against the base: the worktree base when you passed --worktree, HEAD otherwise. Each row is +added −removed path; Enter or → opens that file's hunks, one card per @@ block, with a pending, accepted, or rejected badge. Inside a file ↑↓ picks a hunk, a toggles accepted (Enter does not), r reverts the hunk (git apply --reverse of just that block), n opens a note field under the hunk header (Enter saves, esc cancels; a saved note shows there) that rides to the model with your next prompt, and ← or esc returns to the list. While the pane has focus, typed letters stay in the pane, and esc from the list returns to the composer. The right-click menu offers the same actions. Verdicts and notes persist in the repository's git directory, .git/summonr/review.json, or in .summonr/review.json outside a git repository. /diff PATH jumps to one file. After a turn that changed the tree, the transcript gets one changes: N files · +a −r · vs BASE · /pane diff line. The same data is summonr diff [PATH] (the patch), summonr diff --stat, and summonr diff --json. summonr diff never lists generated caches (__pycache__/, *.pyc, .pytest_cache/, .mypy_cache/, .ruff_cache/) or summonr state under .summonr/ other than check.toml and policy.toml, even without a .gitignore.

→ on a file in the tree (or /view PATH, or the menu's view file) reads it in the view pane with line numbers; lines the diff added are green with a +. ↑↓, PgUp/PgDn, Home/End scroll, o opens it in $EDITOR, esc returns to the tree. e edits the file in place: the keyboard goes to the file (type, Enter, arrows, Home/End), the caret is the inverted cell, ctrl+s saves, and esc leaves. With unsaved text a second esc discards it. The tree, the diff, and the view follow the file system: a change on disk relists, re-diffs, or reloads the pane within about half a second.

In the diff pane t keeps only the files the last turn edited (the header says · last turn), c copies the patch on show to the clipboard (OSC 52 in the terminal; the system clipboard in the window), and a pane diff_split_min_cols columns or wider (100 by default) shows a file's hunks side by side, old on the left; a narrower pane shows the unified diff. The window's Changes pane also has Accept, Revert, and Note buttons on each hunk, a Revert file button and review counts on each file, and Undo after a revert; an undone hunk is not reported to the model. Revert file and the Worktrees pane's Discard ask for a second click before they act; the Worktrees pane also has a Merge button. Its This session button lists just the files this session's turns changed, from the checkpoint taken before its first turn, so changes that were already in the tree stay out. summonr diff --turn N shows what turn N changed: from the checkpoint taken at its start to the next turn's checkpoint, or to the workspace for the newest turn. --turn last is the newest turn that changed files, in the newest session for this workspace, and --turn session spans every turn of that session; its summary reads all turns of session <short id>. Without --stat or --json, it writes the same summary line as --stat to stderr before the patch. A resumed session keeps numbering its turns. The summary reads turn N of session <short id>; the JSON has base: "turn N" and session with the session id. A diff of the whole workspace still reads vs HEAD. A file that summonr's own output is redirected to stays out of every later checkpoint in that workspace, in every session, so --turn never lists it. A change to .summonr/check.toml or .summonr/policy.toml is never a touched path.

While a turn runs, a row above the composer shows a spinner, what the model is doing (thinking, or the tool it is running), the round, the seconds so far, the output rate, and the queue count. A message sent during the turn goes into the running turn, and the model reads it after its current tool calls. A message waits in the queue for the next turn instead while an approval or a question is open, while earlier messages or commands still wait, or when it starts with ! or /; enter on an empty composer sends the waiting plain messages into the running turn.

The output rate is tokens per second. While text streams it is an estimate: characters received in the last three seconds, divided by the characters per token measured on the previous round (4 until then), over that span. When the provider reports the round's usage, the rate becomes exact: output tokens over the time between the first and last streamed chunk, or over the whole round when nothing streamed, as in a round that only called tools.

When a turn ends while the terminal is not focused, summonr rings the bell and sends a desktop notification (OSC 9) naming the workspace. notify in config.toml, or the Notify row in /settings, sets off, turn (the default), or all, which also notifies when an approval is waiting.

The footer shows the workspace and branch on the left and, on the right, the session's spend when the model has a price or the provider reports a cost, then the context and cache meters.

When a command asks for a password through SSH_ASKPASS or SUDO_ASKPASS, such as an ssh key passphrase, a Credential required dialog opens. Type the password and press enter to send it, or press esc to cancel. Closing the dialog also cancels the request. The password goes to the waiting command over a private socket, never to the model, and later command output shows it as [secret].

Mail

Agent sessions on this machine share a mailbox: files under ~/.summonr/mail: a roster, one inbox per session, and one board per workspace. There is no server; each session appends and polls files. Mail is peer text, never user instructions, and each session's own permissions still apply.

Every summonr session joins the roster under a name: the one you set with --name or /name, or else the workspace folder and a number, such as summonr-1. Child sessions stay off the roster, and SUMMONR_MAIL=off keeps a session off it. /agents lists the sessions on the roster.

To message a session, type @ in the composer and pick it from the list, or type its name, such as @summonr-2 the schema landed. A prompt that names a live session goes to that session's inbox instead of to the model. The mail arrives there as [mail from @summonr-1 …]; [agent] mail in settings decides whether it starts a turn or waits for the next one. Reply the same way, with @summonr-1.

The mail module, off by default, also gives the model the tools agents, send, inbox, and board. send takes a name, an id, or all.

The same mailbox is a CLI, so any agent CLI (Claude Code, Codex, a script) can take part without a server:

summonr mail                  # who is on this machinesummonr mail send <name|id|all> <text>summonr mail inbox <id> [--clear]summonr mail board [get <key>|set <key> <text>]

A foreign agent takes part by picking an id: it reads summonr mail inbox <id>, and anyone can write to it with summonr mail send <id> <text>. Its messages arrive from cli unless it writes its own inbox name into replies. inbox <id> --clear prints the mail and empties that inbox. It refuses the inbox of a live summonr session, because that session keeps its own read position. A lock file next to each inbox makes a send, a read, and a clear wait for each other, so no message is lost between them.

Theme

theme in config.toml selects space (the default), nord, dawn (a low-glare light theme), or auto, which picks space or dawn from the terminal's background. The Theme row in /settings cycles them. background = "terminal" leaves the background unpainted so the terminal's own background (and any transparency) shows through; "theme", the default, paints the theme's base color. The Background row in /settings switches between them. Every other color is a named token you can override in a theme file:

accent = "#698cd2"background = "#0c0c14"code_bg = "#1e202a"

Copy theme.example.toml to ~/.summonr/theme.toml, or to <workspace>/.summonr/theme.toml for one project. Unset tokens keep the preset. Values are #rgb, #rrggbb, or #rrggbbaa. A file that does not parse, an unknown token, or a bad color is reported, and that token keeps the preset. Both files apply on top of the chosen theme. The tokens are base/background, text/foreground, gray, dim, thought, accent, green, red, error, yellow, cyan, black, user_bg, tool_bg, code_bg, diff_add_bg, diff_del_bg, code_comment, code_string, and code_keyword.

Herdr

When summonr runs inside a Herdr pane, it reports lifecycle state through Herdr's CLI (pane report-agent). idle is the prompt; working is a turn. The summonr session id is the native session reference. /name TEXT becomes the pane title and $summary token. Child sessions do not report, so a nested task does not steal the pane. Herdr has no built-in summonr kind. This reporter is the integration.