grokbot-tui

Chats and bots

The layout, the bot list, message bubbles, reactions, cards, sync and delivery.

Layout

 ▄▀▀▀▀▀▀▀▀▀▀▄  ▗▛▜▖        ▜▌      ▜▛▜▖     ▟     account  @default ▾  F3                       header
 ▀▀▀▀▀▀▀▀▀▀▀▀  █   ▜▟▜▖▟▀▙ ▐▌▟▘    ▐▙▟▘▟▀▙ ▝█▀    theme  ‹default› ▾  dark  light  system  F4   (logo, title,
 ▀▀▀▀▀▀▀▀▀▀▀▀  ▜▖▜▌▐▌▝▘█ █ ▐▛▙     ▐▌▐▌█ █  █▗    login cursor · box.cursorvm.com               switchers)
 ▀▀▀▀▀▀▀▀▀▀▀▀   ▀▀▘▀▀  ▝▀▘ ▀▘▝▘    ▀▀▀ ▝▀▘  ▝▘    mouse: on (F6)
╭ Bots ──────╮╭ Chat: Helper · Helper is replying ⠹ ──────────────────╮
│ Helper     ││                                               you     │  body: command output
│ Coder      ││                                            ╭──────╮   │  and chat transcript,
│            ││                                            │ hi   │   │  messages on rounded
│            ││  Helper                                    ╰──────╯   │  bubbles (reactions on
│            ││  ╭─────────── 👍 ─╮                                   │  the top-right corner)
│            ││  │ hello!         │                                   │
│            ││  ╰────────────────╯                                   │
╰────────────╯╰───────────────────────────────────────────────────────╯
╭ /command or message ──────────────────────────────────────────────────╮  bottom input bar
│ > _                                                                   │
└───────────────────────────────────────────────────────────────────────┘

Keys: Enter submit · Tab toggle focus input/sidebar · ↑/↓ (or j/k) + Enter in sidebar to pick a bot · ↑/↓, PgUp/PgDn, mouse wheel scroll body · Esc cancel a prompt · Ctrl-C / Ctrl-Shift-C copy (the selection, else the input; never quit) · Ctrl-D (also Ctrl-Q, /quit) quits at once (in a card it dismisses the card) · F1 help · F2 pick a bot · F3 pick an account · F4 cycle dark/light/system · F5 pick a theme · Shift-F5 theme editor · F6 mouse capture on/off.

Markdown in replies

Bot replies render as markdown (CommonMark plus GitHub tables, ~~strikethrough~~ and task lists): headings, bold, italic, inline code, fenced code blocks (a shaded block with the language label; no syntax highlighting yet), bullet and numbered lists with nesting, ☑/☐ task items, │ blockquotes, links as label <url>, box-drawn tables (columns shrink and cells end in … when the chat is narrow) and --- rules. Lines are wrapped before drawing, so scrolling and the bottom-follow stay exact. Your own messages stay plain text. Notices and errors sit in a scrollable panel under the chat, at most 7 rows (click it, then ↑↓ / PgUp / wheel). Theme parts: md_heading, md_code, md_code_block, md_quote, md_link, md_list_marker, md_rule (also link URLs), md_table_border.

Links in the chat are clickable: markdown link labels, their <url>, and bare http(s):// URLs (styled with md_link). A mouse click (with mouse capture on) opens the link with the system opener. Each frame also wraps the links in OSC 8 hyperlinks, so Ghostty, kitty, WezTerm, Windows Terminal, foot, iTerm2 and Alacritty 0.13+ make them natively clickable (e.g. Ctrl/Cmd-click, or with F6 mouse off). Set GROKBOT_TUI_NO_HYPERLINKS=1 to turn OSC 8 off. Keyboard: /open list then /open <n>, or vim o for the newest.

Emoji and reactions

Type : and part of a name, then Tab, to pick an emoji (:tada → 🎉), using the same popup as slash-command completion. Complete :name: shortcodes are turned into emoji when you send. Emoji (ZWJ families, flags, skin tones, ❤️ with VS16) count as one character and two columns in the input and in chat wrapping. /react 👍 (or /react :thumbsup:, vim r) toggles a reaction pill on the newest bot message (top-right corner of its bubble). Tab completes the emoji (already on the message, then recently used, then 👍 ❤️ 😂 🎉 🔥 👀 ✅…) and the message index; /unreact + Tab offers what's on the message. Every message has a [☺+] button (theme part react_button): click it, use bare /react, or vim R (message.react_picker) for a searchable emoji grid (by shortcode and name; recent and on-message first, then by category). Reactions are local: grokbot_api 1.1 has no reaction endpoint and transcripts carry no reaction data, so they're kept in context.db (per account and bot) and never sent. Theme part: reaction_pill.

