Vibe Orchestrator

SkillProductivity

Delegate a coding task to Mistral Vibe and supervise the result via git diff. Trigger: /vibe <instruction>. Claude orchestrates, Vibe codes. Also handles /vibe-report [--since N] [--project NAME] [--fails] — token/cost/failure report.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the Vibe Orchestrator skill

What this skill tells your AI

The instructions your AI receives, as published by pcx-wave/vibe-skill in SKILL.md and read by ahel’s review.

/vibeon | /vibeoff | /vibestatus

Toggle auto-delegate mode — Vibe automatically handles coding tasks without requiring /vibe each time.

CommandAction
/vibeontouch ~/.local/share/vibe-auto.flag → confirm "Auto-vibe ON"
/vibeoffrm -f ~/.local/share/vibe-auto.flag → confirm "Auto-vibe OFF"
/vibestatusreport auto-mode (ON/OFF) and active model override

For /vibestatus, run both checks and print two lines:

Auto-vibe: ON | OFF
Model: <alias>  (override)  OR  Model: <default from ~/.vibe/config.toml>  (config default)

Auto-mode pre-filter (when flag is set)

When vibe-auto.flag exists, apply this gate before loading the full skill:

Task signalAction
1 file, ≤10 lines, exact location already knownEdit directly — do NOT invoke the skill
Logic non-trivial, location unclear, multiple files, HTML/JS content, or >1 changeInvoke /vibe as normal

/vibe-report

If the user invokes /vibe-report, run ~/tools/delegate-report with any flags extracted from the arguments, display output verbatim, and stop.

User saysFlag
"last 7 days", "7d"--since 7
"last 30 days", "30d"--since 30
"project foo"--project foo
"only failures", "fails", "bugs"--fails
"adapt", "adaptations", "by adaptation"--adapt
"all delegates", "everything", "compare delegates"--all
"delegate foo", "only opencode"--delegate foo
(nothing)(no flags — vibe runs only)

The report defaults to vibe runs only. The log is shared across delegate tools (vibe, opencode, gemini); use --all for the cross-delegate comparison or --delegate NAME to scope to a different one.


/vibe-model-pick | /vibe-model-clear

Override the Vibe model for all subsequent delegations without touching ~/.vibe/config.toml.

CommandAction
/vibe-model-pick <alias>echo <alias> > ~/.local/share/vibe-model.flag → confirm
/vibe-model-clearrm -f ~/.local/share/vibe-model.flag → confirm "back to config default"

Aliases are machine-specific — read the actual list (and the default) from [[model]] entries in ~/.vibe/config.toml before suggesting one; never assume an alias from this table exists. Example snapshot from one machine:

AliasModelProviderNotes
deepseek-flashdeepseek-v4-flashDeepSeekDefault — fast, cheap; solid for inline edits
mistral-medium-3.5mistral-vibe-cli-latestMistralStronger reasoning; reliable for inline edits
devstral-smalldevstral-small-latestMistralAgent-mode — read/explore only, weak at inline edits
localdevstral (llamacpp)LocalRequires local server on :8080

Run the bash command, print one confirmation line showing the active model, and stop.


When the user invokes /vibe <instruction>, Claude delegates the implementation to Mistral Vibe via its programmatic mode, supervises in real time, and reports.


Known Limits

Hard constraints — not config options. Full details in SKILL-reference.md.

  • UTF-8 / special chars → silent search_replace match failure. Use python3 str.replace() for accented chars or emoji.
  • Code duplication → Vibe may re-insert a block already written. Grep for duplicate definitions after every run.
  • HTML in prompt → tags like <div> are shell redirects (exit 127). Write HTML content to a temp file; reference the path in the prompt.
  • Source code in bash heredoc → quotes/backslashes mangle. Use search_replace directly; never a helper script that replaces code.
  • read_file caps at 50 KiB / 2000 lines → a larger file comes back as read_file failed: Output (N KiB) exceeds maximum allowed size, and Vibe then re-reads it in slices, so it may reason about only part of the file without saying so. Keep any file you hand it (diffs, generated inputs) under 50 KiB — split by area rather than dumping one big diff.
  • Files outside the workdir are unreadable--trust does NOT cover them. Any path outside workdir raises an outside_directory approval that is auto-denied headlessly: the run exits 0 with 0 tool calls and, before the 2026-08-02 parser fix, said nothing at all. Any file the prompt points Vibe at must live inside the workdir. Stage temp inputs in a gitignored dir in the repo, not /tmp.
  • Orchestration chain → 6 failure points in order: CLI auth → pseudo-TTY → stream parser → TOML pricing → git diff → JSON log. When a run produces unexpected results, work down this list. Full details in SKILL-reference.md.

