Package Exports
- @wrongstack/tui
Readme
@wrongstack/tui
Ink-based terminal UI for the WrongStack agent. Renders the interactive chat panel, status bar, slash-command picker, model picker, todo list, and permission-confirm dialogs.
The TUI is lazy-loaded by @wrongstack/cli — it only imports React/Ink when the user passes --tui. Plain-REPL users pay no startup cost.
Install
pnpm add @wrongstack/tui @wrongstack/coreYou'd only depend on this directly if you're embedding WrongStack inside another tool and want the TUI surface. Otherwise install wrongstack.
Quick example
import { runTui } from '@wrongstack/tui';
import { Agent, DefaultEventBus, SlashCommandRegistry, DefaultAttachmentStore } from '@wrongstack/core';
const exitCode = await runTui({
agent, // configured Agent instance
slashRegistry, // SlashCommandRegistry
attachments: new DefaultAttachmentStore(),
events: new DefaultEventBus(),
model: 'claude-sonnet-4-6',
banner: true,
yolo: false,
appVersion: '0.1.6',
provider: 'anthropic',
family: 'anthropic',
keyTail: '…ABC',
effectiveMaxContext: 200_000,
altScreen: true,
});
process.exit(exitCode);What you get
┌─ banner ─────────────────────────────────────────────┐
│ wrongstack 0.1.6 — anthropic · claude-sonnet-4-6 │
└──────────────────────────────────────────────────────┘
user> refactor auth.ts to async/await
⠋ thinking (3 tools used · 4.2k tokens · 1.3s)
…
> █ ⚠ YOLO
───────────────────────────────────────────── ctx 47%- History pane — assistant text, tool calls, tool results, errors, turn summaries
- Streaming text — partial deltas render live; on abort, partial response is preserved
- Status bar — model · provider · context-window % · YOLO chip · spinner
- Input box — multi-line buffer with bracketed-paste detection, history (↑/↓), placeholder pills for attachments
- Pickers — fuzzy file picker (
@), slash picker (/), model picker (Ctrl+M) - Permission dialog — modal y/n/always/deny for
confirm-permission tools - Todo list — sidebar reflecting
ctx.todos - Attachments — images and files dropped into the input become inline content blocks
Key bindings
| Key | Effect |
|---|---|
Enter |
Submit |
Shift+Enter (or \ newline) |
Insert newline |
Ctrl+C (once) |
Abort current turn |
Ctrl+C (twice) |
Exit |
Ctrl+D (empty buffer) |
Exit |
↑ / ↓ |
History navigation when buffer empty |
@ |
File picker |
/ (at start) |
Slash command picker |
Ctrl+G |
Toggle agents monitor overlay |
Ctrl+S |
Edit autonomy settings (default mode + auto-proceed delay); also /settings |
Esc |
Close any picker / dialog / agents monitor |
Ctrl+L |
Clear screen (TUI keeps state — equivalent to scrolling) |
Mouse mode (--mouse)
With --mouse the TUI takes over terminal mouse tracking (forces alt-screen; native wheel-scroll and text selection are suspended until /mouse off). Then:
- Wheel scrolls chat history, or moves the selection in an open picker.
- Click or drag the right-edge scrollbar to jump/scrub the chat viewport to any position.
- Click inside the input to move the caret (single- and multi-line).
- Click a picker item to select + confirm it (single click).
- Click the permission dialog's
[y]/[n]/[a]/[d]buttons. - Click a
/rewindcheckpoint to select it; click again to rewind. - Click the status-bar model chip to open the model picker, or the
∞ MODEchip to open the autonomy picker. - Click a
/settingsrow to focus it; click again to cycle its value. - Click the lower region to dismiss an open monitor (
Ctrl+F/G/T/P) or the?help overlay (parity withEsc).
Keyboard shortcuts (always available in managed/alt-screen mode, not just --mouse):
- PgUp/PgDn — page scroll through chat history.
- Home/End — jump to top/bottom of chat history (when input is empty); move cursor to start/end of line (when typing).
- Ctrl+Home/End — jump to top/bottom of chat history (always, even when typing).
- ↑/↓ — navigate input history when buffer is empty.
Options worth knowing
altScreen: true(default) — render into the terminal's alternate screen buffer (vim/less/htop style). The TUI owns the whole viewport, native scrollback is untouched, and the live region cannot leak into terminal history. Raw mode plus alt-screen also means every keystroke — including Ctrl+S, Ctrl+Q, Ctrl+Z, Ctrl+\ — reaches Ink instead of being consumed by the terminal driver (runTuiadditionally registers no-op handlers forSIGTSTP/SIGQUIT/SIGTTIN/SIGTTOUas belt-and-suspenders). Setfalse(or pass--no-alt-screen) to render into normal scrollback if you specifically want completed chat to survive after exit; the trade-off is the documented live-region leak on resize / overlay-close / picker-submit, and that some shortcuts may fall through to the terminal.effectiveMaxContext— the context-bar denominator. Pass the model-specific value resolved viaModelsRegistry, not the family baseline; the 1M Opus variant has a much larger window than the 200k default.queueStore— if set, queued input survives a crash. Without it, queued lines are in-memory only.onClearHistory— invoked from the/clearslash command so the TUI can wipe its rendered history entries (keeping just the banner) whileAgent/memory reset happens elsewhere.onAfterExit— called once the alt-screen has been restored on clean shutdown. Use it to print "session saved" hints into the user's normal terminal (alt-screen exit erases the TUI view).
Architecture
runTui — entry; sets up bracketed paste, alt-screen, signal handlers
↓
App (React component) — useReducer-driven state machine
↓ dispatches events to ↓
EventBus — agent.run() emits these, the TUI subscribesState is a single useReducer State shape with discriminated-union Actions. The reducer is exported (reducer) and unit-tested.
License
MIT