TUI and Non-Interactive Standards
Use this note when designing or reviewing any user-facing kb feature.
Core Rule
Every meaningful CLI feature must be usable in the right mode surfaces:
- Interactive shell/TUI entry when the user starts with bare
kb - One-shot non-interactive CLI entry when the user runs
kb <command> ... - Help entry via
--help - TUI slash entry such as
/initwhen the feature is available from the Ink shell
Do not treat the TUI path as extra polish. It is part of the product surface.
Interaction Contract
kbin a real TTY should launch the interactive TUI shell.kb --helpshould print top-level help and exit.kb <command> ...should be non-interactive by default unless that command intentionally runs an interview or session flow.kb <command> --helpshould print help and exit without starting real work.- If a command is exposed inside the TUI, the slash path should mirror the CLI path closely enough that the same feature can be exercised both ways.
- Success or follow-up copy in the TUI transcript (e.g. after
/base useor/base use --default) should use slash form (/base use …), notkb …, so users are not told to leave the Ink shell. Shared formatters takeCmdModeand build hints viacmd()insrc/cli/cmd-ref.ts.
Terminal scrollback (Ink)
- Completed transcript rows should go through Ink
<Static>where they must not be redrawn every frame, so the host TTY keeps them in normal scrollback (seesrc/tui/components/HistoryPane.tsx). - Cursor’s integrated terminal can behave differently from iTerm, Terminal.app, or VS Code’s terminal panel (e.g. scrollback feels “stuck”). If the issue appears only there, try an external terminal to confirm; the
<Static>split is still the right default for real TTYs. - In Ink chat mode,
read()used to drop the prompt string (only the shell showedyou>). Sub-flows such as/docs generatenow echo any non-idle prompt into the transcript and reuse a short form as the input placeholder so questionnaire / review steps read as a normal back-and-forth.
Examples:
kb initmust support both its command-line path and the TUI/initpath.kb scanmust support both its command-line path and the TUI/scanpath.kb base use/kb base deletemust work as bothkb base …(CLI) and/base use …//base delete …(TUI).kb syncmust work as bothkb sync(CLI) and/sync(TUI) when release-install commands are exposed.- A help flag should work from both
kb --helpandkb init --help. - A normal intent command like
kb query "topic"is already non-interactive by shape and should not need an extra mode flag. - The public intent surface is exactly
kb query,kb submit, andkb invalidate, mirrored by/query,/submit, and/invalidatein the TUI shell. kb facts(list / search / show) must work as/facts …in the TUI shell (and in chat mode for parity), mirroring the same flags as the CLI.--verboseonkb query/kb chatadds human rowssummary>/status>/confidence>.--debugswitches the defaultsources>(titles-only) footer to one detailedsource>line per hit. Use these on that invocation (TUI shell:chat --verbose,chat --debug) before a chat session starts—there is no mid-session toggle.
Flag Standardization Guidance
- Do not add new mode flags casually.
- Prefer entrypoint-driven behavior:
- bare
kb=> interactive shell kb <command>=> one-shot non-interactive command
- bare
- Reserve
--non-interactivefor commands that otherwise prompt the user during their own command flow (e.g.kb init). - Before renaming or removing any existing flag, verify current semantics, help output, scripts, tests, and TUI dispatch paths.
Current repo guidance:
- Subcommands are already one-shot by default; adding
--non-interactiveto them is redundant. - Mode flags should be reduced, not multiplied.
Mutation Safety Guidance
For commands that can mutate durable KB state or external systems, prefer a consistent safety contract:
- Default to a non-mutating mode unless the user explicitly opts into writes.
- Use
--applyas the shared opt-in flag for real writes. - Do not expose a “preview mode” flag — default (no flag) is already preview/no-op.
- Use
--previewonly forkb invalidate, where the default is to apply (reversed semantics). - Help text and success output should make the default clear so users are not surprised when a command previews instead of writing.
Current repo direction:
kb publish ...previews by default and only writes on--apply.kb scanpreviews by default and only writes on--apply.kb invalidatepreviews by default and only writes on--apply.- Any preview-by-default command should, in interactive mode, show the plan then ask “Apply? [y/N]” rather than requiring the user to re-run with
--applymanually. - Avoid inventing command-specific synonyms for “really do it” when
--applyalready fits.
Validation Checklist
For any new or changed user-facing command, verify the relevant subset of:
kbkb --helpkb <command> --helpkb <command> ...- TUI slash invocation such as
/init - Real-TTY behavior, not only unit tests
For high-risk CLI changes, keep the repository rule from AGENTS.md: run end-to-end validation with kb init using a disposable ci-* base before declaring completion.
Known Gaps to Watch
kb init --helpshould behave like help, not kick off or resume init work.
Prerequisites and errors (DRY)
Many commands need exactly one of these at a time, and errors must name the missing prerequisite clearly (never “A or B” when both matter):
- Knowledge base — an effective base (
config.activeBaseorconfig.defaultBase), or an explicit--base <name>on commands that support it. - LLM — a constructible provider from
~/.kb/config.json+ environment keys (kb config llm).
Canonical user-facing strings live in src/cli/cli-prerequisites.ts (CLI_ERROR_NO_KB_BASE, CLI_ERROR_NO_LLM_PROVIDER, etc.). CLI and TUI should reuse them so /query and kb query behave the same as bare kb + slash commands.
When a command needs both (e.g. kb chat), check base first, then LLM, and surface one error at a time.