Step 1 — Detect workdir

  1. git rev-parse --show-toplevel in the current directory.
  2. If ambiguous or no git repo → ask with AskUserQuestion.

Step 2 — Decompose the task

Critical rule: keep tasks atomic and focused — one objective, one prompt.

SizeDefinitionMax turnsApproach
Trivial1 file, change is obvious and locatedSkip delegation — edit directly
Simple1 file, non-trivial logic or unknown location5–81 vibe call
Medium2–3 related files, 1 objective8–121 structured vibe call
Complex>3 files OR business logic OR DB migrationsBreak into sub-tasks

Decomposition for complex tasks:

Sub-task 1: Explore / read relevant files (read-only, 5 turns)
Sub-task 2: Implement change A in file X (8 turns)
Sub-task 3: Implement change B in file Y (8 turns)
Sub-task 4: Verify / test (5 turns)

→ Check git diff between each sub-task before launching the next.


Step 3 — Write the Vibe prompt

The prompt must be self-contained.

Structure:

Stack: Python/Flask, SQLAlchemy, SQLite
Key files: app.py (routes + fetch), models.py (Entry)

TASK: [one single thing to do, stated as an imperative]

CONSTRAINTS:
- [what must not break]
- [expected format if relevant]

VERIFY: grep for "def function_name" in file.py and confirm it exists.

Formulation rules:

  • One task per prompt — never "also do X and Y"
  • Name the exact files to modify
  • Include a grep-based verification criterion (not a file re-read)
  • Language: English (better Mistral performance)

Prompt adaptations:

  • Any task that defines or calls a specific function: include the exact signature — def validate(data: dict) -> tuple[bool, list[str]]:.
  • No fixed signature, but conventions matter: point at the file to read first ("read app.py, follow its route/jsonify style") instead — don't do both, they're substitutes.
  • Write/modify tasks: append an output format block:
    OUTPUT FORMAT:
    Modified: <file>
    Does: <one line>
    No other prose.
    

⚠️ Shell safety: if the prompt contains UTF-8 accented chars, emojis, : in Python/YAML code, or typographic apostrophes — the vibe-delegate script passes them safely via a temp file (printf %q). Never interpolate such a prompt directly into a bash heredoc.

Verification — always use grep, not file re-read:

VERIFY: grep for "def extract_labels" in app.py and confirm it exists.

Step 4 — Launch Vibe

~/tools/vibe-delegate "<workdir>" "<prompt>" [max-turns] [agent] [timeout-secs]
ArgumentDefaultNotes
workdirAbsolute path, must exist
promptSelf-contained task description
max-turns10Mistral turn limit — hard cap at 12, never more
agent(none)See agent table below
timeout-secs180Wall-clock kill timer
--require STR(none)Repeatable. Abort before launch if STR is absent in the workdir — pass the search_replace anchor here
--with-review N(none)After Vibe finishes, Claude reviews the diff and re-delegates fixes up to N times (see Step 8)
--verbose(off)Print per-token-type cost breakdown (input/output tokens × pricing) instead of the compact summary line
--no-ghostwrite(off)Disable the ghostwrite fallback — after 3 failed Vibe attempts, halt and ask instead of finishing (see Step 6)

Available agents:

These are Vibe's built-in agents. Custom agents can add to this list, but nothing outside it is guaranteed to exist — an unknown name aborts the run in ~1.5s with Error: Agent 'X' not found.

AgentTypeUse
(default → auto-approve)agentGeneral implementation — all tools run without approval
planagentRead-only — reviews, analysis, planning. The right pick for any "look, don't touch" task
defaultagentStandard loop, prompts for approval (needs a TTY — avoid headless)
accept-editsagentAuto-approves edits but not other tools
leanagentReduced tool surface
exploresubagentRead-only exploration — cannot be passed to --agent; usable only as a sub-agent from within a run

⚠️ code-reviewer, planner, and code-architect were listed here until 2026-08-02 but do not exist in current Vibe. Use plan for review and planning work. Verify against BUILTIN_AGENTS in vibe/core/agents/models.py if a name is ever in doubt.

Read-only runs write nothing by design — that is success, not WROTE_NOTHING. Do not apply the Step 6 retry/ghostwrite ladder to a plan/explore run.

