mistralai/mistral-vibe(opens in a new tab)
Language: Python
Minimal CLI coding agent by Mistral
- stars
- 5,255
- watchers
- 30
- forks
- 756
- open issues
- 440
- Last updated
- 10/30/2026
Language: Python
Minimal CLI coding agent by Mistral
██████████████████░░
██████████████████░░
████ ██████ ████░░
████ ██ ████░░
████ ████░░
████ ██ ██ ████░░
██ ██ ██░░
██████████████████░░
██████████████████░░
Mistral's open-source CLI coding assistant.
Mistral Vibe is a command-line coding assistant powered by Mistral's models. It provides a conversational interface to your codebase, allowing you to use natural language to explore, modify, and interact with your projects through a powerful set of tools.
Warning
Mistral Vibe works on Windows, but we officially support and target UNIX environments.
Linux and macOS
curl -LsSf https://mistral.ai/vibe/install.sh | bashWindows
First, install uv
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"Then, use uv command below.
uv tool install mistral-vibe
Vibe needs a GIL-enabled build of Python 3.12 or later. If you have a free-threaded (no-GIL) Python installed and the install fails, force the GIL build of Python 3.14:
uv python install cpython-3.14 uv tool install --python cpython-3.14+gil mistral-vibe
pip install mistral-vibe
read, write_file, edit).grep (with ripgrep support).todo list to track the agent's work.ask_user_question).task)./) and file paths (@).@ mentions — .png, .jpg, .jpeg, .gif, .webp files are sent to vision-capable models (e.g. Mistral Medium 3.5) as native multimodal content.config.toml file.Vibe comes with several built-in agent profiles, each designed for different use cases:
ask: Requires approval for tool executions.accept-edits: The default agent. Auto-approves file edits only (write_file, edit). Useful for code refactoring.auto-approve: Auto-approves all tool executions. Use with caution.Use the --agent flag to select a different agent:
vibe --agent ask
To change the default agent used when --agent is not passed, set
default_agent in your config.toml:
default_agent = "ask"
Valid values are ask, accept-edits, auto-approve,
lean (only when listed in installed_agents), or the name of any
custom agent file in ~/.vibe/agents/ or the project's .vibe/agents/
directory. Subagents such as explore are not accepted.
Note:
default_agentapplies in both interactive and programmatic (-p/--prompt) sessions. Pass--auto-approveor--yolowith any agent when a run should approve all tool calls without prompting.
Vibe supports subagents for delegating tasks. Subagents run independently and can perform specialized work without user interaction, preventing the context from being overloaded.
The task tool allows the agent to delegate work to subagents:
> Can you explore the codebase structure while I work on something else?
🤖 I'll use the task tool to delegate this to the explore subagent.
> task(task="Analyze the project structure and architecture", agent="explore")
Create custom subagents by adding agent_type = "subagent" to your agent configuration. Vibe comes with a built-in subagent called explore, a read-only subagent for codebase exploration and skill loading used internally for delegation.
The interactive prompt shows each known subagent's name, type, live status, and
latest measured context size. Opening a
subagent also shows its active status above the prompt and its context use in the
bottom-right gauge. With an empty or locally edited prompt, press Down from its
last line to focus the list;
unsent text stays in the prompt. Use Up and Down to highlight a row, then press
Enter to open it; with the mouse, hover to highlight and click to open. Select Main
conversation to return, or press Escape from a subagent view. Escape on
the focused list returns to the prompt with Main conversation selected. Ctrl+C adds a
local-only, error-styled user message explaining that subagents cannot be
controlled directly; return to Main and ask the main agent to stop one. Press
Ctrl+C again to quit Vibe. After
opening Main conversation, press Up from its row to focus the prompt. While a
subagent view is open, Up stops at the Main row. Idle subagents are ready:
the main agent can still send them more instructions. Waiting subagents outside
the running batch are hidden behind a last N subagents waiting row; while the
list is focused, that row offers a to show all subagents, and a again to
hide the waiting ones. Once shown, the list keeps its Main row for the rest of
the session; switching sessions hides the waiting subagents again. Subagents whose last turn failed remain listed as
failed. Hidden waiting subagents still count toward max_subagents. Explicitly stopped subagents
leave the list after you return to Main. The first subagent
update does not add a local information message. When a subagent becomes ready,
its transcript shows a local information message explaining how to give it a
new goal or stop it from Main. Set show_subagent_status_list = false in
config.toml, or change it through /config, to hide this UI.
The ask_user_question tool allows the agent to ask you clarifying questions during its work. This enables more interactive and collaborative workflows.
> Can you help me refactor this function?
🤖 I need to understand your requirements better before proceeding.
> ask_user_question(questions=[{
"question": "What's the main goal of this refactoring?",
"options": [
{"label": "Performance", "description": "Make it run faster"},
{"label": "Readability", "description": "Make it easier to understand"},
{"label": "Maintainability", "description": "Make it easier to modify"}
]
}])
The agent can ask multiple questions at once, displayed as tabs. Each question supports 2-4 options plus an automatic "Other" option for free text responses.
Vibe's interactive interface requires a modern terminal emulator. Recommended terminal emulators include:
Most modern terminals should work, but older or minimal terminal emulators may have display issues.
Navigate to your project's root directory:
cd /path/to/your/projectRun Vibe:
vibe
If this is your first time running Vibe, it will:
~/.vibe/.env for future useAlternatively, you can configure your API key separately using vibe --setup.
Start interacting with the agent!
> Can you find all instances of the word "TODO" in the project?
🤖 The user wants to find all instances of "TODO". The `grep` tool is perfect for this. I will use it to search the current directory.
> grep(pattern="TODO", path=".")
... (grep tool output) ...
🤖 I found the following "TODO" comments in your project.
Simply run vibe to enter the interactive chat loop. The terminal UI is a native
Rust binary (vibe-rs) bundled with the package.
Ctrl+J, or Shift+Enter / Alt+Enter in terminals that report them, to insert a newline.cursor_blink = false in config.toml for a steady cursor. Changes apply without restarting through /config or /reload.@ symbol for smart autocompletion (e.g., > Read the file @src/agent.py). A bare @ quickly lists immediate non-hidden entries; after a path character, Git workspaces suggest tracked and non-ignored files. Pasting a standalone existing absolute or home-relative file or folder also creates a mention.! to execute it directly in your shell, bypassing the agent (e.g., > !ls -l).Ctrl+G to edit your current input in an external editor.Ctrl+O to toggle the tool output view.Ctrl+T to toggle the todo list view.Ctrl+\ to toggle the debug console.Shift+Tab to cycle through agents (ask, accept-edits, ...).Enter or Ctrl+Enter atomically consumes the stored queue item and steers it into the active turn. Ctrl+C removes the newest queued prompt. Escape interrupts the active turn and pauses remaining prompts, and Enter resumes a paused queue. Shell commands and non-side-channel slash commands require an idle session./exit, exit, quit, :q, or :quit in the input box, or press Ctrl+C / Ctrl+D twice within ~1 second. Set ask_confirmation_on_exit = false (or toggle it in /config) to make Ctrl+D quit on the first press; Ctrl+C always requires confirmation.Ctrl+Y or Ctrl+Shift+C to copy the current selection to clipboard. With autocopy enabled (default via autocopy_to_clipboard = true), mouse selection automatically copies on release and shows a brief confirmation.wl-copy, xclip, or xsel. If pasting fails, install xclip (or wl-clipboard), or hold Shift while selecting to use your terminal's native selection. Over SSH, Vibe relies on OSC 52 alone, so your terminal must support it.You can start Vibe with a prompt using the following command:
vibe "Refactor the main function in cli/main.py to be more modular."Vibe includes a trust folder system to ensure you only run the agent in directories you trust. When you first run Vibe in a new directory which contains a .vibe subfolder, it may ask you to confirm whether you trust the folder.
Trusted folders are remembered for future sessions. You can manage trusted folders through its configuration file ~/.vibe/trusted_folders.toml. In the trust prompt, use Up/Down or the mouse wheel to scroll the detected-file list. Text is selectable there too: double-click selects a word, triple-click selects a paragraph, and dragging near the list edges scrolls while extending the selection.
This safety feature helps prevent accidental execution in sensitive directories.
You can run Vibe non-interactively by piping input or using the --prompt flag. This is useful for scripting.
vibe --prompt "Refactor the main function in cli/main.py to be more modular."By default, it uses your configured default_agent (accept-edits unless changed).
To approve all tool calls without prompting, pass --auto-approve or --yolo
(also available for interactive sessions):
vibe --prompt "Refactor the main function in cli/main.py to be more modular." --auto-approveWhen using --prompt, you can specify additional options:
--max-turns N: Limit the maximum number of assistant turns. The session will stop after N turns.
--max-price DOLLARS: Set a maximum cost limit in dollars for this run. The turn is interrupted once the cost exceeds this limit, and the outcome is price_limit.
--max-tokens N: Set a maximum cumulative LLM token budget for this run, counting both prompt and completion tokens. The turn is interrupted once usage exceeds this limit, and the outcome is token_limit.
--agent NAME: Select the agent profile for this run.
--model ALIAS: Use this model alias for the run instead of active_model, without saving it as the default. Only for a new session; an unknown alias is a usage error.
--auto-approve, --yolo: Approves all tool calls without prompting, including in interactive sessions. Can be combined with any --agent value.
--enabled-tools TOOL: Enable specific tools. In programmatic mode, this disables all other tools. Can be specified multiple times. Supports exact names, glob patterns (e.g., bash*), or regex with re: prefix (e.g., re:^serena_.*$).
--disabled-tools TOOL: Disable specific tools after --enabled-tools filtering. Can be specified multiple times. Supports exact names, glob patterns (e.g., bash*), or regex with re: prefix (e.g., re:^serena_.*$).
--output FORMAT: Set the output format. Options:
text (default): Human-readable text outputjson: All messages as JSON at the endstreaming: Newline-delimited JSON per messageOn a resumed session, text and streaming cover only the new run, so a run that ends without a reply of its own prints none; json holds the whole session.
--prompt-file PATH: Read the prompt from a file instead of --prompt. With -p and no text, the prompt is read from stdin.
--system-prompt-file PATH: Replace Vibe's own instructions in the system prompt, and the agent's, with the file's text. The harness's general guidance still comes first, and the project context, AGENTS.md files and tool instructions still follow it. An empty or unreadable file is a usage error.
--time-limit SECONDS: Stop the run after this many seconds. The active turn and its tool processes are stopped, and the session can be resumed with --resume. SIGTERM stops a run the same way.
--output-dir DIR: Write DIR/export.json and copy the session journal to DIR/session/, whichever way the run ends. The export records the Vibe version, the effective config (header and environment values redacted), token usage, cost, the number of model calls the agent made (steps), the stop reason and the outcome. When the session had config issues, such as a skill or a config file it could not load, the export lists them under warnings, each as {"message": str, "source": str | null}, where source names the file or skill it is about; the field is left out when there are none, and the run goes on regardless. Usage, cost and steps count this run only, also on a resumed session, while the copied journal holds the whole session. Only an argument the parser itself rejects (an unknown flag, a malformed value) exits 1 without an export.
Example:
vibe --prompt "Analyze the codebase" --max-turns 5 --max-price 1.0 --max-tokens 50000 --output json
vibe --prompt-file task.md --auto-approve --time-limit 1800 --output-dir out/| Code | Outcome in export.json |
Meaning |
|---|---|---|
0 |
finished |
The agent finished. |
1 |
usage_error, config_error |
The run could not start as asked. |
2 |
infrastructure_failure |
The model API (after retries) or the runtime failed. |
3 |
turn_limit, token_limit, price_limit, deadline, terminated, length, refusal |
The agent stopped without finishing; score what it left. length means the conversation or the last answer hit a model token limit: the context window, or the output-token cap (agent_max_output_tokens, else the model's max_output_tokens where the provider requires a cap). An answer cut off at the output cap is first discarded and asked for again, up to output_limit_retries times (6 by default). refusal means the model declined to answer. |
4 |
aborted |
The --agent-socket server aborted the run (experimental, see below); error.message in export.json is the server's message. |
vibe -p exits only after its app-server process has, however the run ends.
Killed outright (SIGKILL, the OOM killer), it takes its app-server with it: at
once on Linux and Windows, and on macOS as soon as the app-server sees its
input close, normally within a second. vibe run-server's app-servers do the
same.
Experimental: the option is hidden from --help, and it and the protocol below
may change or go away without notice.
vibe -p --agent-socket PATH runs the same session as vibe -p on the host,
with the same export, exit codes, limits and SIGTERM handling, connected to an
agent server on the Unix socket PATH. The server serves two independent
things, either or both:
modelAccess decides whether the model calls it directly, from
run_typescript as tools.<namespace>.<name>, or both. --enabled-tools and
--disabled-tools filter only Vibe's own tools. Without a sandbox, the
session runs on this host as plain vibe -p does.The option needs -p or --prompt-file, and cannot be combined with
--workdir, --worktree, --add-dir, --continue or --teleport; those are
usage errors (exit 1). It is not supported on Windows (exit 1).
To continue a finished run with a new prompt, start a new run with
--resume SESSION_ID, taking the ID from session_id in the first run's
export.json. The new run redoes the handshake, so the server may be a new one
and declares its tools again; the model sees the whole earlier conversation
followed by the new prompt. Every limit (--max-turns, --time-limit,
--max-tokens, --max-price) counts from the new run's start. Its export, under
the same session_id, counts only its own steps, usage and cost_usd; its
journal_dir holds the whole session, earlier runs included.
When the server serves a sandbox, the workspace context (git metadata and
AGENTS.md files) is read from the sandbox. Session storage, model calls, MCP
servers and hooks stay on this host. Background processes run in the sandbox,
which must be POSIX for them, as enable_background_processes says. Rewinding
the session restores no files, since the files are in the sandbox. If the
sandbox fails, the run ends as infrastructure_failure.
Vibe's tool helper can crash in a sandbox that works: it exits part way through
a tool call without answering, say from a segmentation fault or an
out-of-memory kill. The model then reads a tool error saying the command may or
may not have run, with the exit code and what the helper printed, and the turn
goes on. The third crash in a row ends the run as infrastructure_failure; a
tool call the helper answers, even with an error, resets the count. Sessions
sharing the sandbox, such as subagents, share the count. A helper that cannot
start at all (no interpreter at python) ends the run at once, as does a crash
outside the model's tool calls, such as while copying skills into the sandbox
or saving a large output there.
As on the host, when the model reads a file, the AGENTS.md files in the
directories between it and the workspace are added to what it reads, once per
session. Looking for them costs a sandbox/readFile request per directory, so
a session looks in each directory once. When Vibe's own write_file or
search_replace writes an AGENTS.md, its directory is looked in again on the
next read below it; a bash command whose text names AGENTS.md makes every
directory be looked in again. An AGENTS.md that appears any other way, for
example from a script or git checkout, in a directory already looked in is
not seen for the rest of the session.
File tools follow symlinks in the sandbox, as on the host. Approvals, though,
match a path as written, since this host cannot see the sandbox's links. So
without --auto-approve, approving a link also allows its target, and a link
in the workspace that points outside it counts as a workspace path, for example
for accept-edits. The target is still in the sandbox. Runs with
--auto-approve, such as evaluation runs, ask for no approval and are not
affected.
A tool output too large to hand the model whole is kept with the session on
this host, as with plain vibe -p, and also saved in an owner-only directory
under the sandbox's temporary directory. The model is given that sandbox path,
so it reads the output back with the sandboxed tools.
Only the user's Vibe files are loaded on this host, and --trust has no
effect. Of the project's own files, only the AGENTS.md files and the skills
(.vibe/skills and .agents/skills) are read from the sandbox. The rest of the
project's .vibe/ directory (config, agents, tools, hooks, plugins and
prompts) is ignored, whereas vibe -p --trust loads it: loading it would run
code the sandbox controls on this host, such as hooks, MCP servers and tools.
A run whose sandbox workspace has it logs a warning.
The model reads skills with the sandboxed tools, so the skills read on this
host (the user's, and the plugins', the built-in ones included) are copied into
the sandbox's temporary directory under mistralai-vibe-skills/<digest>/,
outside the workspace, and the model is given those paths. A copy is named by
the digest of its files, checked before it is reused, and uploaded only when
missing, so later runs on the same sandbox reuse it. A skill with more than 200
files, a file over 256 KiB, more than 1 MiB of files besides SKILL.md, or a
SKILL.md over 2 MiB is not copied, and the run goes without it. Each skill
left out, for that or because the copy failed, is logged and listed in the
session's config issues, and so in the export's warnings.
The caller creates the socket with mode 0600 inside a 0700 directory, and
serves it for the whole run. A Unix socket path is limited to 104 bytes on macOS
(108 on Linux), so keep it short, for example under /tmp.
For each request, Vibe opens a new connection, writes one line, reads one line
and closes the connection; concurrent requests use concurrent connections.
Answer them concurrently too: a request's timeout covers the time it waits
behind others, such as a background process's poll behind a long shell
command. A line is a compact JSON-RPC 2.0 message with camelCase fields, followed by \n.
Replies may carry fields Vibe does not know, which it ignores.
| Method | Params | Result |
|---|---|---|
agent/initialize |
{"protocolVersion": 2, "vibeVersion": str} |
{"protocolVersion": 2, "workspace"?: str, "python"?: str, "tools"?: [ToolDefinition], "toolTimeoutSeconds"?: float, "instructions"?: str} |
sandbox/execute |
{"command": str, "cwd": str, "timeout": float | null} |
{"exitCode": int, "stdout": str, "stderr": str} |
sandbox/readFile |
{"path": str, "maxBytes": int} |
{"contentBase64": str | null} |
tools/call |
{"callbackId": str, "name": str, "input": any, "toolCallId": str} |
{"output"?: any, "annotations"?: object, "error"?: {"message": str, "code"?: str, "details"?: any}} |
agent/initialize is called once, before the session starts, and must
answer within 60 seconds. The server must
answer with protocol version 2. workspace and python come together, or
not at all when the server serves no sandbox: workspace is the absolute
sandbox path the session starts in, and python the interpreter that runs
Vibe's tool helper (Python 3.9 or later). Each tool is
{"namespace"?: str, "name": str, "description"?: str, "inputSchema"?: object, "outputSchema"?: object, "modelAccess"?: "direct" | "programmatic" | "both"};
namespace defaults to client and modelAccess to programmatic. A
namespace cannot be vibe or ui, nor the name of an MCP server the session
has, and a <namespace>.<name> is declared once. toolTimeoutSeconds, a
positive number, is how long a tools/call may take; it defaults to 600.
instructions are project instructions for the session. The system prompt
carries them where it carries the project's AGENTS.md files, under the
same heading and with the same weight, followed by any AGENTS.md found in
the workspace. They are left out, like the AGENTS.md files, when
include_project_context is off. Without a socket, the same text goes in
an AGENTS.md at the root of the workspace, which vibe -p reads when the
directory is trusted (--trust).
If the handshake fails, the run ends as infrastructure_failure (exit 2);
if it declares a tool Vibe cannot offer, as usage_error (exit 1), before
the model is called.sandbox/execute runs command with sh in cwd, with the sandbox's own
environment. Vibe runs its tool helper with cwd / and passes the
session's working directory in the command itself, so a model that deletes
or moves that directory gets a tool error, as on the host. A non-zero exit
is a result, not an error. A command that
outlives timeout seconds is answered with the error code -32001: the
model reads it as a timed-out command, as on the host, and the turn goes
on. A server that stops a command sooner, at a limit of its own, says so in
the error's optional data, as {"timeoutSeconds": float}, and the model
is told that limit; without it, the model is told no limit. To stop a
command, the server kills the process it started (swerex kills its process
group); the processes the command started go with it. Vibe waits 30
seconds past timeout for the reply; if none comes, it closes the
connection and the sandbox has failed.sandbox/readFile returns at most maxBytes of a regular file, base64
encoded, or null when there is no such file.tools/call runs one call of a declared tool. callbackId is unique to the
call, name is <namespace>.<name>, and toolCallId the model's call it
answers. output
is the tool's result, and error makes the call a tool error. Either way the
model reads it and the turn goes on. So does an error reply, a malformed
reply, a lost connection, or no reply within toolTimeoutSeconds: each is a
tool error. A shorter --time-limit shortens the wait to match.For a sandbox request, any other error reply, a malformed reply, or a
connection that closes or cannot be opened means the sandbox failed: the turn
fails and the run ends as infrastructure_failure (exit 2). A tool helper
command that exits non-zero is a crash of the helper, not of the sandbox; see
Sandboxed sessions.
The server can end the run itself, from any request, by answering it with the
error code -32002 and a message: {"code": -32002, "message": str}. Vibe
stops the run as SIGTERM does and exits 4, with the outcome aborted and the
server's message as error.message in export.json. Vibe gives the abort no
meaning of its own; the message is for whoever reads the export.
Experimental: the command is hidden from --help, and it and its protocol may
change or go away without notice. It is not supported on Windows (exit 1).
vibe run-server serves many runs at once, each what
vibe -p --agent-socket PATH --output-dir DIR does, with the same outcomes,
exit codes, limits and export. Every run must use an agent socket that serves a
sandbox.
Runs share app-server processes rather than starting one each. An app-server
serves at most --runs-per-app-server K runs (default 16). A run goes to the
app-server with the most runs that still has room, so that a spare one drains;
when none has room, another starts. An app-server exits once its last run has
gone, and a later run starts another. One that exits early fails only its own runs, as
infrastructure_failure. One that has not let a run go 15 seconds after it
ended takes no new run.
It speaks JSON-RPC 2.0 on stdin and stdout, framed as the agent socket is: one
compact JSON message with camelCase fields per line. Only the protocol is
written to stdout. Errors are {"code": str, "message": str, "data": any},
where code is invalid_request, invalid_params, not_initialized,
not_found, conflict, method_not_found or internal_error; an
invalid_params error names the fields at fault, never their values.
--max-runs N caps the live runs.
| Method | Params | Result |
|---|---|---|
initialize |
{"protocolVersion": 1, "clientInfo": {"name": str, "version": str}} |
{"protocolVersion": 1, "serverInfo": {"name": "vibe-run-server", "version": str}, "maxRuns": int | null} |
run/start |
{"runId": str, "prompt": str, "agentSocket": str, "outputDir"?: str, "resumeSessionId"?: str, "agent"?: str, "autoApprove"?: bool, "enabledTools"?: [str], "disabledTools"?: [str], "maxTurns"?: int, "maxTokens"?: int, "maxPrice"?: float, "timeLimit"?: float, "config"?: object, "credentials"?: {str: str}} |
{} |
run/stop |
{"runId": str} |
{} |
ping |
{} |
{} |
initialize comes first, once. Any other protocol version is refused with
invalid_request, whose data is {"protocolVersion": 1}; any other
request before it, with not_initialized.run/start answers at once, and the r | |
| |
|