Blocks instead of emoji mean the terminal's font has no emoji glyphs (common over SSH into a terminal with a text-only font). /emoji text (pref emoji.render = auto|glyph|text, default auto = glyph) shows reactions, the react button and the picker as :shortcode: text instead; /emoji glyph switches back. No emoji images are bundled.

Newlines in the prompt

Prefer Ctrl-J for a newline (works in every terminal). Ctrl-Enter inserts a newline on Windows (conhost and Windows Terminal always report it) and in Linux terminals that report modifiers (kitty, Ghostty, foot, WezTerm; Alacritty needs a binding such as { key = "Enter", mods = "Control", chars = "\n" }, which arrives as Ctrl-J). Shift/Alt-Enter work when the terminal reports modifiers. A trailing \ then Enter also inserts a newline. macOS Terminal.app sends plain Enter for Shift/Ctrl-Enter; enable “Use Option as Meta key” for Option-Enter, or use Ctrl-J / \+Enter.

Right column: bot header and attachments

The right column starts with a header for the open chat's bot: its name in bold and its description, wrapped (long ones are cut after 3 rows; click ▾ more / ▴ less). It follows the bot-list refresh, so renames and new descriptions show at once. The sidebar shows bot names only. A bot whose name has the description typed into it ("Lippy · Very ratatui-focused …") shows as "Lippy", with the rest as its description.

Below the header, a collapsible bar on the right of each chat lists its attachments, sent and received, with type, name, size, sender and time. [view] opens images in the viewer, text/HTML/Markdown in the built-in browser and other files in the system app (with the SSH guard); [⤓] opens the download modal; clicking a row jumps to its message. It has a search box and All/Img/Vid/Aud/Doc filters, and keys j/k, Enter, v, d, /, t and Esc when focused. Toggle with F9, the «/» buttons, View ▸ attachments, /attachments show|hide|toggle (alias /files) or keymap view.toggle_attachments; pref ui.attachments_panel (show/hide, default show since 1.28.0; hide = 1-column strip with the count). These toggle the whole column, header included. Hidden when the chat body is under 70 columns. Theme parts: attach_panel_frame, attach_panel_filter, attach_panel_count, bot_header_name, bot_header_desc.

Bot replies are laid out like your messages: the bot's name on its own row, the reply directly under it, the same 2/3 bubble width, on the left where yours are on the right. Since 1.38.0 each message's text sits on a rounded bubble (╭─╮ │ … │ ╰─╯), accent- coloured for yours and muted for the bot's, and its reaction pills ride the bubble's top border at the top-right corner, like the desktop app. Every panel, dialog, popup and the input box uses rounded corners too.

Grok Bot cards: questions, approvals, secrets, forms and drafts (1.29.0)

When a bot asks you something, the chat shows a card with its state (● pending, answered, dismissed or expired) and a button. Click the button, press F11 (keymap card.open, vim normal c) or run /card to open the newest pending one:

Card Keys
Question (single, multi-select, your own answer, multi-step) 1-9 or ↑↓ Enter; Space toggles; Tab type your own; x dismiss
Auto-review approval / local tool permission a approve (allow once), d deny
Secret request type (masked) and Enter; Ctrl-D dismiss
Form Tab between fields (secret fields masked), Ctrl-S submit, Ctrl-D dismiss
Email / Slack / meeting draft s send, e edit the body, x discard
Agent ↔ agent or agent ↔ Cursor conversation (1.41.4) Click [view conversation] (or F11 / /card when it is the newest pending card); ↑↓ / mouse wheel scroll; Esc close

Secrets are sent only through the gateway's submitSecret / submitUserForm calls; they are never logged, stored or shown and are wiped after sending or closing. When this bot messages another Grok Bot or a Cursor cloud agent, those turns are collapsed into a card in the main chat (↔ agent: … or ▣ cursor-agent: …) instead of appearing as regular bubbles; click the card to read the side conversation in a modal. The turns come from transcript kind: "message" entries with toAgent / fromAgent, and Cursor launches from message.type == "cursor-agent" (bcId + title). Purchase approvals, connector grants and similar cards are shown read-only; act on them in the desktop app. Theme parts: card_frame, card_pending, card_done, card_button.

Chat sync