Recommended max turns:

  • Read/explore: 5
  • Simple change (1 file): 8
  • Medium change (2–3 files): 12
  • Never exceed 12 — decompose instead

Background launch:

~/tools/vibe-delegate "<workdir>" "<prompt>" 10 > /tmp/vibe_out.txt 2>&1 &
# Monitor with: tail -f /tmp/vibe_out.txt

Step 5 — Supervise in real time

The script prints live:

=== VIBE START ===
Workdir : /path/to/project
Agent   : default
Turns   : 10
Timeout : 180s
Prompt  : Stack: Python/Flask. File: app.py ...
===================
  [read]  app.py
  [tool]  file: app.py
  [tool]  search_replace [OK] ...
  [vibe]  Done. Converted date to datetime.date in fetch_data().
Tool calls: 5
Delegate tokens (run): 4,800  (last turn: 4,600+200)  |  cost ~$0.0086
Claude Sonnet 4.6 eq: same tokens would cost ~$0.0168  (ratio x2.0)
=== VIBE DONE (exit: 0) ===
=== SYNTAX OK (1 check(s)) ===

=== UNCOMMITTED CHANGES ===
 app.py | 4 ++--
[log] → ~/.local/share/delegate-runs.jsonl  (4800 tokens, exit 0, 34.2s)

Vibe never commits. All changes are left unstaged — git checkout . reverts everything if needed.

Red flags to act on immediately:

FlagMeaningAction
[WARN]Vibe encountered an errorRead the error, fix manually
[ERROR]Vibe refused to start (bad agent name, bad flag) — run died in ~1.5sFix the argument named in the message; do NOT retry verbatim
[DENIED]A tool call was auto-denied. Almost always a path outside the workdirMove the file inside the workdir and re-run. --trust will not fix it
[APPROVAL]Vibe needed a permission it did not haveSame as [DENIED] — check the outside workdir label
[tool] search_replace [FAIL]UTF-8 match failureEdit manually with Python str.replace()
exit: 1 or non-zeroVibe failed / did not complete verificationRead diff, correct prompt
No [tool] file: lines, or 0 files changedLooks like WROTE_NOTHINGbut this is stdout, not proof. The harness only prints what its stream parser recognized; it does not print what Vibe actually submitted to write_file. Do the journal check below before treating this as a failure.See mandatory pre-check below. Only once that check comes back genuinely empty: revise prompt and retry; ghostwrite fallback after 3 attempts. Follow Step 6. Not applicable to plan/explore — writing nothing is correct there.
Mangled tool name in output (e.g. write_filecepte)Vibe produced a garbled tool call — write never executedSame journal check as above before assuming the content is gone.
=== SYNTAX ERRORS ===Post-run syntax check failedFix before committing
Same file read 5+ timesVibe is looping — run likely lostAbort, check diff, try again

Known bugs and workarounds:

BugCauseFix
Variable declared twiceVibe doesn't check scopeGrep the variable before relaunching
Truncated promptSpecial chars in inline promptScript uses temp file — should be fixed
Wrote a Python helper just to replace codeMisdiagnosed search_replace limitUse search_replace directly for ASCII code; write_file only if new content is too long for the prompt
Empty run — 0 files changed despite ≥3 tool callsMulti-edit prompt: first search_replace target not found byte-for-byteSplit into sequential single-change runs; grep target string locally before delegating
Tool calls: 0 on a run that clearly did work; no [vibe] answer lineFixed 2026-08-02. The stream parser only understood the legacy role-keyed schema; current Vibe emits tool calls as type: effect and message content as a list of blocks. content.lower() on that list threw AttributeError, killing the parser and discarding the whole run outputAlready patched. If it reappears, the run output is still recoverable from ~/.vibe/logs/session/<newest>/messages.jsonl — nothing is lost, only unprinted

If exit non-zero: do not relaunch immediately. Read the diff, understand what was done, fix the prompt.

Mandatory pre-check before declaring WROTE_NOTHING. stdout is a filtered stream, not a record of what Vibe did — the parser can drop or mislabel a write it doesn't recognize the shape of. The session journal is not filtered: ~/.vibe/logs/session/session_<ts>_<id>/messages.jsonl records the exact arguments of every write_file/search_replace call, whether or not the write later succeeded. vibe-delegate already runs this check for you — when the filesystem shows 0 changed files, it scans the journal for the newest session and prints an [RECOVERY] block listing every write attempt found, with char counts, before the syntax-check section. Read that block before concluding the run failed. A run that "wrote nothing" per stdout but shows a few-KB write_file in [RECOVERY] did not fail — the content exists and is very likely usable; treat it as a formatting/parsing problem, not an empty run, and inspect the content in the journal directly:

