Available now
One workspace. One gate for every action.
Kvist is a terminal coding agent that works in a single project directory. It works through tool calling — read, edit, run, verify — and feeds the results back before deciding the next step. Models without tool calling fall back to JSON. Every action goes through one executor that can refuse it.
Kvist is released, with source available under the MIT license. The run and twelve guides below show the real interface. Linux and Windows · macOS without guarantees
Recorded run
A real run, not a rehearsal
This is the terminal recording of Kvist fixing a slug function: it reads both files, plans, and stops at the approval gate with the one-line diff on screen before it is allowed to write. Then it runs the check and reports 4/4. Nothing was cut or re-taken. The replay below is the recording parsed into text, so you can select it and your screen reader can read it.
╭────────────────────────────────────────────────────────────────────────────────────────────────╮│ │ ││ ╭────────────────────────────────────────────╮ │ ││ │ │ │ Provider: [DeepSeek] ││ │ │ │ ││ │ │ │ Fast: [deepseek-v4-flash] ││ │ ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ │ │ ││ │ ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ │ │ Smart: […seek-v4-flash-vision-exp]││ │ ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ │ │ ││ │ ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ │ │ Planner: [deepseek-v4-pro] ││ │ │ │ ││ │ │ │ Role: [coder] ││ │ │ │ ││ │ │ │ Effort: [medium] ││ ╰────────────────────────────────────────────╯ │ ││ │ │╰────────────────────────────────────────────────────────────────────────────────────────────────╯❯ check.py fails. Read slugify.py and check.py, fix slugify so runs of non-alphanumeric characters collapse into a single dash and leading and trailing dashes are stripped, then run python3 check.py.⚡ Role: coder (manual)╭─ Final Answer ───────────────────────────────────────────────────────────────────────────────────╮│ ││ Fixed slugify.py so runs of non-alphanumeric characters collapse into a single dash and ││ leading/trailing dashes are stripped. python3 check.py now passes 4/4. ││ │╰──────────────────────────────────────────────────────────────────────────────────────────────────╯ ❯
Use the slider to move through the run. The final frame is shown when playback is stopped.
Recorded with web/assets/demo/record.fish — the same script, every time.
The loop
Tool calling, a few changes at a time
A request is routed to a role and executed step by step through tool calling; a model or provider without it falls back to JSON. In one response the model may ask for any number of reads and searches, which run side by side, up to four at a time. Edits and commands are capped at eight per response and run in order, and a failed one stops the rest. The action set is closed — the model cannot invent a new capability mid-run.
-
21 action types
read_file, write_file, replace_in_file, search_code, run_shell, git_diff, fetch_url, delegate_task, refuse, final_answer and eleven more. The list is a Literal in protocol.py, and a test fails the build if the prompts, the parser and the executor drift apart.
-
5 roles plus auto
coder edits, reader investigates without writing, devops handles services and deployment, chat answers without touching files, universal chats and acts as the request needs and is the default when nothing else fits. Routing tries explicit rules first, then a small model, then keywords — in that order, in English and Russian.
-
Untrusted output stays untrusted
Every result the model can be influenced by — file contents, search hits, shell output, fetched pages — is wrapped in a delimited block, and a forged closing tag inside the payload is defanged before it reaches the prompt.
-
Progress is derived, not declared
The model does not report a percentage. Progress is derived from the phase each response is in — inspect, plan, edit, verify — and only moves forward. When the step budget runs out, Kvist asks whether to continue instead of stopping silently.
The boundary
What it will not do
The parts that hold are the ones that never read the prompt. A hostile file, a poisoned web page or a confused model all meet the same gates.
-
.env is refused outright
Reading or writing a .env file is blocked on the path check, before the file is opened, and blocked again for shell commands. The refusal names the violation instead of failing quietly.
-
Secrets are masked, not trusted
Under Bubblewrap the sandbox shadows .ssh, .aws, .gnupg, .kube and .azure, id_rsa / id_ed25519 / id_ecdsa, .netrc, .npmrc, .git-credentials and every .pem, .key, .p12, .pfx and .crt. Files become /dev/null, directories become empty tmpfs — so a path built at runtime finds nothing either.
-
No network from the shell by default
The sandbox unshares the network namespace unless AGENT_SANDBOX_NET is set to allow. PID, IPC and UTS namespaces are unshared too, host /home and /root are hidden, and the host root is never bound.
-
Dangerous commands are approved once each
git push, git reset, git clean, docker rm, docker prune, systemctl start/stop/enable and package-manager install/remove are per-execution approvals. Project policy can deny more, never less: deny and the built-in hard blocks always win over allow.
-
Approval binds the exact command
What you approve is argv plus the resolved cwd, the role, the policy digest, the sandbox profile, the child environment, the target executable's identity and content, and the attested shebang interpreter. If any of it changes before the spawn, the approval is void.
-
Nothing detaches and survives
A child that calls setsid() escapes a process group, so the whole subtree runs in a cgroup v2 scope and is torn down with cgroup.kill on both timeout and completion. Output is drained into fixed-size buffers, so a noisy child cannot grow the parent.
Kvist is a supervised tool, not a security product. Transitive imports are not individually attested, history-aware tools can surface content that never appears in argv, and a narrow window remains between the final check and the spawn. Read the diff.
Recovery
Interrupt it without losing the work
A run writes down what it is about to do before it does it. That is what makes an interrupted run resumable and a change reversible.
-
/resume continues the right conversation
A checkpoint is written before every externally visible action. Resuming follows the checkpoint into the session it belongs to instead of replaying it into whatever session happens to be open.
-
/revert only touches what it can prove
The journal separates a write it can prove it made from content that merely looks right. Reverting overwrites the first and leaves the second alone. A corrupt journal stops the mutation instead of being read as an empty one.
-
Sessions are documents, not a scratch buffer
/new starts a session; it does not erase the previous one. Each session keeps its own history, checkpoint and style, and the style is stamped at the start so a plan and the steps that follow it come from the same beliefs.
Control
Your models, your rules
Providers, effort and tone are settings, not assumptions baked into the agent.
-
Six providers, plus your own endpoint
OpenAI, Anthropic, DeepSeek, OpenRouter, OmniRoute and FreeFlow, or any OpenAI-compatible or Anthropic-compatible endpoint you add from /provider. Streaming and JSON mode are per-provider facts, not one assumption applied to all of them.
-
Two model tiers per run
fast and smart. A run starts on fast and escalates to smart on a complex task, a parse error, repeated errors or stagnation — unless you pinned a model yourself.
-
Six levels of reasoning effort
false, low, medium, high, xhigh, max — one /effort command, translated per provider. If a provider rejects the setting, Kvist retries without it and warns you every single time.
-
Fourteen working styles
Nine for the action loop — minimalist, paranoid, architect, perfectionist, senior-dev and more — and five for chat. One session keeps one style, so the executor cannot quietly disagree with the plan it was handed.
-
The project states its own rules
KVIST.md and KVIST-CHAT.md in the workspace root carry your build commands, layout and conventions into every call. They are workspace content, so they are quoted as data inside bounds that rule out changing the role, the safety rules or the action protocol.
-
Headless when you need it
A newline-delimited JSON frontend runs Kvist from another process, with session and journal operations available as one-shot commands. The Electron GUI uses that same protocol and nothing else.
Install
Get started with Kvist
Run the release installer to install the kvist command and the Node.js needed by its terminal interface. No Python or administrator rights are needed. Choose your components and confirm installation.
-
01
Install the release
Linux / macOS: curl -fsSL https://github.com/davud-d/Kvist/releases/download/latest-plugin-build/install.sh | bash
-
02
Windows (PowerShell)
irm https://github.com/davud-d/Kvist/releases/download/latest-plugin-build/install.ps1 | iex
-
03
Start it in a project
cd into the project and run kvist. The launcher uses the current directory as the workspace. The first launch asks for a provider, the models and the key, and saves them in ~/.kvist/config.json.
-
04
Give it a task
Type the task, or / for the command list. Approvals appear inline; answer them and the run continues. /changes shows what was written, /revert puts it back.
What you need
- A provider API key or sign-in with a ChatGPT Plus/Pro subscription
- A terminal for setup and approvals
- Linux and Windows; macOS without guarantees
Running from source requires Python 3.11+: clone the repository, install requirements.txt in a venv, and run main.py --workspace <path>. See the repository README for details.