Bots come from listAgents. Chat text comes from getAgentTranscriptTail (there is no separate listChats/getChat API). On bot select (and on /reload), grokbot-tui pages that endpoint via beforeSeq/nextBeforeSeq up to 1000 entries, merges into SQLite, and rebuilds the body from the DB so messages from the desktop app show up. While a bot is active it also polls the newest page every few seconds (backing off to 15s when idle).

Sending and delivery

sendPrompt returns only a receipt (messageId). Replies are picked up by polling the transcript. - Accepted (receipt) → "replying" indicator until a new bot entry shows up. - Rejected (4xx, or a disallowed URL) → shown as not delivered, and the local copy is removed. - Unknown (5xx, network error, timeout, or no receipt) → grokbot-tui does not resend. It checks the transcript right away, and the message shows as confirmed if it appears there. Check before resending yourself.

Your message is stored locally as pending and takes the transcript entry id once the gateway echoes it, so it is never duplicated. Transcript text is parsed like grok-bot-cli's transcript.js (text / prompt / message / content parts / message.content / preview). Terminal control sequences are stripped before display. Whether an entry is yours is decided from its kind first: known user labels (user-prompt, user, human, prompt, user-message) are yours; send-message and any other kind are the bot's — even if another field contains user or prompt, so assistant or coding-agent reports are not labelled as you. When kind is absent, role, then sender, then type are checked for those same exact labels (not substring matches).

Selecting text

With mouse capture on (the default; F6 / /mouse toggles and persists as ui.mouse_capture), drag in the chat to select. The selection is copied on mouse-up. Ctrl-C / Ctrl-Shift-C also copy a selection. Esc clears it.

Since 1.30.0 a selection copies only message text: the selected cells are mapped back to each message's source (your text, or the bot's markdown source). Bubble padding, the name header, the [⧉]/[☺+] row, card and status rows, code-block headers, table borders, the sidebar and the right column are left out, and lines that were only soft-wrapped on screen join back into one line. The selection is clipped to the chat pane; a message selected from start to end copies whole.

Copy buttons:

Copying uses OSC 52 (works over SSH; in tmux set -g set-clipboard on) and, when running locally, also the system clipboard (wl-copy, xclip, xsel or pbcopy if installed). A notice says what was copied ("Copied 12 lines."). Theme part: copy_button.

