main
md 5.16 KB

siGit Code TUI features: tabs and thinking display

Status: shipped, pending merge (sigit #25 and #26, 2026-07-05). Owner: product/eng. Audience: internal (private repo). Scope: the siGit Code interactive terminal UI (src/chat.rs, Unix-only). These two features were built from direct requests rather than the parity roadmaps; this document records their specs after the fact so the behavior has a source of truth alongside sigit-code-parity-roadmap.md and sigit-code-copilot-cli-parity.md.


1. Spec: tabbed TUI (sigit #25)

Inspired by GitHub Copilot CLI's June 2026 terminal redesign (Session, Gists, Issues, and PR tabs switched with the Tab key).

Behavior

Three tabs, rendered as a bar at the top of the screen on every view:

  • Session (default): the chat exactly as before, messages, input, footer.
  • History: the saved sessions from the session store ($SIGIT_CONFIG_DIR/sessions/*.jsonl), listed newest first with id, coarse age ("2m ago"), and message count. Up/Down selects. Enter restores the selected session into the live backend, pushes a system message saying how many messages were restored, and returns to the Session tab. d starts a delete; a second d on the same selection confirms, any other key cancels. The pending delete is keyed by session id, not list index, so a refresh can never redirect the confirmation. r refreshes. An empty store shows "No saved sessions yet."
  • Cloud: siGit Code Cloud status and settings. Shows the signed-in account, on-device vs remote inference, current model and engine state, the Local Inference setting, the permission policy summary, and the config dir. l toggles Local Inference, with a note that the toggle takes effect at the next model selection because the running backend is not swapped. r refreshes.

Interaction rules

  • Tab cycles Session, History, Cloud. It only cycles when the input buffer is empty, so pasted text containing tabs cannot hijack navigation; on non-Session tabs the input is inactive so Tab always cycles. Esc on a non-Session tab returns to Session.
  • Inference keeps streaming while the user is on another tab. Restoring history sits behind the busy gate so the conversation cannot be swapped mid-turn.
  • A tool approval prompt arriving while on another tab auto-switches to the Session tab: a y/a/n prompt must never be invisible.
  • The Cloud status fetch runs as a spawned task feeding the render loop over a oneshot, because account::status_line() can hit the network and must not freeze rendering. A dead fetch degrades to "press r to retry".

Implementation notes

Pure logic (the tab cycle, age and row formatting, session_store::list) lives outside the Unix-only mod tui behind the same cfg-gate pattern as permissions::TUI_SESSION, so all four CI targets compile and unit-test it.


2. Spec: thinking display with /thinking (sigit #26)

Reasoning models (Qwen 3, DeepSeek-R1 distills) emit their thought process in <think> tags. Before this feature the TUI hid reasoning behind the spinner while streaming and then rendered it as an unconditional box on every finished message. Both extremes were wrong: watching the model reason is useful, reading every past thought is noise.

Behavior

  • While streaming, if the turn is still inside its think block, the TUI shows the last three wrapped lines of the reasoning live, dim italic, under the thinking spinner. Once visible (non-think) text arrives, the reply streams as before.
  • Finished replies collapse the reasoning to a one-line dim indicator with the line count, e.g. · thought for a bit (12 lines), /thinking to show.
  • /thinking [on|off] (bare flips) expands the full reasoning block, dim and visually separated, above each reply across the whole transcript, and collapses it again when turned off.

Boundaries

  • Display only. Nothing changes in what is sent to the model, what is saved to the session store, or what goes over ACP, where reasoning stays stripped and editors render their own thought UI. /thinking is TUI-only and not advertised over ACP, the same precedent as /resume.
  • Restored sessions re-split think blocks on entry, so /resume and the History tab keep the indicator behavior.
  • The TUI streams only when no tools are offered (on-device tool turns buffer to detect tool calls), so the live tail appears exactly on the turns that can stream, which are also the turns where think deltas exist.

Implementation notes

The live-tail extraction is a pure, width-aware function with its own greedy word wrap (hard-splitting over-wide words), unit-tested without ratatui.


3. Follow-ups worth considering

  • Copilot CLI's Issues and Pull requests tabs have a natural sigit equivalent once the MCP repo workflow tools (sigit-si #3) are consumed by the client: a fourth tab listing issues and PRs for the current repo when its origin points at sigit.si.
  • A persistent setting for the thinking toggle (settings.toml) if the per-session default proves annoying.
  • Coarse ages in the History tab could show absolute timestamps on a keypress if precision is ever needed.