JOURNAL=$(ls -t ~/.vibe/logs/session/*/messages.jsonl | head -1)   # or the [RECOVERY] path printed
python3 -c "
import json
for line in open('$JOURNAL'):
    m = json.loads(line)
    if m.get('role') != 'assistant': continue
    for tc in (m.get('tool_calls') or []):
        fn = tc.get('function') or {}
        if 'write' in fn.get('name',''):
            print(json.loads(fn['arguments'])['content'])
"

Confirmed case (2026-08-11, this repo): stdout showed 0 files changed and a turn-limit stop on a temperature.py delegation. The journal held a complete, correct 5,706-char module from the first write_file call — Vibe had simply wrapped it as {"module_docstring": """...."""}\n<real source> instead of emitting raw source, the write step rejected the shape, and Vibe burned the rest of the turn budget on write→rm→write instead of on the actual task. Stripping the one-line JSON envelope recovered the module outright — no retry needed. The retry that was run cost ~180k tokens for a result no better than turn 1's output already sitting in the journal.

If, after reading the journal, there is genuinely no write_file/edit call for the target file at all — that's the real WROTE_NOTHING, and Step 6 applies.


Step 6 — Iteration

  • Max 3 attempts per sub-task before escalating to the user.
  • Between attempts, read the git diff to avoid doubling partial work.

WROTE_NOTHING / failed write — auto-retry then ghostwrite (default)

When a run produces any of the following:

  • No [tool] file: lines (WROTE_NOTHING)
  • Mangled tool call output (e.g. write_filecepte, search_replacecepte)
  • Non-zero exit with 0 files changed

First, revise the prompt (simpler target, explicit file path, shorter scope) and re-run. This counts against the 3-attempt limit.

If all 3 attempts still produce WROTE_NOTHING or a failed write, the default is to finish the task, not halt. Claude writes the content itself (ghostwrite) — either delegating only the file-write step to Vibe or writing the file directly — then continues the chain. The task always completes; a stubborn Vibe never strands the run.

The Step 7 report must list ghostwritten files separately from Vibe-authored files — never silently mix them:

Files ghostwritten (Claude-authored after 3 failed Vibe attempts):
  - path/to/file.ext

--no-ghostwrite — opt out of the ghostwrite fallback

When the user passes --no-ghostwrite to /vibe, Claude must not ghostwrite. After 3 failed attempts, halt and ask instead:

⛔ Vibe wrote nothing after 3 attempts (--no-ghostwrite set).
  File expected : <path>
  Failure signal: <WROTE_NOTHING | mangled write | exit N, 0 files>
  Sub-task      : <description>

Options:
  (r) Revise prompt manually and retry
  (a) Abort

Do not proceed to subsequent sub-tasks until resolved.


Step 7 — Report to the user

✓ Vibe finished — <1-line summary>

Files modified:
  - path/to/file.ext (+X / -Y lines)

[If problem]:
⚠ <description> — completing manually / relaunching?

Ready to commit?

When --verbose was passed: after the file list, quote the full token/cost block verbatim from the script output — the lines starting with Model, Input, Output, Total, Claude. Do not paraphrase or summarize them. Example:

Model               : mistral-medium-3.5
  Input  :     45,120  @  $1.50/M  = $0.0677
  Output :      1,280  @  $7.50/M  = $0.0096
  Total  :     46,400              = $0.0773
  Claude :     46,400  (@ $3/$15/M)  = $0.1531  (x1.98 cheaper)

Step 8 — Review pass (--with-review N)

Triggered only when the user passes --with-review N to /vibe. Runs after Step 5 confirms exit 0.

8.1 — Read the diff

Run git diff (or git diff HEAD if changes are staged). Read the full output. This is Claude's review input — do not ask Vibe to review.

8.2 — Classify issues

Scan only for fundamental issues. Flag these:

CategoryExamples
Incorrect logicoff-by-one, wrong branch condition, inverted boolean
Crash-causing gapunhandled None/null dereference, missing required field, index out of range
Broken contractfunction signature changed without updating callers, return type mismatch
Wrong scopechange applied to wrong file/function/class
Security holeunsanitized user input passed to shell/SQL/eval, credentials in plaintext

Do NOT flag: style, naming, formatting, unused imports, performance, readability, or subjective preferences. If unsure whether an issue is fundamental, skip it.

8.3 — Re-delegate one issue per iteration

For each fundamental issue found (up to N):

  1. Write one atomic Vibe prompt targeting that single issue (follow Step 3 rules).
  2. Launch via vibe-delegate (same workdir, same model).
  3. After Vibe finishes, re-read the diff and re-evaluate: did the issue get fixed?
  4. Move to the next issue (or stop if N exhausted or no issues remain).

Never bundle two fixes into one prompt. One issue → one delegation → one diff check.

If N iterations are exhausted and issues remain, list them but do not attempt further fixes — report and stop.

8.4 — Tracking state across iterations

Maintain these counters locally (not persisted mid-run):

review_iterations_allowed  = N
review_iterations_used     = 0
review_issues_found        = <count from initial diff review>
review_issues_fixed        = 0
review_issues_remaining    = review_issues_found

Increment review_iterations_used and review_issues_fixed after each successful re-delegation. Update review_issues_remaining accordingly.

8.5 — Report

After all iterations or no issues remain, append to the Step 7 report:

Review pass (--with-review N):
  Issues found:     X
  Issues fixed:     Y
  Issues remaining: Z
  Iterations used:  A / N

If issues remain, list each one briefly (file:line — description). Do not re-attempt them.

8.6 — Log fields

After the review pass, the run log entry for the original Vibe run must include these additional fields in ~/.local/share/delegate-runs.jsonl:

"review_iterations_allowed": N,
"review_iterations_used": A,
"review_issues_found": X,
"review_issues_fixed": Y,
"review_issues_remaining": Z

When --with-review is not used, all five fields are omitted (do not write them as 0).


Orchestration rules

  • Decompose before delegating — one task, one prompt.
  • Streaming always — never --output text.
  • Check diff between sub-tasks — never launch the next one blind.
  • Ghostwrite only as a last resort — always delegate to Vibe first; never write code yourself before 3 failed attempts. After 3 failures the default is to ghostwrite and finish (declare it in the Step 7 report). --no-ghostwrite disables the fallback and halts instead. See Step 6.
  • Max 12 turns per call — decompose instead of extending.
  • Grep target before delegatinggrep -n "exact_target" file.py before any search_replace prompt. Pass that anchor as --require "exact_target" so the delegate aborts before launching if it's gone. Always use grep for VERIFY, not file re-read.
  • Match model to task — inline-edit tasks → deepseek-flash or mistral-medium-3.5; never route edits to agent-mode devstral-small (read/explore only).
  • UTF-8 / emoji in the prompt → the script handles it via temp file, but test with a short prompt first.
  • UTF-8 in OLD/NEW strings → mandate python3, no exceptions — before writing any Vibe prompt, scan every OLD and NEW string for accented chars (é è à ç ù ô î û ñ...) or emoji. If ANY are present, the prompt MUST explicitly instruct Vibe to use python3 -c with pathlib.Path.read_text/write_text(encoding='utf-8') for those changes — never search_replace. A search_replace [OK] on UTF-8 content is a silent no-op: the tool call is accepted but the string is never found. This is the #1 cause of phantom successful runs that changed nothing.
  • Check string delimiters before delegating JS/JSON changes — if a replacement value contains apostrophes (') and the target JS property uses single-quote delimiters, the NEW string must switch to double-quotes (") for that property. Scan OLD→NEW pairs for this conflict before writing the prompt; never leave it to Vibe to discover at runtime.
  • After any run that touches imports: grep the import line — always run grep "^from X import" file.py before the next sub-task.
  • search_replace [OK] ≠ correct change — always grep the specific changed line, not just check syntax.
  • Provide data structure context — if a route accesses a DB payload, include the exact field paths (payload['produit']['nom']) in the prompt.
  • Reuse existing assets — for UI tasks, tell Vibe to link existing CSS/JS files. "Use /static/style.css and CSS class bar-row" is always better than "generate a dark theme".

Run Log

Every run appends one JSON entry to ~/.local/share/delegate-runs.jsonl. Log fields and jq queries → see SKILL-reference.md.

Shortened here. Read the whole file on GitHub.

Signals

GitHub stars
118
Forks
12
Last commit
Aug 2026
Advanced
Catalog kind
skill
Gateway key
vibe-pcx-wave
Source
github.com/pcx-wave/vibe-skill