Guides
Twelve recordings of the real interface
Each one is the terminal, not a diagram — a spoiler of the interface, not a mockup of it. They're recorded the same way as the run on the front page, with the same script every time, so nothing here is staged for the screenshot.
About these recordings They show the interface, so they do not need a model: each one runs under a throwaway HOME against a small OpenAI-compatible gateway on localhost — which is what OmniRoute, the built-in local gateway option, is for. No real provider is called and no real key is typed, which is why the models are called local-fast and local-smart. Everything else on screen is the agent you install.
Setting it up
First run: pick a provider
On the first launch in a project Kvist asks which provider to use. The list carries every built-in one plus an entry for adding your own OpenAI- or Anthropic-compatible endpoint. Arrow keys move, Enter confirms, and then it asks for that provider's key at a masked prompt and writes it to ~/.kvist/config.json — never into the project. The recording picks OmniRoute because that is the local-gateway option; picking OpenAI or Anthropic is the same three keystrokes.
╭─ Select LLM Provider ────────────────────────────────────────────────────────────────────────────╮│ ││ ↑/↓ — select, Enter — confirm, Esc — cancel ││ │╰──────────────────────────────────────────────────────────────────────────────────────────────────╯╭──────────────────────────────────────────────────────────────────────────────────────────────────╮│ OpenAI — Strict JSON mode with GPT models ││ Anthropic — Native Claude models (Opus, Sonnet, Haiku) ││ DeepSeek — Native DeepSeek models (V4 Pro, V4 Flash) ││ OpenRouter — Public multi-provider gateway (OpenAI, Anthropic, Google, etc.) ││ ❯ OmniRoute — Local OpenAI-compatible gateway ││ FreeFlow LLM — Gemini/Groq via FreeFlow, JSON — best effort ││ + Create custom provider... — Add a new OpenAI- or Anthropic-compatible endpoint │╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
Add your own endpoint
The last entry in the provider list adds an OpenAI- or Anthropic-compatible endpoint of your own: an id, the protocol, the base URL, whether it survives streaming, a key, a display name, and the model ids it serves. The key is whatever that endpoint expects — a gateway on your own machine usually ignores it. Streaming starts off, because many third-party gateways answer plain requests correctly and break on stream=true.
╭─ Select LLM Provider ────────────────────────────────────────────────────────────────────────────╮│ ││ ↑/↓ — select, Enter — confirm, Esc — cancel ││ │╰──────────────────────────────────────────────────────────────────────────────────────────────────╯╭──────────────────────────────────────────────────────────────────────────────────────────────────╮│ OpenAI — Strict JSON mode with GPT models ││ Anthropic — Native Claude models (Opus, Sonnet, Haiku) ││ DeepSeek — Native DeepSeek models (V4 Pro, V4 Flash) ││ OpenRouter — Public multi-provider gateway (OpenAI, Anthropic, Google, etc.) ││ OmniRoute — Local OpenAI-compatible gateway ││ FreeFlow LLM — Gemini/Groq via FreeFlow, JSON — best effort ││ ❯ + Create custom provider... — Add a new OpenAI- or Anthropic-compatible endpoint │╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
Two models, two jobs
fast carries routing, chat and the simple steps; smart takes complex work and error recovery. /models sets both, together or one at a time. Providers with a fixed catalogue offer a list; ones with dynamic ids — a local gateway, for instance — take the id you type.
╭─ Select action: ─────────────────────────────────────────────────────────────────────────────────╮│ ││ Enter — cancel and keep current models ││ │╰──────────────────────────────────────────────────────────────────────────────────────────────────╯╭──────────────────────────────────────────────────────────────────────────────────────────────────╮│ Enter one model id for all roles ││ ❯ Enter a separate model id per role ││ Keep current models (unchanged) │╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
Replace a key
/change-api replaces the key for the provider you are on, at the same masked prompt as the first launch. It never prints the old one; the panel says only that one is configured. Leaving the field empty keeps what is already there.
╭─ New OmniRoute API key ──────────────────────────────────────────────────────────────────────────╮│ ││ Leave empty and press Enter to keep the current key. ││ ││ Current: *** (already configured) ││ ││ Enter value (input hidden, Esc to cancel): ││ │╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
Driving a run
Every command in one place
Typing / opens completion; /commands prints the whole list with what each one does. Sixteen commands cover providers, models, roles, effort, sessions, the change journal and the verbose-output toggle.
╭────────────────────────────────────────────────────────────────────────────────────╮│ │ ││ ╭────────────────────────────────────────────╮ │ ││ │ │ │ Provider: [OmniRoute] ││ │ │ │ ││ │ │ │ Fast: [local-fast] ││ │ ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ │ │ ││ │ ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ │ │ Smart: [local-smart] ││ │ ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ │ │ ││ │ ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ │ │ Planner: [local-planner]││ │ │ │ ││ │ │ │ Role: [auto] ││ │ │ │ ││ │ │ │ Effort: [medium] ││ ╰────────────────────────────────────────────╯ │ ││ │ │╰────────────────────────────────────────────────────────────────────────────────────╯ /models — Select fast, smart and planner models /role — Choose auto, coder, devops or chat role /effort — Set the model's reasoning effort level /session — Create, switch or delete saved sessions /style — Select a style for chat/agent responses /resume — Continue the latest saved checkpoint /verbose — Enable or disable verbose runtime output /plan — Enable or disable the planning stage /changes — Show agent file changes across all sessions /revert — Restore agent-changed files to their baselines /strime — Toggle streaming for the active custom provider /bye — Save the active session and exit Kvist (aliases: /exit, /quit) ❯
Roles decide what it may touch
/role pins the run to coder, reader, devops or chat, or leaves it on auto. Auto routes each request itself: explicit rules first, then a small model, then keywords. Pinning devops here is the difference between an agent that edits your source and one that works on services and deployment.
╭─ Select Agent Role ──────────────────────────────────────────────────────────────────────────────╮│ ││ ↑/↓ — select, Enter — confirm, Esc — cancel ││ │╰──────────────────────────────────────────────────────────────────────────────────────────────────╯╭──────────────────────────────────────────────────────────────────────────────────────────────────╮│ Auto (automatic role detection) — Agent automatically detects role from your request ││ (coder/reader/devops/chat) ││ Coder — Code generation, editing, debugging, refactoring, testing ││ Reader — Read-only: inspect/analyze files and report; writes are disabled ││ ❯ DevOps — Infrastructure, deployment, server management, CI/CD, Docker ││ Chat — Questions, explanations, advice, brainstorming, discussions │╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
Reasoning effort, one knob
/effort sets how hard the model thinks, on one scale from false to max — six levels, one command, whatever the provider underneath calls them. Providers slice it differently, so when your choice collapses into something coarser Kvist says so at the moment you make it rather than leaving you to wonder: DeepSeek, for instance, answers that only false, high and max are actually distinct there.
╭─ Reasoning effort (applies to every provider) ───────────────────────────────────────────────────╮│ ││ ↑/↓ — select, Enter — confirm, Esc — cancel ││ │╰──────────────────────────────────────────────────────────────────────────────────────────────────╯╭──────────────────────────────────────────────────────────────────────────────────────────────────╮│ false — No reasoning — fastest, cheapest, weakest on hard problems ││ low — Light reasoning — quick answers, shallow multi-step work ││ medium — Balanced reasoning — the default trade-off ││ high — Deep reasoning — slower, better on complex tasks ││ ❯ xhigh — Extended reasoning — noticeably slower, for the hardest tasks ││ max — Maximum reasoning the provider allows — slowest │╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
Working styles
/style picks how the agent works and how it writes: nine styles for the action loop — minimalist, paranoid, architect, perfectionist, senior-dev among them — and five for chat. One session keeps one style, stamped when it starts, so the plan and the steps that follow it come from the same beliefs.
╭─ Select agents style ────────────────────────────────────────────────────────────────────────────╮│ ││ ↑/↓ — select, Enter — confirm, Esc — cancel ││ │╰──────────────────────────────────────────────────────────────────────────────────────────────────╯╭──────────────────────────────────────────────────────────────────────────────────────────────────╮│ Default — Does what was asked. Answers for everything it touched, and nothing else. ││ Senior-dev — Does what the result needs, not only what was asked. Never makes a point of it. ││ Friendly Developer — The same work, but you are never left alone with the result. ││ Perfectionist — Quality is part of the task, not a task for later. Pays in time. ││ ❯ Paranoid — An outage costs more than time spent checking. Looks before irreversible steps. ││ Minimalist — Every line is an obligation for years. Extends a task only toward deletion. ││ Explainer — A fix it cannot explain is no different from a wrong one. Finds the cause. ││ Fast-and-dirty — A prototype may be bad if it is honestly called one. Says so first. ││ Architect — A change that blurs the structure is negative even when the task is done. │╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
A diagnostic switch
/verbose turns on the diagnostic output. It says what it did, so you are never guessing which state you are in.
╭────────────────────────────────────────────────────────────────────────────────────╮│ │ ││ ╭────────────────────────────────────────────╮ │ ││ │ │ │ Provider: [OmniRoute] ││ │ │ │ ││ │ │ │ Fast: [local-fast] ││ │ ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ │ │ ││ │ ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ │ │ Smart: [local-smart] ││ │ ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ │ │ ││ │ ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ │ │ Planner: [local-planner]││ │ │ │ ││ │ │ │ Role: [auto] ││ │ │ │ ││ │ │ │ Effort: [medium] ││ ╰────────────────────────────────────────────╯ │ ││ │ │╰────────────────────────────────────────────────────────────────────────────────────╯❯ /planPlanning stage disabled — the agent starts acting immediately, without an upfront plan. ❯
Sessions and undo
Named sessions
/session lists what the workspace has and creates a new one. Each session keeps its own history, checkpoint and style. Creating one does not erase the old conversation — it stays on disk and is one /session away. An auto-named session left empty is dropped, so opening and quitting cannot litter the list.
╭─ Manage Sessions (current: chat-20260910-155928) ────────────────────────────────────────────────╮│ ││ ↑/↓ — select, Enter — switch, Esc — cancel ││ │╰──────────────────────────────────────────────────────────────────────────────────────────────────╯╭──────────────────────────────────────────────────────────────────────────────────────────────────╮│ ● chat-20260910-155928 — current session ││ ❯ + Create new session... — Start a fresh named session │╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
See what changed, put it back
/changes prints a diff of everything the agent wrote in this workspace, across sessions. /revert restores it — after a confirmation that opens on “No”, because it is a decision. It only overwrites content the journal can prove the agent wrote; anything you changed yourself is kept.
╭────────────────────────────────────────────────────────────────────────────────────╮│ │ ││ ╭────────────────────────────────────────────╮ │ ││ │ │ │ Provider: [OmniRoute] ││ │ │ │ ││ │ │ │ Fast: [local-fast] ││ │ ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ │ │ ││ │ ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ │ │ Smart: [local-smart] ││ │ ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ │ │ ││ │ ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ │ │ Planner: [local-planner]││ │ │ │ ││ │ │ │ Role: [auto] ││ │ │ │ ││ │ │ │ Effort: [medium] ││ ╰────────────────────────────────────────────╯ │ ││ │ │╰────────────────────────────────────────────────────────────────────────────────────╯╭───────────────────────── Agent changes in this workspace (all sessions) ─────────────────────────╮│ # slugify.py (session: chat-20260910-042952) ││ --- slugify.py ││ +++ slugify.py ││ @@ -7,4 +7,4 @@ ││ ││ ││ def slugify(title: str) -> str: ││ - return _KEEP.sub("-", title.lower()) ││ + return _KEEP.sub("-", title.lower()).strip("-") ││ │╰──────────────────────────────────────────────────────────────────────────────────────────────────╯ ❯
Without an interface
No interface at all
Kvist answers one-shot commands for scripts and other frontends: --config-info reports the saved provider and models without an LLM call, --session-op manages sessions, --journal-op prints or reverts the change journal. All of it is JSON on stdout. The Electron GUI is built on the same protocol and nothing else.
$ python main.py --workspace /tmp/kvist-hl-ws --config-info | jq '{provider: .saved_provider, role:.saved_role}'{ "provider": "omniroute", "role": "auto"} $ python main.py --workspace /tmp/kvist-hl-ws --session-op status | jq '{active: .active_session, sessions}'{ "active": "chat-20260910-042952", "sessions": [ "chat-20260910-042952" ]} $ python main.py --workspace /tmp/kvist-hl-ws --session-op create --session-name review | jq '{ok, active_session}'{ "ok": true, "active_session": "review"} $ python main.py --workspace /tmp/kvist-hl-ws --journal-op changes | jq -r .report