Shift-drag or Option-drag usually bypasses mouse capture so the terminal can select instead (that copies what's on screen, borders included).

Prompt history and completion

Every prompt and slash command you submit is saved in context.db (per account, newest first, with the bot it went to and when), so history survives restarts. The newest history.max_entries are kept (default 10000; /prefs set history.max_entries 50000). Consecutive duplicates are stored once, and /login … lines are saved without their arguments so a pasted token never lands in history.

Tab after / fuzzy-completes slash commands and arguments; Shift-Tab cycles.

Loading spinner

While grokbot-tui signs in, loads the bot list and opens the first chat (at startup, on /account use <name>, and during /login gateway|cursor|desktop), a centered overlay shows a braille spinner and the current stage: Signing in to …, Checking gateway …, Exchanging Cursor token… (the EnsureSandBox exchange), Reading the desktop app session…, Loading bots…, Loading chat with …. The work runs in the background, so input keeps working (Ctrl-D / Ctrl-Q still quit). If a step fails, the error replaces the spinner with a retry hint; Esc dismisses it. Pref ui.spinner = off shows a static glyph instead of the animation (reduced motion). Theme parts: loading (frame and message) and spinner (glyph).

Layout toggles, menus and Alt over SSH

Images in replies and your image attachments show inline in the bubble (scaled to fit, at most images.inline_max_rows rows, default 12; images.inline = off turns it off). Click one for the full-size viewer.

/status [extra text] asks the current bot "Where are we now? Give me a brief status of your current tasks." (plus your text) as a normal prompt; change the text with commands.status_prompt.

Theme parts: collapse_button, sidebar_strip.

Video, GIFs and music (1.24.0)

Videos and animated GIFs play inside the bubble, scaled like images (capped at images.inline_max_rows), with a one-row bar under them: [▶]/[⏸] toggles playback, clicking the bar seeks, and the label shows elapsed/total time (GIFs: frame position). Clicking the picture opens the full-size player (Space play/pause, ←/→ seek 5 s, Home, Esc/q close). Music links and attachments get the same bar. Only one video or track plays at a time, and it pauses when scrolled out of view; GIFs loop on their own.

Build Decodes Download
standard GIF; H.264 MP4 (OpenH264, compiled in); MP3, AAC/M4A, FLAC, Vorbis, WAV. Other video codecs use a system ffmpeg if one is on PATH grokbot-tui-installer.sh/.ps1, .deb/.rpm/.msi/.pkg, Homebrew, AUR
-ffmpeg the above, plus FFmpeg linked in statically: nearly every codec and container, nothing to install grokbot-tui-ffmpeg-<target>.tar.xz/.zip

grokbot-tui-setup.sh / grokbot-tui-setup.ps1 (release assets) ask which build to install (--variant standard|ffmpeg / -Variant to skip the question).

Prefs: media.inline (on), media.fps (10, 1–30), media.decoder (auto|builtin|ffmpeg|system), media.gif_autoplay (on). /media pause|stop|decoders. Theme part: media_bar. On Linux, sound needs ALSA (libasound2).

Right column: bot header and attachments

The right column starts with a header for the open chat's bot: its name in bold and its description, wrapped (long ones are cut after 3 rows; click ▾ more / ▴ less). It follows the bot-list refresh, so renames and new descriptions show at once. The sidebar shows bot names only. A bot whose name has the description typed into it ("Lippy · Very ratatui-focused …") shows as "Lippy", with the rest as its description.

Below the header, a collapsible bar on the right of each chat lists its attachments, sent and received, with type, name, size, sender and time. [view] opens images in the viewer, text/HTML/Markdown in the built-in browser and other files in the system app (with the SSH guard); [⤓] opens the download modal; clicking a row jumps to its message. It has a search box and All/Img/Vid/Aud/Doc filters, and keys j/k, Enter, v, d, /, t and Esc when focused. Toggle with F9, the «/» buttons, View ▸ attachments, /attachments show|hide|toggle (alias /files) or keymap view.toggle_attachments; pref ui.attachments_panel (show/hide, default show since 1.28.0; hide = 1-column strip with the count). These toggle the whole column, header included. Hidden when the chat body is under 70 columns. Theme parts: attach_panel_frame, attach_panel_filter, attach_panel_count, bot_header_name, bot_header_desc.

Bot replies are laid out like your messages: the bot's name on its own row, the reply directly under it, the same 2/3 bubble width, on the left where yours are on the right. Since 1.38.0 each message's text sits on a rounded bubble (╭─╮ │ … │ ╰─╯), accent- coloured for yours and muted for the bot's, and its reaction pills ride the bubble's top border at the top-right corner, like the desktop app. Every panel, dialog, popup and the input box uses rounded corners too.

Grok Bot cards: questions, approvals, secrets, forms and drafts (1.29.0)

When a bot asks you something, the chat shows a card with its state (● pending, answered, dismissed or expired) and a button. Click the button, press F11 (keymap card.open, vim normal c) or run /card to open the newest pending one:

Card Keys
Question (single, multi-select, your own answer, multi-step) 1-9 or ↑↓ Enter; Space toggles; Tab type your own; x dismiss
Auto-review approval / local tool permission a approve (allow once), d deny
Secret request type (masked) and Enter; Ctrl-D dismiss
Form Tab between fields (secret fields masked), Ctrl-S submit, Ctrl-D dismiss
Email / Slack / meeting draft s send, e edit the body, x discard
Agent ↔ agent or agent ↔ Cursor conversation (1.41.4) Click [view conversation] (or F11 / /card when it is the newest pending card); ↑↓ / mouse wheel scroll; Esc close

Secrets are sent only through the gateway's submitSecret / submitUserForm calls; they are never logged, stored or shown and are wiped after sending or closing. When this bot messages another Grok Bot or a Cursor cloud agent, those turns are collapsed into a card in the main chat (↔ agent: … or ▣ cursor-agent: …) instead of appearing as regular bubbles; click the card to read the side conversation in a modal. The turns come from transcript kind: "message" entries with toAgent / fromAgent, and Cursor launches from message.type == "cursor-agent" (bcId + title). Purchase approvals, connector grants and similar cards are shown read-only; act on them in the desktop app. Theme parts: card_frame, card_pending, card_done, card_button.

Muting other devices while you're here

Set notifications.claim_presence = true (per account; /mute, or Notify → mute) and, while grokbot-tui is focused and you've used it in the last notifications.idle_minutes (default 10), it tells the Grok Bot service this install is active, the same report the desktop app sends, so pushes to your other devices can be held. 🔕 in the header shows a live claim; /whoami shows the lease. It's released when the terminal loses focus, you go idle, log out, switch accounts or quit; if the process is suspended the lease simply expires. It needs a Cursor session (/login desktop or /login cursor). Theme part: presence_indicator.

Edit this page on GitHub