README
¶
vibe-coder
A local-first coding agent for Ollama, written in Go.
Ships as a single static binary named vibe — one-shot prompts, an interactive REPL,
a rich tool system, session persistence with compaction, and optional RAG, all without leaving your machine.
Contents
- Features
- Requirements
- Install
- Quick Start
- Model Configuration
- CLI Flags
- MCP & Skills Management CLI
- Slash Commands
- Vision
- RAG Usage
- Development
- Architecture Overview
- Contributing
- Security
- License
Features
Core agent
- One-shot prompts (
-p) and interactive REPL with streaming output. - Multi-turn agent loop (up to 50 iterations, 2 retries) with tool observation feedback.
- Native tool calling — registry schemas are sent as Ollama
tools; capability is detected from/api/tags, with a/api/showprobe when tag metadata is missing (Tools: native|xml|autoin/status), plus per-session 400 fallback and caching. Streamed calls are accumulated across chunks, and native turns replay structured history (assistant.tool_calls+ roletoolresults) instead of text envelopes. - Batched tool calls — up to 5 sequential calls per turn (native or
<invoke>XML fallback) for independent calls. - Empty-response recovery — retries with escalating guidance when the model returns an empty reply.
- XML fallback parser — kept for models without native function calling; native calls always win when present.
- Verify-after-write — after every
Write/Edit, the agent Reads the edited region and runs the relevant check before moving on. - Autonomous missions (agent-managed) — for work that outlasts a single reply, the agent can call
MissionStartto declare a mission. The runtime then keeps starting turns for it, with no turn limit, until the agent itself callsMissionComplete(goal done) orMissionBlocked(needs the user). The mission is task-agnostic and needs no user command to activate. - Durable work state — the mission, live TODO checklist, and task store are persisted in a session sidecar and restored on
--resume//session, so a crash or restart never loses what is done and what remains. A compact progress summary derived from that state is injected into the system prompt every turn, so compaction can never make the agent forget what is done or what remains. - Auditable run log — each autonomous turn appends one redacted JSONL record (turn, mission status, checklist counts, tools used, error) to
<state dir>/runs/<session>.jsonl, so a multi-hour run can be reviewed after the fact.
Autonomous missions
Some work does not fit in one reply: a batch of hundreds of items, a migration, or an iterative generate-and-check loop that runs for hours. For those, the agent itself decides to run a mission:
- It calls
MissionStartwith the user's goal and opens aTodoWritechecklist. - The runtime keeps starting new turns for it (
[mission] continuing autonomously) after each per-turn iteration cap — with no turn limit. - The agent ends the mission itself:
MissionCompletewith a summary when the goal is done, orMissionBlockedwith a reason when it needs the user.
You can initiate an autonomous mission directly with /goal <objective> (or
/mission start <goal>), or let the agent declare one itself via MissionStart.
When started with /goal, the agent follows a phased protocol: Phase 1 plans the
step-by-step approach and verification strategy via TodoWrite (clarifying any
ambiguities with AskUserQuestion), and Phase 2 executes the plan autonomously.
You can also inspect and end one from your side with /mission:
/mission shows the mission (goal, status, turns), /mission done [summary]
marks it complete (for example when you did the work yourself), and
/mission cancel [reason] stops it without completing. While a mission is
active the run is unattended: Ask and
Network tools are auto-approved so nothing blocks waiting for the user, while
mandatory dangerous-command confirmations are denied (never prompted) so the
agent gets a denial and can adapt or call MissionBlocked. Empty responses are
retried automatically and, if they persist, or if several turns run no tools at
all, the mission is paused instead of spinning. The mission, checklist, and tasks
are written to disk after every turn, so --resume continues an interrupted
mission exactly where it stopped and tells you a mission is waiting. /status
shows the current mission, and every turn is appended to the redacted run log at
<state dir>/runs/<session>.jsonl for auditing.
Tools (exposed to the model)
- File ops:
Read,ReadMany,Write,Edit(with inline unified-diff preview in the TUI). - Search:
Glob,Grep(multiline, context lines, output modes). - Shell:
BashandInteractiveBashwith dangerous-command blocklists and protected-path guards. - Web:
WebFetchandWebSearch(DuckDuckGo scrape) with SSRF protection: private hosts are rejected up front, every redirect target is re-validated (max 5 hops), connections are pinned to publicly resolved addresses (DNS-rebinding guard), and requests respect cancellation. - HTTP:
HTTPRequest— a genericmethod/url/headers/bodyrequest tool for REST/JSON APIs, including local services (it allows localhost, unlikeWebFetch). Network-tier, domain-agnostic, and the reliable alternative to shelling out tocurl. - Notebook:
NotebookEditfor.ipynbJSON round-trip. - Tasks:
TodoWrite(live to-do panel),TaskStart,TaskList,TaskComplete,TaskCancel. - Missions:
MissionStart(declare a long-running goal and hand the runtime control),MissionComplete(goal done),MissionBlocked(needs the user). - Questions:
AskUserQuestionfor interactive multi-choice prompts. - Orchestration:
SubAgent(bounded ReAct loop over Read/Glob/Grep, opt-in writes) andParallelAgents(explicit model calls only). - Git:
GitStatus,GitDiff,GitUndo(restore the latest vibe-coder checkpoint). - All file-search tools skip heavy directories (
.git,node_modules,vendor, etc.) and respect cancellation.
Session management
- Atomic persistence — append-only JSONL with temp-file + rename (
0o600). - Project-aware indexing & auto-resume — sessions are keyed by
sha256(cwd)[:16]so runningvibeautomatically resumes where you left off in that directory (tagged as(resumed)in the banner). Use/newor-n/--newto start fresh. - Temporal (ephemeral) sessions — run with
-t,--temporal, or--temp. Nothing is written anywhere: the chat transcript, the metrics, the log, the mission run log, and the user-level directories are all skipped, so the run leaves no trace (tagged as(temporal)in red in the banner). Files and code modifications made by the agent remain intact, settings still go to the shared config file, and the RAG index is kept in memory. Running/promotewithin a temporal session keeps it by promoting it to permanent. - Isolated sessions — run with
--isolatedor--isolate(or configured viaVIBE_CODER_ISOLATED=trueorISOLATED=true). The run keeps the records it saves inside the current folder:.vibe-isolated.jsonl(transcript),.vibe-isolated-history.txt(prompt history),.vibe-isolated-media/, and.vibe-isolated-rag.db. It records no metrics and no log, creates no user-level state directory, and other directories cannot access it; the settings (models, providers,SIDECAR_DISABLED, remembered tool permissions) still go to the shared config file, so they apply to every mode. Runningvibein a directory containing an isolated session automatically detects and loads it (tagged as(isolated)in yellow in the banner). Running/newresets the isolated session — the file keeps only its marker line, so the folder keeps detecting the mode — and/sessions deleteonly reaches the folder-local session./savewrites the transcript into the folder and reports that the session stays isolated there. - Compaction — triggered at 300 messages or 70 % of context window; a sidecar model (falling back to the primary model when unconfigured) summarizes the oldest messages while keeping the last 30 verbatim.
- Token estimate — maintained incrementally so compaction checks are O(1).
Safety and permissions
- Three permission tiers: Safe (allow unless denied), Ask, and Network (prompt unless approved by a remembered rule or
-y). Persistent denials apply to safe tools too. - The prompt has a clock: the permission card prints a
⏳ 3:00badge on its input line (▸ ⏳ 3:00 Select option (1-7) [default: deny]:) and counts down while it waits. When the clock reaches zero the action is denied as if the user had pickedDeny once— no session rule is recorded, nothing is executed, and the model is told the prompt timed out so it keeps working on the rest of the task instead of retrying. Configure it withVIBE_USER_TIMEOUT(aliasVIBE_CODER_PERMISSION_TIMEOUT),PERMISSION_TIMEOUTor--permission-timeout(Go duration, default3m;offwaits forever), or for the session with/settimeout 5mplus/saveto keep it. The countdown appears only when both stdin and stdout are terminals; a piped run keeps the old blocking behavior. - Command gravity — every shell command is classified before it runs, so the permission card shows its weight instead of reading every command alike.
READcommands only inspect (a pipeline made of readers whose paths all resolve inside the workspace, with2>/dev/nullallowed and no output redirection), so they run without a prompt and without spending a decision-model round trip.WRITEcreates or updates files.DESTRUCTIVEdeletes or overwrites data that already exists, includingcat a > bover a file that already holds content,cp a b/mv a bonto an existing file,>truncation versus>>append, and history-rewriting git commands.CATASTROPHICcannot be undone. The card printsGRAVITY <level>plus a one-line reason, and the level is handed to the decision model as runtime context; a model verdict that itself reportshighrisk is never auto-approved, however confident it claims to be. -ymode enables auto-approval except for catastrophic commands inBash,InteractiveBashandBackgroundRun. Remembered approvals, an approved session rule, and unattended missions cannot bypass them either (a mission denies them instead of hanging on a prompt).- In-session memory —
/yesand/notoggle at runtime; decisions are remembered for the current session. - Persistent permissions — saved as
TOOL_PERMISSIONSinvibe-coder.env(skipsBash:allow; legacypermissions.jsonis migrated). - Catastrophic commands are always confirmed:
rm -rf /,rm -rf ~/$HOME/*/./.., a recursive delete of a system root or of the workspace itself,sudo/doas/su,mkfs*,fdisk,dd of=/dev/*,shutdown/reboot/halt, a fork bomb,curl … | sh,> /dev/sd*, recursivechmod/chownon a system root,git push --force,git filter-branch, andnpm/cargo publish. Wrappers cannot launder them:nice sudo rm -rf /,bash -c 'rm -rf /'andxargs rmare judged by the command they run, never by the wrapper name. - Dangerous-command blocklist (
rm -rf /,> /dev/sda, etc.) and protected-path guard (/proc,/sys,~/.ssh/id_*,~/.aws/credentials, etc.). This is a second, independent layer applied at execution time: the permission gate decides whether to ask, the blocklist refuses outright what an agent has no legitimate reason to run. - Environment scrubbing — shell tools and auto-tests run with
safety.CleanEnv()to strip secrets. - Plan mode —
/planpermits read-only tools and session task management;Write/Editare restricted to<cwd>/.vibe-coder/plans/, without symlink paths. Shell commands, auto-tests,NotebookEdit,GitUndo, and unknown/MCP tool effects are blocked until act mode resumes. - Review mode —
/review <prompt>permits read-only tools and session task management, with no file mutations or shell commands. Unknown/MCP tool effects are blocked by default. - Offline mode —
--offlineflag or/offlinecommand blocks all network-tier tools (WebSearch,WebFetch,HTTPRequest) and external network shell commands (such ascurl,wget,git clone,npm install), while permitting loopback/local requests (localhost,127.0.0.1,::1). Shell commands are evaluated by the decision model when active or via regex heuristics. - Workspace domain boundary protection — the directory where
vibewas started and its subdirectories define the active workspace domain. Safe tools (Read,Glob,Grep,DescribeImage) execute prompt-free inside the domain, but reading files, inspecting images, or querying directories outside the domain requires explicit user consent via the permission card. Users can approve once, allow for the session (Allow this session), block for the session, or persist (Always allow/Always deny). User-provided files (such as/pasteclipboard images) remain authorized. A refusal (4,5,6) refuses only that action: the model receives the denial plus the instruction to work around it, and the run keeps going. OnlyCancel(7) aborts the run and returns to the prompt. - Delegated permissions — sub-agents inherit the parent's tool availability, permission checks, and active mode.
allow_writes=trueonly requests access; it does not grant approval. Child tool execution is serialized, including permission prompts, checkpoints, and auto-tests.
TUI
- Two UI modes:
plain(default, line-based) andrich(Bubble Tea + Lipgloss with pinned status bar, themed markdown, and syntax highlighting). Select with--ui rich. - To-do panel —
TodoWriterenders a live Cursor-style task list with status glyphs. - Inline diff renderer —
Edittool shows colored unified-diff (red/green/cyan) with 50-line truncation. - Type-ahead — keystrokes pressed mid-generation are captured and prefilled into the next prompt.
- Multiline input — dedicated keybinding starts multi-line mode; plain Enter submits.
- Terminal restoration — signal handler ensures raw mode is never left behind on Ctrl+C, ESC, or panic.
Integrations
- RAG (optional,
-tags rag) — SQLite-backed indexing with incremental (content-hash) re-indexing and hybrid retrieval: lexical token overlap fused with embedding cosine similarity (reciprocal rank fusion) when theembeddingrole is bound, lexical-only otherwise. Build with--rag-index, query with--rag. - MCP — stdio JSON-RPC client that discovers external tools and wraps them as
mcp_<server>_<name>. Servers start independently and best effort: a broken one is reported and costs only itself./mcp(or the startup block) shows each server's scope, state and tool count,/mcp toolslists the wrapped tool names, calls are bounded by a per-call timeout, and output respects the shared tool output cap. - Agent Skills on demand — discovers
SKILL.mdskill folders and flat.mdfiles under project, user, andSKILLS_DIRSroots; the prompt carries a name+description catalog and theSkilltool loads the full content, with read-only access to bundled files. - File checkpoint + auto-test — pre-Edit/Write copies leave the working tree, index, and stashes untouched. A successful changed file becomes a completed checkpoint; failed or unchanged edits do not replace usable undo history. Related tests request shell execution permission, and failures or denied execution are re-injected as
[AUTO-TEST]observations. - Language server diagnostics — after a successful
Edit/Writethe file's language server (gopls, typescript-language-server, rust-analyzer,ruff server/pylsp) is asked for a report, and the result lands as a[DIAGNOSTICS]observation. A fresh error skips the auto-test for that edit: the server answered in milliseconds what the suite would take a minute to say. The read-onlyDiagnosticstool exposes the same report on demand, and/statusshows which servers resolved. - File watcher — external file changes appear as
[System Note] N file change(s) detectedon the next iteration.
Requirements
- Go
1.26+(seego.mod) - A running Ollama instance for model-backed execution
Install
Pre-built binaries (recommended)
Download the archive for your OS and architecture from the
GitHub Releases
page, extract it, and move the binary to a directory in your PATH.
| OS | Architecture | Asset |
|---|---|---|
| Windows | amd64 | vibe_<version>_windows_amd64.zip |
| Linux | amd64 | vibe_<version>_linux_amd64.zip |
| Linux | arm64 | vibe_<version>_linux_arm64.zip |
| macOS | amd64 | vibe_<version>_darwin_amd64.zip |
| macOS | arm64 | vibe_<version>_darwin_arm64.zip |
Windows PowerShell example:
# Download the latest release (replace <version> with the release tag, e.g. v0.1.0)
$version = "<version>"
$url = "https://github.com/jonathanhecl/vibe-coder/releases/download/${version}/vibe_${version}_windows_amd64.zip"
Invoke-WebRequest -Uri $url -OutFile vibe.zip
Expand-Archive -Path vibe.zip -DestinationPath "$env:LOCALAPPDATA\Programs\vibe" -Force
# Add to PATH, e.g. via Environment Variables settings or:
$env:Path += ";$env:LOCALAPPDATA\Programs\vibe"
Linux / macOS example:
# Replace <version> and <os>_<arch> with the desired release and platform
version="<version>"
asset="vibe_${version}_linux_amd64.zip"
curl -LO "https://github.com/jonathanhecl/vibe-coder/releases/download/${version}/${asset}"
unzip "${asset}"
sudo mv vibe /usr/local/bin/
Install with Go
If you have Go installed:
go install github.com/jonathanhecl/vibe-coder/cmd/vibe@latest
Make sure your GOBIN or GOPATH/bin is in PATH.
Install a development build
From a local clone, use the dev install scripts to build and install with the current Git metadata:
Linux / macOS:
./install-dev.sh
Windows:
.\install-dev.ps1
Build from source
go build -o vibe ./cmd/vibe
Windows:
go build -o vibe.exe ./cmd/vibe
Verify the install:
vibe --version
Build
go build -o vibe ./cmd/vibe
Windows:
go build -o vibe.exe ./cmd/vibe
Helper scripts:
./run.sh # build + run with forwarded flags
./release.sh # cross-compile archives, create a Git tag, and publish a GitHub Release
.\run.ps1
.\release.ps1
On Linux/macOS, publish a release with:
GITHUB_TOKEN=<token> ./release.sh v1.0.5
release.sh verifies that the binary compiles cleanly (or runs the test suite if --run-tests is provided), builds all platform
archives into dist/, writes dist/checksums.txt, then creates the Git tag and
the GitHub Release and uploads the assets. --yes skips the confirmation prompt
for non-interactive use. It needs a token with repo scope in GITHUB_TOKEN
(or GH_TOKEN, or an authenticated gh CLI); a zip tool (zip, 7z, bsdtar, or
python3) is used for packaging.
The release workflow produces platform archives in dist/, then uploads them as assets to the GitHub Release for the requested tag.
Quick Start
One-shot prompt:
./vibe -p "Summarize this repository"
Interactive mode:
./vibe
Use a specific model and host:
./vibe --model llama3.1:8b --ollama-host http://127.0.0.1:11434
Send an initial prompt and keep chatting:
./vibe -p "Refactor main.go" -i
Model Configuration
Model settings are loaded with this precedence:
- defaults
- config file
- environment variables
- CLI flags (highest priority)
Default config file path:
- Windows:
%LOCALAPPDATA%\vibe-coder\vibe-coder.env - Linux/macOS:
~/.config/vibe-coder/vibe-coder.env
You can override the config file path with:
VIBE_CODER_CONFIG=<path>
Model keys and overrides:
- Config file key:
MODEL=<model-name> - Config file key:
UI=plain|rich - Config file key:
SIDECAR_MODEL=<model-name> - Config file key:
DECISION_MODEL=<model-name> - Config file key:
DECISION_ENDPOINT=<url> - Config file key:
DECISION_API_KEY=<token> - Config file key:
ASSISTED_YES=true|false - Config file key:
THINK=off|low|medium|high|max - Config file key:
PROVIDER_<NAME>_ENDPOINT=<url>,PROVIDER_<NAME>_KIND=ollama|openai,PROVIDER_<NAME>_API_KEY=<token>— named provider registry (see Providers & roles) - Config file key:
ROLE_<ROLE>_PROVIDER=<name>,ROLE_<ROLE>_MODEL=<model-name>with<ROLE>inMAIN|SIDECAR|DECISION|EMBEDDING|VISION— role bindings - Config file key:
KEEP_ALIVE_MAIN=<seconds>,KEEP_ALIVE_WORKER=<seconds>— how long a role's model stays loaded after a request:-1pins it forever,0uses the provider default,N>0unloads it afterNidle seconds (defaults: main-1, workers300) - Environment:
VIBE_CODER_MODEL=<model-name> - Environment:
VIBE_CODER_UI=plain|rich - Environment:
VIBE_CODER_SIDECAR_MODEL=<model-name> - Environment:
VIBE_CODER_DECISION_MODEL=<model-name> - Environment:
VIBE_CODER_DECISION_ENDPOINT=<url> - Environment:
VIBE_CODER_DECISION_API_KEY=<token> - Environment:
VIBE_CODER_ASSISTED_YES=true|false - Environment:
VIBE_CODER_THINK=off|low|medium|high|max - Environment:
VIBE_CODER_KEEP_ALIVE_MAIN=<seconds>,VIBE_CODER_KEEP_ALIVE_WORKER=<seconds>— per-role model keep_alive (-1pins,0provider default,Nunload after N idle seconds) - Environment:
VIBE_CODER_TEMPORAL=true|false(aliases:VIBEGO_TEMPORAL,TEMPORAL) - Environment:
VIBE_CODER_ISOLATED=true|false(aliases:VIBEGO_ISOLATED,ISOLATED) - Config file key / environment:
CHAT_TIMEOUT/VIBE_CODER_CHAT_TIMEOUT(Go duration, e.g.30m; default15m) — deadline for a single/api/chatturn, useful for slow local models in long missions - Config file key / environment:
PERMISSION_TIMEOUT/VIBE_USER_TIMEOUT(aliasVIBE_CODER_PERMISSION_TIMEOUT; Go duration, e.g.90s; default3m,offwaits forever) — how long any prompt that waits on the user runs before deciding for them: a permission prompt denies the action, anAskUserQuestioncard takes its default - CLI:
--permission-timeout <duration|off>— same bound from the command line - CLI:
--ui plain|rich - CLI:
--model <model-name>(or-m <model-name>) - CLI:
--sidecar <model-name> - CLI:
--decision-model <model-name>— Ollama model with thedecisioncapability - CLI:
--decision-endpoint <url>— custom System One server (e.g.http://192.168.0.33:8765) - CLI:
--assisted-yes(auto-approve safe commands via the decision model) - CLI:
--think <level>(off|low|medium|high|max; explicit levels need a thinking-capable model) - CLI:
--keep-alive-main <seconds>,--keep-alive-worker <seconds>— per-role model keep_alive (-1pins forever,0provider default,Nunload after N idle seconds)
If no model is set, vibe auto-selects one based on detected RAM tier.
Recommended model:
ornith:9bworks very well with Ollama for coding and multi-turn tool conversations invibe../vibe --model ornith:9b --ollama-host http://127.0.0.1:11434
What is the sidecar model for?
MODEL is the conversational/coding model that answers every prompt. The
sidecar is a smaller, faster model vibe uses internally for
short, high-leverage tasks the main model would either bloat the context
with or answer too slowly. All sidecar calls are guarded by a worker
semaphore, request deduplication (singleflight) and a small LRU cache,
so even on a single local Ollama instance you never see N parallel
requests piling up.
The sidecar is invoked in three places today:
- Session compaction — when the session has more than 300 messages
or the incremental token estimate exceeds 70% of
ContextWindow,Session.Compact()sends the oldest messages to the sidecar with a "Summarize the conversation concisely" prompt and replaces them with the summary. The last 30 messages are kept verbatim. - Tool-output condensation — when a tool (typically
Read,Bash,Grep) returns more than ~6 KB, the output is sent to the sidecar with a strict "produce 4-10 bullets, preserve paths/symbols/errors, no prose" system prompt. The condensed bullets replace the raw bytes in the model's context. - Path disambiguation — when the agent rescues a relative path
(e.g.
Read("config.go")) and finds multiple known absolute candidates, the sidecar picks one based on the user's current goal.
Pick a sidecar that is fast and cheap (e.g. llama3.2:3b,
qwen3.5:4b, phi3:mini). Leave it empty to disable all three
behaviours: compaction will truncate to a static "Earlier conversation
truncated…" note, large tool outputs will be inserted verbatim into the
context, and ambiguous paths will not be rescued.
Fast model (rabbit)
RABBIT_MODEL / ROLE_RABBIT_MODEL (or use /setmodel <provider> rabbit <model>)
is an optional faster model used for read-only retrieval work such as
SubAgent searches and batched ReadMany lookups. It runs with read-only
tools and a tighter turn/byte budget, then hands the summary back to the
main model.
If the rabbit role is unset, or its provider is unreachable, retrieval falls
back to the sidecar and then to the main model. Pick something small and
fast (the tested target is lfm2.5-8b-a1b-uncensored style models);
rabbit is best-effort and falls back rather than failing the task.
Decision model
DECISION_MODEL / DECISION_ENDPOINT configure a specialized, text-only decision function (not a conversational chat model, vision model, or tool-calling model). The client speaks the System One protocol — POST <endpoint>/v1/systemone with typed questions (noul for yes/no, choice for categorical picks, score for ordinal scales) — and gets structured answers back with probabilities and confidence. It works against either backend:
- Native Ollama (>= 0.35): any Ollama model that advertises the
decisioncapability in/api/show. WhenDECISION_ENDPOINTis empty, the configuredDECISION_MODELis queried on the main Ollama host. - Custom System One server: a dedicated endpoint such as
jev-style servethat answers/healthzand/v1/systemonedirectly (setDECISION_ENDPOINT).
Example Ollama decision-capable models:
ollama pull nimble
ollama pull tev1
ollama pull clef-flash # also advertises the vision capability
You can test the configured decision model inside the interactive session with /decision test.
Decision workflows powered by the decision model
When configured, the decision model acts as a fast, discrete decision engine for key agent operations:
- Assisted command execution mode (
--assisted-yes,/yes assisted,/decision assisted on): Instead of either prompting on every action or blindly auto-approving everything, the decision model reviews every action that would otherwise prompt — shell commands, network tool calls (WebSearch/WebFetch/HTTPRequest), file mutations (Write/Edit/GitUndo), and unknown/MCP tools. Safe actions are auto-approved; destructive or risky ones prompt the user. Mandatory shell confirmations (sudo,rm -rf /, …) and persistent denials still apply.- Review notices: The terminal always reports what assisted mode did, so a permission prompt is never a mystery:
✓ <tool> approved by the decision model (assisted mode) (<latency>)when approved;⚠ <tool> flagged as risky by the decision model (<latency>) · asking for permissionwhen the model rejected it;⚠ <tool> cannot be classified by the decision model · asking for permission; and⚠ decision model unavailable · assisted mode disabled · asking for permissionwhen the model is missing or errors. - Startup banner status: When assisted mode is active, the startup banner highlights
(assisted enabled)in green alongside the configured decision model; when inactive, it displays(run '/yes assisted' to enable)in standard white. - Configuration persistence: Setting assisted mode can be saved across runs in
vibe-coder.env(ASSISTED_YES=true,/save,--save). - Absence and error fallback: If no decision model is configured or if it experiences network or inference errors, assisted mode automatically deactivates and safely reverts to standard user confirmation prompts (
AskPermission). - Setup suggestion: Configuring a decision model via
/setmodel decision <number|name>automatically suggests enabling assisted mode.
- Review notices: The terminal always reports what assisted mode did, so a permission prompt is never a mystery:
- Workspace path disambiguation: When the agent references an ambiguous file basename matched across multiple repository paths, the decision model selects the best matching candidate according to user context and intent.
- Autonomous mission completion verification:
Monitors active autonomous missions and checks whether the goal has been fully fulfilled when pending tasks complete or turn activity idles, preventing unnecessary spin or stall. When the decision model reports the goal as not yet complete, the runtime feeds that verdict back to the model as an explicit reminder (do not call
MissionCompleteyet; address the remaining checklist) instead of silently continuing. This works with any configured decision model and does not require assisted permission mode (--assisted-yes). - Tool failure classification & diagnosis: When tool executions fail (e.g. bash commands or test failures), the decision model diagnoses the primary cause (compile/syntax error, missing dependency, failing test assertion, permission issue, or network timeout) and injects diagnostic hints for recovery.
- Conventional commit classification:
Analyzes git diff summaries during
/committo categorize changes into standard conventional commit types (feat,fix,refactor,test,docs,chore). - Thinking loop detection: During long reasoning turns, the decision model checks whether the model is repeating itself and cancels cyclical thinking.
- Agent tool (
Decide): Exposes the decision model directly as an agent tool to the main coding model and sidecars. When the agent needs an extra review, second opinion, sanity check, or discrete classification between options, it callsDecidewith context state, a decision question, and candidate options.
Configuration
# Native Ollama decision model (checks the "decision" capability at startup)
./vibe --decision-model tev1:0.8b
# Custom System One endpoint
./vibe --decision-endpoint http://192.168.0.33:8765
# Via environment
export VIBE_CODER_DECISION_MODEL=tev1:0.8b
export VIBE_CODER_DECISION_ENDPOINT=http://192.168.0.33:8765
export VIBE_CODER_DECISION_API_KEY=<bearer token> # optional
# Via config file (vibe-coder.env)
DECISION_MODEL=tev1:0.8b
DECISION_ENDPOINT=http://192.168.0.33:8765
DECISION_API_KEY=<bearer token>
# Interactive (inside vibe)
/setmodel decision tev1:0.8b # or /setmodel decision 3 by list number
/provider systemone endpoint http://192.168.0.33:8765
/setmodel systemone decision tev1:0.8b
/decision test
An endpoint that is not the Ollama host needs its own provider: add it with /provider add systemone http://192.168.0.33:8765 and bind the role with /setmodel systemone decision <model>. To go back to the Ollama host, bind /setmodel decision <number|name> with no provider argument, or unbind with /setmodel decision off.
At startup vibe runs a backend check: a GET /healthz that returns {"status":"ok"} means a System One server; a 404 means Ollama, where /api/show must report the decision capability for the configured model. When DECISION_ENDPOINT is set it is used directly; otherwise DECISION_MODEL is queried on the Ollama host.
Question types:
| Type | Description | Response |
|---|---|---|
noul |
Yes/no question | {"noul": 0.0-1.0} (P(true)) |
choice |
Multiple choice | {"choice": "option", "probabilities": {...}} |
score |
Ordinal scale (2-10 levels) | {"score": 0.0-(K-1), "legend": {...}} |
Enhanced decisions:
- Danger assessment: Uses score scale
["safe", "low risk", "medium risk", "high risk", "critical"]instead of binary yes/no - Goal completion: Uses score scale
["not started", "in progress", "mostly done", "completed"]for nuanced progress tracking - Safety decisions: Assisted mode asks a paired
actionchoice (allow/review/block) andriskscore, auto-approving only high-confidenceallowverdicts - Classification confidence: Low-confidence results are marked with
(uncertain)suffix
Live integration tests (require a running backend):
DECISION_LIVE_OLLAMA=http://localhost:11434 DECISION_LIVE_MODEL=tev1:0.8b go test -tags live -v -run Live ./internal/decision/
DECISION_LIVE_ENDPOINT=http://localhost:8765 go test -tags live -v -run Live ./internal/decision/
Migration note: the old JEVSTYLE_* settings were removed. JEVSTYLE_V3_ENDPOINT becomes DECISION_ENDPOINT; JEVSTYLE_MODEL is gone — configure an Ollama model that advertises the decision capability via DECISION_MODEL instead. Legacy JEVSTYLE_* keys still present in vibe-coder.env or the environment produce a startup warning and are otherwise ignored.
Remote Ollama for vibe only
If Ollama runs on another machine in your network, you can configure vibe and persist
those settings in one command, without changing global environment variables:
.\vibe.exe -model "qwen3.5:9b" -sidecar "qwen3.5:4b" -ollama-host "http://192.168.1.50:11434" -save
What this does:
- Applies model, sidecar model, and host for the current run.
- Writes
ROLE_MAIN_MODEL,ROLE_SIDECAR_MODEL, andPROVIDER_OLLAMA_ENDPOINTto%LOCALAPPDATA%\vibe-coder\vibe-coder.env. - Keeps the change scoped to
vibeonly (nosetxneeded).
Next runs can simply use:
.\vibe.exe
If you use PowerShell and want to run from source with the same flags:
.\run.ps1 -model "qwen3.5:9b" -sidecar "qwen3.5:4b" -ollama-host "http://192.168.1.50:11434" -save
OpenAI-compatible endpoints (llama.cpp, vLLM, LM Studio, etc.)
vibe natively supports OpenAI-compatible endpoints (/v1). Each provider has its own endpoint, and every role can bind to any provider. Any URL ending in /v1 (such as http://127.0.0.1:8080/v1) is automatically detected as an OpenAI-compatible endpoint.
Run with a custom endpoint via CLI:
vibe --endpoint http://127.0.0.1:8080/v1 --model my-model
Or configure via vibe-coder.env:
PROVIDER_OLLAMA_ENDPOINT=http://localhost:11434
PROVIDER_OPENAI_KIND=openai
PROVIDER_OPENAI_ENDPOINT=http://127.0.0.1:8080/v1
ROLE_MAIN_PROVIDER=openai
ROLE_MAIN_MODEL=my-model
# Optional: keep the sidecar on local Ollama
ROLE_SIDECAR_PROVIDER=ollama
ROLE_SIDECAR_MODEL=qwen3.5:4b
Legacy keys (MODEL, MODEL_ENDPOINT, SIDECAR_MODEL, SIDECAR_ENDPOINT, OLLAMA_HOST, …) are still read and migrated to this form on the next save.
CLI Flags
--version— print version and exit--help— show usage and exit--ui <mode>— UI mode (plainorrich)-p, --prompt <text>— one-shot prompt-i, --interactive— interactive mode (combine with-pto send an initial prompt and keep chatting)-m, --model <name>— model name--endpoint <url>— main model endpoint URL (Ollama or OpenAI-compatible/v1, auto-detected; alias:--model-endpoint)--provider <name>— main model provider (ollamaoropenai, auto-detected by default; alias:--model-provider)--sidecar <name>— sidecar model name--sidecar-endpoint <url>— sidecar model endpoint URL--sidecar-provider <name>— sidecar model provider (ollamaoropenai)--no-sidecar— disable sidecar for this session only; with--save, persistsSIDECAR_DISABLED=true--decision-model <name>— decision model name (Ollamadecision-capable model)--decision-endpoint <url>— decision model endpoint URL (custom System One server)-y, --yes— enable yes mode (auto-approve non-dangerous tools)--assisted-yes— enable assisted execution mode with the decision model (auto-approve safe commands, prompt for dangerous commands)--offline— enable offline mode (blocks network tools and external commands while permitting localhost)--lsp— force language-server diagnostics on for this run (defaultauto: active when a server resolves for the workspace)--no-lsp— disable language-server diagnostics for this run--lsp-servers <family:command,...>— override a server command per language family (go,python,typescript,rust); an override needs no workspace marker--lsp-timeout <ms>— how long to wait for the server's report on the version just sent (default5000, clamped250..60000)--no-lsp-after-edit— do not check diagnostics automatically after a written file (theDiagnosticstool stays available)--debug— enable debug logs-n, --new— start a new session (bypass automatic session resume for current directory)-t, --temporal, --temp— start a temporal session (discards chat transcript on exit; keeps agent code changes)--isolated, --isolate— run an isolated session saved exclusively in<cwd>/.vibe-isolated.jsonl(detected automatically when present in the directory)--resume— resume the last session for this project--session-id <id>— resume a specific session--list-sessions— list known sessions--ollama-host <url>— Ollama base URL--max-tokens <n>— max generated tokens--temperature <f>— sampling temperature--context-window <n>— model context window--no-think— disable Ollama native thinking (faster replies)--think <level>— thinking effort:off|low|medium|high|max(default: model default; persisted with--saveasTHINK)--hide-think— hide Ollama thinking blocks in CLI output--show-think— show Ollama thinking blocks in CLI output (overrides config/env)--context <file>— pin a.md/.txtguide file as a persistent session instruction (repeatable, accumulated; alsoVIBE_CODER_CONTEXTenv andCONTEXT=config key)--rag— enable RAG mode--rag-mode <type>— codebase RAG mode type;context/chat/hybridenable the experimental session-history strategy (lower compaction trigger, recall of demoted turns, needs an embedding model)--rag-path <path>— RAG database path--rag-topk <n>— RAG top-k chunks--rag-index <path>— build/index RAG path and exit--save— persistROLE_<ROLE>_PROVIDER/ROLE_<ROLE>_MODEL,PROVIDER_<NAME>_ENDPOINT/_KIND/_API_KEY,ASSISTED_YES,HIDE_THINK, andTHINKintovibe-coder.env
Harness tunables (defaults < config file < env):
KEEP_TOOL_RESULTS=<n>/VIBE_CODER_KEEP_TOOL_RESULTS— how many recent tool results stay unmasked in the transcript window (default6; values below1fall back to the default)TOOL_OUTPUT_MAX_TOKENS=<n>/VIBE_CODER_TOOL_OUTPUT_MAX_TOKENS— cap on a single tool result's token size (default0= auto; negative values normalize to0)COMPACT_AT_PERCENT=<n>/VIBE_CODER_COMPACT_AT_PERCENT— context-window fill percentage that triggers compaction (default85, clamped to50..95)METRICS_DISABLED=true|false/VIBE_CODER_METRICS_DISABLED— turn off JSONL metrics recording (the in-memory/statssummary still updates)
Interactive prompts share one budget. An AskUserQuestion card and a permission prompt both wait on the same human, so VIBE_USER_TIMEOUT (alias VIBE_CODER_PERMISSION_TIMEOUT), PERMISSION_TIMEOUT and --permission-timeout (default 3m, off waits forever) bound both:
- an
AskUserQuestioncard takes its default option when the budget runs out, and the answer line counts the wait down in a leading⏳ 2:43badge (▸ ⏳ 2:43 Select option (1-3) [default: 1]:), so the instruction never shifts while the clock runs; the clock only appears when both ends are terminals, because where it cannot be shown waiting beats taking a default nobody saw coming - ESC at the card also takes the default, and the default is always option 1: the tool tells the model to list its recommended option first, and an option marked
{"recommended":true}is moved to position 1 before the card is drawn, so a model that flags the best answer without reordering it still gets it as the default - a permission prompt denies the action on the same deadline, with the same countdown
Language servers (defaults < config file < env < CLI):
LSP_ENABLED=auto|on|off/VIBE_CODER_LSP_ENABLED— gate for diagnostics, both the automatic check and theDiagnosticstool (defaultauto: active when a server resolves for the workspace;onalso reports at startup when nothing resolved, so a silent session cannot read as a working one)LSP_SERVERS=<family:command,...>/VIBE_CODER_LSP_SERVERS— override a server command per family (go,python,typescript,rust). An override needs no workspace marker; what actually resolved is what/statusprints, so a typo shows up thereLSP_AFTER_EDIT=true|false/VIBE_CODER_LSP_AFTER_EDIT— check the edited file automatically afterEdit/Write(defaulttrue)LSP_TIMEOUT_MS=<n>/VIBE_CODER_LSP_TIMEOUT_MS— milliseconds to wait for the report on the version just sent (default5000, clamped to250..60000; a timeout is reported as "no answer", never as a clean file)
Per-request harness metrics (model-reported token counts, estimated prompt-section sizes, timings) are appended as JSONL to <state-dir>/metrics/<session-id>.jsonl; isolated and temporal sessions record nothing and keep the summary in memory only. Inspect them live with /stats.
Context management & measuring harness changes
vibe budgets the transcript in estimated tokens, not bytes. Each request splits CONTEXT_WINDOW into an output reserve (MAX_TOKENS, default reserve 4096, capped at window/4), the system prompt, the native tool schema, a 5% margin, and what remains is the transcript budget. Transcript selection is sticky: the window start only moves forward while the suffix stays within budget (recomputed windows fill up to 60% of the transcript budget), and it resets whenever the stored transcript is rewritten (compaction, /clear session, resume).
Large tool outputs older than the last KEEP_TOOL_RESULTS tool turns are masked on the wire only — the stored transcript is never mutated — replaced by a short [elided] stub. TOOL_OUTPUT_MAX_TOKENS caps a single tool result (auto = 15% of the context window, 2000-token floor, ≈4 bytes per token) across Read, Bash, InteractiveBash/SendInput, HTTPRequest, and WebFetch, including sub-agent tools.
Compaction fires when the masked transcript exceeds COMPACT_AT_PERCENT of the transcript budget (or history grows past 300 messages), keeps a 40%-of-budget token tail, summarizes the rest via the sidecar/main model, preserves the original first user request, and carries prior summaries forward so repeated compactions never lose them.
Experimental session-history mode (--rag-mode context, plus the context/chat/hybrid/session RAG_MODE env/config values) is optional and compacts earlier (50% trigger / 120-message guard) so older turns move into the in-memory store, while recall brings back only the relevant ones. It needs an embedding-capable client; otherwise it stays inert and compaction behaves as usual. The store is rebuilt on resume because it is intentionally in-memory.
Measuring a harness change:
go build -o vibe-metrics ./cmd/vibe-metrics
vibe-metrics "$HOME/.local/state/vibe-coder/metrics/<session>.jsonl"
vibe-metrics ~/.local/state/vibe-coder/metrics/ # whole directory
Each row reports requests, errors, total/average prompt and completion tokens, average system and transcript estimates, masked results, compactions, and wall-time p50/p95, plus a TOTAL row.
MCP & Skills Management CLI
vibe provides dedicated CLI subcommands to list, add, and remove MCP servers and custom skills.
MCP (Model Context Protocol)
Manage stdio-based JSON-RPC MCP servers in global or project-local configurations.
- List configured servers:
vibe mcp list - Add or update an MCP server:
# Adds a local server under .vibe-coder/mcp.json (default) vibe mcp add --env API_KEY=secret weather-server node path/to/server.js # Adds a global server under configDir/mcp.json vibe mcp add --global --env DEBUG=true logger-server python path/to/logger.py - Remove a server:
vibe mcp remove weather-server vibe mcp remove --global logger-server
Inside the session, /mcp prints the same status block the startup banner
prints — every configured server with its scope (project overrides global),
its state, and its tool count, or the reason it failed:
> /mcp
project
- weather-server: connected · 3 tools
global
- logger-server: failed · start mcp server logger-server: exec: "python": executable file not found in $PATH
/mcp tools lists the model-facing tool names each server exposes
(mcp_<server>_<tool>). Servers are started independently and best effort, so a
broken one never takes the others down; every model-facing call is bounded by a
per-call timeout and its output is capped like any built-in tool.
Skills
Skills are task-specific playbooks the agent loads on demand. Two formats are
supported: the standard Agent Skills layout (<root>/<skill-name>/SKILL.md
with YAML frontmatter name/description, plus optional bundled files like
references/ or scripts/) and legacy flat <root>/<name>.md files.
Discovery roots, in precedence order (the first root wins a name collision):
<cwd>/.vibe-coder/skills(project)<cwd>/skills(project)<configDir>/skills(user)- Every directory listed in
SKILLS_DIRS— set it to point at skill folders you already have installed, e.g. invibe-coder.env:
(also via envSKILLS_DIRS=~/.config/opencode/skills:~/.config/devin/skillsVIBE_CODER_SKILLS_DIRSor flag--skills-dirs; entries are separated by:on Unix,;on Windows, and~is expanded).
The system prompt carries only a catalog (name + description). When a task
matches a skill, the agent calls the Skill tool to load its full
instructions and the list of bundled files, which it can then open with
Read — files inside discovered skill roots are read-only accessible without
a permission prompt.
- List discovered skills (name, source, path, description):
Inside a session,vibe skill list/skillsshows the same list. - Configure
SKILLS_DIRSinteractively —/skills setdetects skill folders installed by other tools, in this order:~/.agents/skills(Agents shared),~/.claude/skills(Claude Code),~/.config/opencode/skills(OpenCode),~/.config/devin/skills(Devin) and~/.codex/skills(Codex). It prints them as numbered choices and replaces the currentSKILLS_DIRSwith your selection:
Choices take effect immediately; run/skills set # interactive picker (or prints usage without a TTY) /skills set 1,2 # pick options 1 and 2 /skills set all # pick every detected folder /skills set none # clear SKILLS_DIRS /skills set ~/my-skills # use a custom folder (mixable with numbers)/saveto persist them to the config file. - Add a new skill:
# Adds a local skill under .vibe-coder/skills/my-skill.md (default) vibe skill add my-skill path/to/source.md # Adds a global skill under configDir/skills/my-skill.md vibe skill add --global my-global-skill path/to/source.md
Slash Commands
Slash commands are entered at the > prompt during an interactive session.
Session
/save— persist model/thinking settings to disk (and the current session when it is permanent)/promote— keep a temporal session by promoting it to permanent and saving the transcript/new— save the current session and start a brand new one/clear— show clear options (session, sessions, context)/clear session— discard the current session without saving it and start fresh/clear sessions— delete ALL saved sessions (asks Y/n;--yesconfirms non-interactively)/clear context— unpin all persistent context files (same as/context clear)/sessions— list saved sessions with origin project path, modification time, and message preview (*= current project)/session <id>— resume a specific session quickly/session last— resume the most recently modified session/sessions delete <id>— delete a specific session/sessions delete --all— delete every saved session/resume— resume the last session for this project path/redo— re-print the last assistant response (rendered) to recover the context of what you were working on/compact— force a sidecar-summarized compaction/tokens— show token usage vs the context window (attached images count too)/status,/info— show the current configuration in a colored, banner-style summary (session, aModelssection with the provider and model bound to every role, UI, thinking, tools, hidden thinking, assisted state, yes/plan mode, offline mode, mission, context usage). Role labels match the/setmodelvocabulary (Main,Sidecar,Rabbit/Fast,Decision,Vision,Embedding) and the vision tag follows the same listing rule as the intro. Provider endpoints are left to/providers. TheLanguage serversrow reports which language servers resolved for this workspace, or why none did (off (LSP_ENABLED=off),none for this workspace), together with whether diagnostics run automatically after an edit and the wait timeout in milliseconds/stats— show session harness metrics (requests, provider-reported and estimated tokens, timings) plus the metrics JSONL file path/context <file.md|file.txt>— pin a guide file as a persistent session instruction (when files are already pinned, it asks[A]ppend / [R]eplace / [C]ancel)/context add <file...>— accumulate another guide file/context replace <file...>— drop all pinned files and pin these instead/context list— show pinned files/context drop <name|#|path>— unpin one file/context clear— unpin all files
Pinned context files are injected into the system prompt on every turn, so they stay alive for the whole session: compaction and transcript truncation can never drop them. The pinned list is saved with the session and restored on --resume//resume (file contents are re-read from disk). CLI --context flags accumulate: vibe --context guide.md --context rules.txt.
Providers & roles
A provider is a named, reusable endpoint (ollama by default). A role binds to a (provider, model) pair. The six roles are main, sidecar, rabbit, decision, vision and embedding.
/providers— list the configured providers, the model bound to each role under its provider, and anUnassigned rolesblock that says what each remaining role falls back to; models that can see images carry the cyanvisiontag/provider <NAME>— show one provider (kind, endpoint, API key state, roles)/provider add <NAME> [endpoint]— add a reusable provider (the endpoint is prompted when omitted; if you skip it, create it later with/provider <NAME> endpoint <url>)/provider <NAME> endpoint <url>— set a provider endpoint (creates the provider when it does not exist); when the provider needs an API key it is requested/provider <NAME> key <api-key>— store an API key (also prompted when omitted)/provider <NAME> kind <ollama|openai>— force the protocol; otherwise it is inferred from the endpoint (/v1→ openai)/provider <NAME> test— check reachability and count the models the provider reports/provider <NAME> remove— remove a provider (refused while a role is bound to it)/models [PROVIDER]— list that provider's models, numbered and tagged with colored capability tags (PROVIDERdefaults toollama)/setmodel [PROVIDER] <role> <number|name>— bind a role to a model by list number or by name (PROVIDERdefaults toollama, so/setmodel main 5works as before)/setmodel [PROVIDER] <role> off— unbind a role/setmodel— show the current role assignments and usage
Roles and the capability each one requires:
| Role | Required capability | Notes |
|---|---|---|
main |
completion | The only role required to operate |
sidecar |
completion | Compaction, summarization, image descriptions |
decision |
decision | System One decision backend |
embedding |
embedding | Stored and validated, pending of use |
vision |
vision | Optional; derived from main then sidecar when unbound |
Model validation uses the model's capability tag for Ollama models. For other providers the tag is trusted and shown as unverified, because the provider cannot report capabilities. At startup the models are re-checked and the result is reported.
Vision without a vision role: when no model is bound to vision, images are routed automatically: the main model describes them when it can see, otherwise the sidecar when it can, and when neither can the request is answered with an honest note instead of guessing. Bind /setmodel vision <number|name> to make a specific model the eyes.
One client per provider: when main and sidecar share a provider, a single client is reused instead of one per role.
These settings persist as PROVIDER_<NAME>_KIND, PROVIDER_<NAME>_ENDPOINT, PROVIDER_<NAME>_API_KEY, ROLE_<ROLE>_PROVIDER and ROLE_<ROLE>_MODEL. Legacy keys (MODEL, SIDECAR_MODEL, MODEL_ENDPOINT, MODEL_PROVIDER, SIDECAR_ENDPOINT, SIDECAR_PROVIDER, DECISION_MODEL, DECISION_ENDPOINT, OLLAMA_HOST) are read on load and migrated; on save they are replaced by the new keys, so an older binary will no longer find them.
Model
/models [PROVIDER]— list the provider's models, numbered and tagged/setmodel [PROVIDER] <role> <number|name>— bind a role to a model/think— show the thinking level and model capability/think off|low|medium|high|max|on— set thinking effort for this session (/savepersists it)/sidecar on|off— toggle the sidecar for this session/sidecar perm-on|perm-off— persist sidecar state tovibe-coder.env/settimeout— show how long a permission prompt waits before it auto-denies/settimeout 3m|90s|45|off— set the permission timeout (a bare number means seconds,offwaits until answered);/savepersists it/sidecar status— show current sidecar state/decision status— show the decision backend and assisted mode/decision test— run a connectivity and decision test/decision assisted on|off— toggle assisted command execution mode/hide-think— hide model thinking blocks in CLI output/show-think— show model thinking blocks in CLI output (default)
Mode
/yes— auto-approve subsequent permission prompts/yes assisted— enable assisted execution mode (auto-approve safe commands via the decision model)/no— require manual approval (default)/plan— enter plan mode (writes restricted to.vibe-coder/plans/)/plan <goal>— enter plan mode and immediately start planning that goal/code— exit plan mode and return to coding mode/approve— exit plan mode and resume act mode in the same chat/goal <objective>— start an autonomous mission (the agent plans step-by-step with verification criteria, asks clarifying questions, and executes autonomously)/mission— show the agent's active mission (goal, status, continuation turns)/mission start <goal>— alias for/goal <objective>/mission done [summary]— mark the active mission complete from your side (e.g. you did the work yourself)/mission cancel [reason]— stop the mission without completing it (pauses the autonomous run)/offline [on|off|status]— toggle or set offline mode; blocks network tools (WebSearch,WebFetch,HTTPRequest) and external shell commands (curl, wget, etc., verified via the decision model or regex heuristics) while keepinglocalhostaccessible
Git
/commit— stage + commit current changes (LLM-suggested message)
Clipboard
/paste <prompt>— read the system clipboard and send its content to the agent along with your prompt. Automatically detects content type:- Image (screenshot, copied picture): saved to a temp file and attached so vision-capable models can see it.
- Text (code, errors, URLs): injected inline between
--- clipboard ---delimiters. - Files (copied from Finder/Explorer): paths are listed so the agent can
ReadorBashthem. - Empty: a system note explains the clipboard is empty.
/image <prompt>— alias for/pastewith identical behavior.
Requires platform clipboard tools: pngpaste on macOS (brew install pngpaste), wl-paste/xclip on Linux, or PowerShell on Windows (built-in).
Misc
/mcp— show the MCP servers this session connected to, grouped by the scope their config came from (projectoverwritesglobal), each with its state and tool count or the reason it failed/mcp tools— list the wrapped tool names (mcp_<server>_<tool>) every connected server exposes/help— show the command reference/exit,/quit,/q,/bye— save and exit (Ctrl+D also exits)- Double-tap ESC — stop the running agent and return to the prompt
- Ctrl+C — cancel the current operation (press twice quickly to force-exit)
Built-in Tool Notes
The agent exposes tools to the model through the system prompt. Users normally do not call these directly, but their behavior affects speed and context usage:
Readacceptsstart_line,end_line,offset,limit, andmax_bytesfor partial file reads. Without those parameters it reads the full file with line numbers. On image files (.jpg,.png,.gif,.bmp) it attaches the picture to the conversation instead (vision-capable models only).ReadManyreads several known files concurrently and returns each under its own header. It acceptspaths(aliasfile_paths/files) plus optionalmax_bytes(per file) andtotal_bytes(combined cap).DescribeImagehas a vision-capable model look at an image file and answer aquestionabout it (second opinion or closer look; sidecar preferred). A focusedquestion(e.g. "Is the Save button visible?") gets an answer to only that; omitting it asks for a thorough general description.Screenshotcaptures a monitor or the active window and has a vision model answer aquestionabout it, so the agent can verify visual results of its own work. The capture is analyzed in memory and never saved to disk, the session, or the transcript. Params:target(screen/window),monitor(all/current/1-based number; screen only),question.Writecreates or overwrites a file; dangerous paths and protected directories are blocked.Editapplies a replacement; the TUI renders a colored unified-diff preview.Globacceptshead_limitto bound large file listings.Grepacceptshead_limit,offset,glob,output_mode,multiline,-i,-A,-B, and-C.Diagnosticsreports the language server's errors and warnings for one file as it is on disk:file_pathis required andseverity(error/warning/info/hint/all) filters the report to that level and worse. Read-only, so it works in plan/review mode and never asks permission. A clean file answers in one line, and when the server did not answer for the version just sent the result says so instead of reading as clean. Servers come from the workspace markers plusPATH(gopls, typescript-language-server, rust-analyzer,ruff server/pylsp); when none resolves the tool is not registered at all.Bashruns a shell command with cleaned environment and dangerous-pattern guards.InteractiveBashstarts a persistent terminal session for multi-step CLI workflows.WebFetchandWebSearchfetch web content with SSRF protection.HTTPRequestperforms a generic HTTP request (defaultGET) with optional headers/body and a bounded response size. It is what a mission should use to talk to an API such as ComfyUI instead of fragilecurlshelling.NotebookEditedits.ipynbcells by JSON round-trip.TodoWritemaintains a live task list that the TUI renders as a panel.MissionStart/MissionComplete/MissionBlockedlet the agent run a multi-turn mission autonomously. While a mission is active the runtime keeps starting turns after the per-turn iteration cap; only the agent ends it. The mission, checklist, and tasks are persisted with the session, so--resumecontinues an interrupted mission.AskUserQuestionpauses the agent to ask the user a multi-choice question.SubAgentandParallelAgentsspawn child agents with bounded fan-out.GlobandGrepskip heavy directories such as.git,node_modules,vendor,dist,build,target, and.vibe-coder.Read,Glob, andGreprespect cancellation, so ESC ESC or Ctrl+C can stop long file operations cleanly.
File checkpoints and undo
In Git repositories, Write and Edit save a private pre-edit copy under the worktree's Git directory (vibe-coder-checkpoints/, files created with mode 0600). Checkpoints support tracked, staged, untracked, ignored, and newly created files without changing the index or using git stash.
GitUndo restores the latest completed checkpoint for the repository only when the current file's contents and permissions still match the recorded post-edit state. Otherwise it reports a conflict and preserves both the file and checkpoint. Undoing a newly created file removes that file, not its parent directories. Existing files recover their original contents and permissions.
Checkpoint creation rejects paths outside the repository, Git metadata, symlink paths, and source files larger than 16 MiB. Post-edit files are limited to 32 MiB for checkpoint validation. Outside Git repositories, edits still work but no checkpoints are created. Pending copies from interrupted edits are retained for manual recovery and are not automatically restored. Stashes from older versions remain untouched and must be inspected manually; GitUndo no longer pops them.
Vision
vibe detects at startup whether the active model advertises vision
capability (/api/tags) and tells the agent about it in the system prompt,
so it knows whether attached images actually reach it. /status and the
startup banner mark the model with the vision tag, and /setmodel re-checks
on every switch. The intro only lists a Vision bullet when it adds something:
binding the vision role always shows it, while an unset one disappears once the
main or sidecar model can already see images.
With a vision-capable model (e.g. llava, qwen2-vl, moondream), ask
about pictures directly — Read on an image file attaches it:
./vibe --model llava
> review ./photos and organize them into subfolders by clothing color
Borrowed vision (main model without vision)
When the main model cannot see but the sidecar can, Read images still
work: each picture arrives as a sidecar-generated textual description
(Vision: via SIDECAR in the system prompt). The main model works with
that description as its borrowed eyes.
For a closer look, a second opinion, or a follow-up about specific
details, the model can call DescribeImage with a question — answered by
the sidecar when it sees, otherwise by the main model itself:
./vibe --model qwen3.5:9b --sidecar moondream
> read ./photos/jacket.png, then check the buttons closely
Rules and limits:
- Formats:
.jpg,.jpeg,.png,.gif,.bmp(.webpand others are rejected with a clear error — convert first). - Each file: max 15 MB on disk, downscaled to 1024 px (longest side) before sending, max 5 images per message.
- Images count toward compaction triggers and
/tokens(1500 tokens each, proxy value) and reserve transcript budget, so big photo tasks compact like long text ones. - The transcript stores a tiny
[image path=...]marker; bytes resolve at send time and are cached per session, so--resumere-reads them from disk. - The sidecar never receives images (summarization, condensation, and path disambiguation stay text-only).
Screenshots
The Screenshot tool captures the screen or the active window and sends the
pixels straight to the vision routing (same model order as DescribeImage).
The capture never becomes a file the model or the session can see: it is
encoded in memory, answered as text, and forgotten — no transcript marker,
no leftover image.
Backends per platform:
- Linux Wayland —
grimon Hyprland (hyprctlsupplies monitor/window geometry) and Sway (swaymsg);spectacleon KDE;gnome-screenshoton GNOME.spectacleandgnome-screenshotonly write files, so the capture goes through a private0600file in a fresh0700directory under$XDG_RUNTIME_DIR(tmpfs, i.e. RAM) that is deleted right after reading. - Linux X11 — ImageMagick
importcaptures the root window or the active window (xdotool/xpropfor the window id); monitor selections are cropped in memory usingxrandr --listmonitorsgeometry. - Windows — pure Go via
user32/gdi32(EnumDisplayMonitors,BitBlt,DwmGetWindowAttribute); no external tools needed. - macOS —
screencapture(a temp file under$TMPDIR, removed after reading;allmeans the main display) plusosascriptfor the frontmost window bounds. Requires Screen Recording permission for the terminal app (System Settings → Privacy & Security → Screen Recording).
If no backend is available the tool fails with a clear error naming what to install instead of guessing.
RAG Usage
Build an index:
./vibe --rag-index ./somewhere
Run with RAG enabled:
./vibe --rag -p "Find where permissions are enforced"
Retrieval is hybrid when the embedding role is bound: chunks are ranked by
lexical token overlap and by embedding cosine similarity, then fused with
reciprocal rank fusion. Bind an encoder with /setmodel embedding <number|name>
(any model advertising the embedding capability, e.g. nomic-embed-text) and
point it at an endpoint with /provider. With no embedding role — or a
provider that cannot embed, such as an OpenAI-compatible one — the engine falls
back to lexical-only retrieval and never fails.
Indexing is incremental: each file's content hash is stored, so a re-run only
re-embeds the files that changed and prunes the ones that disappeared. Source
files are split at declaration boundaries (func, type, class, …) instead of
fixed windows, and chunks are embedded in batches through the provider's
/api/embed.
RAG is an optional build feature. To compile with RAG support:
go build -tags rag -o vibe ./cmd/vibe
Development
Running tests
# Default tests (no RAG)
go test ./...
# With RAG support
go test -tags rag ./...
Hermetic end-to-end scenarios (anti-loop with an in-process fake Ollama, and
the CLI flows) run inside go test. Longer, model/network-driven harnesses for
autonomous missions, web research, system tasks, and code editing live under
e2e/ and are run manually; see that README for what each one
covers and how to run it.
Helper scripts
| Script | Purpose |
|---|---|
install-dev.ps1 / install-dev.sh |
Dev build + install with timestamp + short Git hash |
run.ps1 / run.sh |
Build + run with forwarded CLI flags |
release.ps1 / release.sh |
Cross-compile for linux/amd64, linux/arm64, darwin/amd64, darwin/arm64, windows/amd64; produces archives + checksums.txt |
Project layout
cmd/vibe/ # Entry point and CLI wiring
internal/
agent/ # Agent loop, chat orchestration, tool execution
config/ # Config loader (defaults < file < env < CLI)
git/ # Git checkpoint + auto-test
mcp/ # MCP stdio JSON-RPC client
ollama/ # Ollama HTTP client (streaming NDJSON)
onboarding/ # First-run interactive setup
permissions/ # Permission tiers, command gravity, in-session memory, persistence
prompt/ # System prompt builder + instruction-file walk
rag/ # Optional SQLite RAG engine (build tag: rag)
safety/ # Dangerous-command blocklist + env scrub
session/ # Session persistence, compaction, project index
sidecar/ # Sidecar model worker pool
skills/ # Skill discovery (SKILL.md + flat .md) + catalog
slash/ # Slash command dispatcher
terminal/ # Interactive Bash session manager
tools/ # Built-in tool registry + implementations
tui/ # Plain and rich UI implementations
version/ # Build-time version string
watcher/ # File-change poller
Architecture Overview
vibe is structured as a thin cmd/ layer over focused internal/
packages:
- Config (
internal/config) loads settings with the precedencedefaults < vibe-coder.env < environment < CLI flags. - Agent (
internal/agent) runs the multi-turn loop: chat once, parse tool calls, execute, feed results back, repeat. Capped at 50 iterations with 2 retries. - Session (
internal/session) stores the transcript as append-only JSONL. It compacts automatically via the sidecar when the token estimate exceeds 70 % of the context window or message count exceeds 300. - Tools (
internal/tools) register themselves into a central registry. The agent exposes their schemas to the model through the system prompt. MCP tools are discovered at startup — each server independently, best effort — and injected dynamically. - TUI (
internal/tui) abstracts rendering behind aUIinterface.PlainUIis the default;RichUI(Bubble Tea + Lipgloss) is selected with--ui rich. - Ollama client (
internal/ollama) handles streaming NDJSON chat, model tags, version, and pull progress.
Contributing
Contributions are welcome. Please read CONTRIBUTING.md
for the development setup, coding conventions, and the pull-request
checklist. In short:
- Fork the repository and create a topic branch.
- Keep changes small, focused, and covered by tests.
- Run
gofmt -l .,go vet ./..., andgo test ./...before opening a PR. - Describe the motivation and the behavior change in the PR body.
Bug reports and feature requests use the issue templates. This project follows the Contributor Covenant code of conduct.
Security
Please do not open public issues for security problems. See
SECURITY.md for supported versions and the private
reporting process.
License
Released under the MIT License.
Directories
¶
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
vibe
command
|
|
|
vibe-metrics
command
Command vibe-metrics summarizes harness metrics files produced by vibe.
|
Command vibe-metrics summarizes harness metrics files produced by vibe. |
|
e2e
|
|
|
code-edit/checker
command
Command checker validates a code-edit e2e run: the project's tests must pass and only the allowed source files may have changed (no test, config, or unrelated edits, and no new files).
|
Command checker validates a code-edit e2e run: the project's tests must pass and only the allowed source files may have changed (no test, config, or unrelated edits, and no new files). |
|
mission-jobapi
command
Command server is a tiny, self-contained async job API used to exercise the mission runtime end-to-end without ComfyUI.
|
Command server is a tiny, self-contained async job API used to exercise the mission runtime end-to-end without ComfyUI. |
|
mission-jobapi/checker
command
Command checker validates an end-to-end mission run: every dataset item must have exactly one verified line in results.jsonl whose value matches the expected value.
|
Command checker validates an end-to-end mission run: every dataset item must have exactly one verified line in results.jsonl whose value matches the expected value. |
|
mission-resume/progresscheck
command
Command progresscheck reports whether a results.jsonl file holds at least N complete, parseable JSON records.
|
Command progresscheck reports whether a results.jsonl file holds at least N complete, parseable JSON records. |
|
system-hosts/checker
command
Command checker validates a system-hosts e2e run: the hosts file must map the requested address to the requested name exactly once (no duplicates, no wrong IP) and must still contain the entries that were there before.
|
Command checker validates a system-hosts e2e run: the hosts file must map the requested address to the requested name exactly once (no duplicates, no wrong IP) and must still contain the entries that were there before. |
|
web-research/checker
command
Command checker validates a web-research e2e run.
|
Command checker validates a web-research e2e run. |
|
web-research/probe
command
Command probe exercises the real WebSearch and WebFetch tools against the live internet, without a model.
|
Command probe exercises the real WebSearch and WebFetch tools against the live internet, without a model. |
|
internal
|
|
|
clipboard
Package clipboard provides cross-platform access to the system clipboard for text, images, and file references.
|
Package clipboard provides cross-platform access to the system clipboard for text, images, and file references. |
|
decision
Package decision provides a client for the System One decision protocol: POST <endpoint>/v1/systemone with typed questions (noul, choice, score).
|
Package decision provides a client for the System One decision protocol: POST <endpoint>/v1/systemone with typed questions (noul, choice, score). |
|
lsp
Package lsp speaks the slice of the Language Server Protocol that answers the one question the harness asks after an edit: what is wrong with this file? It is a transport-level client over any byte stream — an exec.Cmd pipe in production, an in-memory pipe in tests — and knows nothing about processes, workspaces or paths, which are the caller's business.
|
Package lsp speaks the slice of the Language Server Protocol that answers the one question the harness asks after an edit: what is wrong with this file? It is a transport-level client over any byte stream — an exec.Cmd pipe in production, an in-memory pipe in tests — and knows nothing about processes, workspaces or paths, which are the caller's business. |
|
metrics
Package metrics records per-request harness efficiency data: token usage reported by the model provider plus the estimated sizes of the prompt sections the harness assembled.
|
Package metrics records per-request harness efficiency data: token usage reported by the model provider plus the estimated sizes of the prompt sections the harness assembled. |
|
screenshot
Package screenshot captures a monitor or the active window in memory for the Screenshot tool.
|
Package screenshot captures a monitor or the active window in memory for the Screenshot tool. |
|
visionrole
Package visionrole runs the optional "vision" role: a model bound explicitly with /setmodel vision that looks at images on behalf of a main model that cannot see them.
|
Package visionrole runs the optional "vision" role: a model bound explicitly with /setmodel vision that looks at images on behalf of a main model that cannot see them. |