Repository navigation
feat(desktop): add first-class Mistral Vibe coding agent - #5552
Conversation
|
Ready to review this PR? Stage has broken it down into 7 individual chapters for you: Chapters generated by Stage for commit f786a22 on Jul 9, 2026 10:55pm UTC. |
📝 WalkthroughWalkthroughThis PR adds Mistral Vibe as a built-in terminal agent with registry and icon wiring, env-based model selection, managed lifecycle hooks, event mapping, documentation, discovery, and marketing updates. ChangesMistral Vibe agent support
Estimated code review effort: 3 (Moderate) | ~30 minutes Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Greptile SummaryThis PR adds Mistral Vibe (
Confidence Score: 4/5The change is additive and well-contained — one registry entry drives all derived surfaces, new plumbing follows existing patterns exactly, and tests cover the main paths. The only gap is a missing guard in the hooks.toml writer for the case where a prior partial write left an orphaned start marker. The architecture is methodical and every touch point (registry, model layer, wrapper, event mapping, docs) is covered and tested. The getVibeHooksTomlContent function has an edge case when the end marker is absent from a corrupted file: the old partial block is not stripped before the new one is appended, leading to duplicate hooks firing until the next provisioning run. This is a narrow, self-healing scenario but no test covers it and no guard documents it. apps/desktop/src/main/lib/agent-setup/agent-wrappers-vibe.ts — specifically the orphaned-start-marker branch in getVibeHooksTomlContent.
|
| Filename | Overview |
|---|---|
| apps/desktop/src/main/lib/agent-setup/agent-wrappers-vibe.ts | New file: implements the Vibe PATH-wrapper (exports VIBE_ENABLE_EXPERIMENTAL_HOOKS=true) and marker-guarded ~/.vibe/hooks.toml writer; logic is clean with one edge case when the end marker is missing from a prior partial write. |
| packages/shared/src/agent-models.ts | Adds optional modelEnv field to AgentModelSupport and new buildAgentModelEnv for env-based model delivery; vibe entry added with VIBE_ACTIVE_MODEL and two model aliases; consistent with buildAgentModelArgs contract. |
| packages/host-service/src/trpc/router/agents/agents.ts | Merges buildAgentModelEnv result into the env overlay after config.env, giving the picker-selected model precedence; correct spread order and clean integration. |
| packages/shared/src/builtin-terminal-agents.ts | Registers vibe with id 'vibe', label 'Mistral Vibe', command 'vibe --trust --auto-approve', includeInDefaultTerminalPresets=true; default argv transport auto-submits the positional prompt. |
| packages/host-service/src/events/map-event-type.ts | Maps Vibe's before_tool → Start and post_agent_turn → Stop; correct placement in existing branches, covered by new test cases. |
| apps/desktop/src/main/lib/agent-setup/desktop-agent-capabilities.ts | Registers vibe-hooks-toml and vibe-wrapper action slugs and DESKTOP_AGENT_SETUP_TARGETS entry; satisfies the compile-enforced actions union. |
| packages/ui/src/assets/icons/preset-icons/index.ts | Imports vibe.svg and wires PRESET_ICONS.vibe with same icon for light and dark modes; consistent with amp/claude/gemini which also use a single SVG for both themes. |
| apps/desktop/src/main/lib/agent-setup/agent-wrappers.test.ts | Adds wrapper and hooks.toml unit tests covering happy-path idempotency and user-hook preservation; missing coverage for the orphaned-start-marker edge case. |
Sequence Diagram
%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant User
participant Desktop as Desktop (renderer)
participant HostService as host-service (agents.ts)
participant Wrapper as ~/.superset/bin/vibe (wrapper)
participant Vibe as vibe CLI
participant NotifySh as notify.sh
User->>Desktop: Select Mistral Vibe + model, launch task
Desktop->>HostService: "agents.run({ presetId:"vibe", model:"mistral-medium-3.5", prompt })"
HostService->>HostService: "buildAgentModelEnv → { VIBE_ACTIVE_MODEL: "mistral-medium-3.5" }"
HostService->>HostService: buildAgentModelArgs("vibe",...) → [] (no --model flag)
HostService->>Desktop: "spawn PTY: VIBE_ACTIVE_MODEL=... vibe --trust --auto-approve '<prompt>'"
Desktop->>Wrapper: exec vibe
Wrapper->>Wrapper: "export VIBE_ENABLE_EXPERIMENTAL_HOOKS=true + SUPERSET_AGENT_ID=vibe"
Wrapper->>Vibe: exec real vibe binary
Vibe->>NotifySh: before_tool hook fires
NotifySh->>HostService: "POST /notify (eventType=before_tool)"
HostService->>HostService: mapEventType(before_tool) → Start
HostService->>Desktop: working indicator ON
Vibe->>NotifySh: post_agent_turn hook fires
NotifySh->>HostService: "POST /notify (eventType=post_agent_turn)"
HostService->>HostService: mapEventType(post_agent_turn) → Stop
HostService->>Desktop: working indicator OFF + completion chime
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant User
participant Desktop as Desktop (renderer)
participant HostService as host-service (agents.ts)
participant Wrapper as ~/.superset/bin/vibe (wrapper)
participant Vibe as vibe CLI
participant NotifySh as notify.sh
User->>Desktop: Select Mistral Vibe + model, launch task
Desktop->>HostService: "agents.run({ presetId:"vibe", model:"mistral-medium-3.5", prompt })"
HostService->>HostService: "buildAgentModelEnv → { VIBE_ACTIVE_MODEL: "mistral-medium-3.5" }"
HostService->>HostService: buildAgentModelArgs("vibe",...) → [] (no --model flag)
HostService->>Desktop: "spawn PTY: VIBE_ACTIVE_MODEL=... vibe --trust --auto-approve '<prompt>'"
Desktop->>Wrapper: exec vibe
Wrapper->>Wrapper: "export VIBE_ENABLE_EXPERIMENTAL_HOOKS=true + SUPERSET_AGENT_ID=vibe"
Wrapper->>Vibe: exec real vibe binary
Vibe->>NotifySh: before_tool hook fires
NotifySh->>HostService: "POST /notify (eventType=before_tool)"
HostService->>HostService: mapEventType(before_tool) → Start
HostService->>Desktop: working indicator ON
Vibe->>NotifySh: post_agent_turn hook fires
NotifySh->>HostService: "POST /notify (eventType=post_agent_turn)"
HostService->>HostService: mapEventType(post_agent_turn) → Stop
HostService->>Desktop: working indicator OFF + completion chime
Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.
---
### Issue 1 of 1
apps/desktop/src/main/lib/agent-setup/agent-wrappers-vibe.ts:51-57
**Orphaned start marker silently skips cleanup**
When `VIBE_HOOKS_MARKER_START` is present in an existing `hooks.toml` but `VIBE_HOOKS_MARKER_END` is missing (e.g. a previous partial write), the `if (end !== -1)` guard skips stripping and the function appends a fresh complete block. The resulting file has two start markers and one end marker. On the **next** call, `indexOf(VIBE_HOOKS_MARKER_END, start)` finds the end marker of the newly-appended block, so everything between the two markers is removed — which spans the entire second managed block including the freshly written hooks. In the interim Vibe loads a `hooks.toml` with a dangling partial managed block and duplicate `before_tool`/`post_agent_turn` entries, causing double-firing chimes until the next provisioning run fixes it.
A minimal guard — stripping from `start` to end-of-file when `end === -1` — avoids the transient duplicate state.
```suggestion
if (start !== -1) {
const end = base.indexOf(VIBE_HOOKS_MARKER_END, start);
if (end !== -1) {
base =
base.slice(0, start) + base.slice(end + VIBE_HOOKS_MARKER_END.length);
} else {
// Orphaned start marker (truncated/partial write) — remove everything
// from the start marker to end of file so we don't accumulate blocks.
base = base.slice(0, start);
}
}
```
Reviews (1): Last reviewed commit: "feat(ui): use the official Mistral Vibe ..." | Re-trigger Greptile
| if (start !== -1) { | ||
| const end = base.indexOf(VIBE_HOOKS_MARKER_END, start); | ||
| if (end !== -1) { | ||
| base = | ||
| base.slice(0, start) + base.slice(end + VIBE_HOOKS_MARKER_END.length); | ||
| } | ||
| } |
There was a problem hiding this comment.
Orphaned start marker silently skips cleanup
When VIBE_HOOKS_MARKER_START is present in an existing hooks.toml but VIBE_HOOKS_MARKER_END is missing (e.g. a previous partial write), the if (end !== -1) guard skips stripping and the function appends a fresh complete block. The resulting file has two start markers and one end marker. On the next call, indexOf(VIBE_HOOKS_MARKER_END, start) finds the end marker of the newly-appended block, so everything between the two markers is removed — which spans the entire second managed block including the freshly written hooks. In the interim Vibe loads a hooks.toml with a dangling partial managed block and duplicate before_tool/post_agent_turn entries, causing double-firing chimes until the next provisioning run fixes it.
A minimal guard — stripping from start to end-of-file when end === -1 — avoids the transient duplicate state.
| if (start !== -1) { | |
| const end = base.indexOf(VIBE_HOOKS_MARKER_END, start); | |
| if (end !== -1) { | |
| base = | |
| base.slice(0, start) + base.slice(end + VIBE_HOOKS_MARKER_END.length); | |
| } | |
| } | |
| if (start !== -1) { | |
| const end = base.indexOf(VIBE_HOOKS_MARKER_END, start); | |
| if (end !== -1) { | |
| base = | |
| base.slice(0, start) + base.slice(end + VIBE_HOOKS_MARKER_END.length); | |
| } else { | |
| // Orphaned start marker (truncated/partial write) — remove everything | |
| // from the start marker to end of file so we don't accumulate blocks. | |
| base = base.slice(0, start); | |
| } | |
| } |
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/desktop/src/main/lib/agent-setup/agent-wrappers-vibe.ts
Line: 51-57
Comment:
**Orphaned start marker silently skips cleanup**
When `VIBE_HOOKS_MARKER_START` is present in an existing `hooks.toml` but `VIBE_HOOKS_MARKER_END` is missing (e.g. a previous partial write), the `if (end !== -1)` guard skips stripping and the function appends a fresh complete block. The resulting file has two start markers and one end marker. On the **next** call, `indexOf(VIBE_HOOKS_MARKER_END, start)` finds the end marker of the newly-appended block, so everything between the two markers is removed — which spans the entire second managed block including the freshly written hooks. In the interim Vibe loads a `hooks.toml` with a dangling partial managed block and duplicate `before_tool`/`post_agent_turn` entries, causing double-firing chimes until the next provisioning run fixes it.
A minimal guard — stripping from `start` to end-of-file when `end === -1` — avoids the transient duplicate state.
```suggestion
if (start !== -1) {
const end = base.indexOf(VIBE_HOOKS_MARKER_END, start);
if (end !== -1) {
base =
base.slice(0, start) + base.slice(end + VIBE_HOOKS_MARKER_END.length);
} else {
// Orphaned start marker (truncated/partial write) — remove everything
// from the start marker to end of file so we don't accumulate blocks.
base = base.slice(0, start);
}
}
```
How can I resolve this? If you propose a fix, please make it concise.There was a problem hiding this comment.
Good catch — fixed in 420b6cf.
getVibeHooksTomlContent now strips from the start marker to end-of-file when VIBE_HOOKS_MARKER_END is absent:
} else {
// Orphaned start marker (partial/interrupted write) — strip to EOF.
base = base.slice(0, start);
}so an orphaned/partial block can no longer produce duplicate [[hooks]] entries or a dangling marker. Added a regression test ("cleans up an orphaned start marker left by a partial write") covering the missing-end-marker path.
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/desktop/src/main/lib/agent-setup/agent-wrappers-vibe.ts`:
- Around line 48-61: Update getVibeHooksTomlContent so it removes an existing
managed block even when VIBE_HOOKS_MARKER_START is present but
VIBE_HOOKS_MARKER_END is missing. In the existing marker handling branch, after
locating VIBE_HOOKS_MARKER_START, treat a missing end marker as “strip from
start to end-of-file” instead of leaving the old block in place, then continue
with the existing trim-and-append flow using buildVibeManagedHooksBlock so
duplicate [[hooks]] entries and dangling markers are not emitted.
In `@plans/20260709-mistral-vibe-agent-impl-plan.md`:
- Around line 658-664: The Task 4.3 “optional” labeling is incorrect because the
marketing/docs discovery surfaces are part of the required core scope. Update
the plan section so the Mistral Vibe additions are treated as mandatory across
the referenced surfaces, including the marketing components, docs enumerations,
and mobile store keywords; keep the existing file references like
UniversalCompatibilityDemo, AppMockup/constants, mcp.mdx, terminal-presets.mdx,
and store.config.json aligned with the required scope.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 6fe7507e-da7e-41ea-aadc-595f4f4d6f7e
⛔ Files ignored due to path filters (3)
apps/marketing/public/app-icons/vibe.svgis excluded by!**/*.svgbun.lockis excluded by!**/*.lockpackages/ui/src/assets/icons/preset-icons/vibe.svgis excluded by!**/*.svg
📒 Files selected for processing (24)
AGENTS.mdapps/desktop/src/main/lib/agent-setup/agent-wrappers-vibe.tsapps/desktop/src/main/lib/agent-setup/agent-wrappers.test.tsapps/desktop/src/main/lib/agent-setup/agent-wrappers.tsapps/desktop/src/main/lib/agent-setup/desktop-agent-capabilities.tsapps/desktop/src/main/lib/agent-setup/desktop-agent-setup.tsapps/desktop/src/renderer/routes/_authenticated/components/AgentHooks/hooks/useDefaultV2TerminalPresets/default-v2-terminal-presets.tsapps/desktop/src/renderer/routes/_authenticated/settings/agents/components/V2AgentsSettings/components/AgentIconPicker/agent-icon-options.tsapps/desktop/src/renderer/routes/_authenticated/settings/utils/settings-search/settings-search.tsapps/docs/content/docs/mcp.mdxapps/docs/content/docs/terminal-presets.mdxapps/marketing/src/app/components/FeaturesSection/components/UniversalCompatibilityDemo/UniversalCompatibilityDemo.tsxapps/marketing/src/app/components/HeroSection/components/AppMockup/constants.tsapps/mobile/store.config.jsonpackages/host-service/src/events/map-event-type.test.tspackages/host-service/src/events/map-event-type.tspackages/host-service/src/trpc/router/agents/agents.tspackages/shared/src/agent-command.test.tspackages/shared/src/agent-models.test.tspackages/shared/src/agent-models.tspackages/shared/src/builtin-terminal-agents.tspackages/ui/src/assets/icons/preset-icons/index.tsplans/20260709-mistral-vibe-agent-first-class.mdplans/20260709-mistral-vibe-agent-impl-plan.md
There was a problem hiding this comment.
All reported issues were addressed across 27 files
Architecture diagram
sequenceDiagram
participant User as Desktop User
participant DesktopUI as Desktop Renderer
participant HostService as Host Service (tRPC)
participant Terminal as Terminal PTY
participant VibeWrapper as Vibe PATH Wrapper
participant VibeProc as Mistral Vibe Process
participant NotifyScript as notify.sh (hooks)
participant EventMapper as Event Mapper
participant Indicator as Working Indicator / Chime
Note over User,Indicator: Mistral Vibe launch & lifecycle flow
User->>DesktopUI: Select "Mistral Vibe" + model
DesktopUI->>HostService: agents.run (presetId: "vibe", model: "mistral-medium-3.5")
HostService->>HostService: Build command: "vibe --trust --auto-approve '<prompt>'"
HostService->>HostService: Build model env: { VIBE_ACTIVE_MODEL: "mistral-medium-3.5" }
HostService->>Terminal: Create terminal session with env overlay (includes VIBE_ACTIVE_MODEL)
Terminal->>Terminal: Spawn login shell, exec command via PATH
Terminal->>VibeWrapper: Shell resolves "vibe" → managed wrapper script
VibeWrapper->>VibeWrapper: export SUPERSET_AGENT_ID="vibe"
VibeWrapper->>VibeWrapper: export VIBE_ENABLE_EXPERIMENTAL_HOOKS="true"
VibeWrapper->>VibeProc: exec real vibe binary with same args
VibeProc->>VibeProc: Read ~/.vibe/hooks.toml (pre-provisioned)
Note over VibeProc: Hooks include before_tool & post_agent_turn → notify.sh
VibeProc->>VibeProc: Start solving task (multiple tool calls)
loop For each tool call
VibeProc->>NotifyScript: Execute before_tool hook command (stdin JSON)
NotifyScript->>EventMapper: POST event "before_tool"
EventMapper->>EventMapper: mapEventType("before_tool") → "Start"
EventMapper-->>Indicator: Update working indicator active
end
VibeProc->>NotifyScript: Execute post_agent_turn hook (agent idle)
NotifyScript->>EventMapper: POST event "post_agent_turn"
EventMapper->>EventMapper: mapEventType("post_agent_turn") → "Stop"
EventMapper-->>Indicator: Stop working indicator + play chime
Indicator-->>DesktopUI: Visual feedback (working / done)
Note over VibeProc,NotifyScript: Agent setup (runs once before first launch)
Note over VibeProc: ~/.vibe/hooks.toml written with marker guard<br/>before_tool & post_agent_turn entries
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
|
Thanks for the reviews. Addressed the one actionable code finding — the orphaned start-marker edge case in the Vibe On the CodeRabbit pre-merge warnings:
|
…gent # Conflicts: # bun.lock
|
Hey @Kitenite I'd love to get a review on this. Myself and a couple of my coworkers at Mistral love Superset and would love to be able to use our Vibe CLI on it! |
…lock When the end marker was missing, orphaned-block recovery stripped from the start marker to end-of-file, silently deleting any user [[hooks]] that followed our managed block. Strip only our own superset-notify-* tables and stop at the first foreign TOML table instead.
Vibe re-implemented getClaudeManagedHookCommand verbatim (only SUPERSET_AGENT_ID differed), so a rename of notify.sh or a change to the runtime guard would silently drift the Vibe copy. Extract getManagedNotifyHookCommand(agentId) + a shared MANAGED_NOTIFY_RELATIVE_PATH in agent-wrappers-common and route both Claude and Vibe through it.
|
There's also no running hook only stop hook? |
|
Note on the orphaned-marker fix (420b6cf): the strip-to-EOF ( |

Description
Adds Mistral Vibe (
vibe) as a first-class terminal coding agent, matching the depth of the existing Claude / Codex integrations: selectable in every agent picker with its own icon, launches into a PTY pane with a seeded prompt, exposes a model picker (env-basedVIBE_ACTIVE_MODEL, since Vibe has no--modelflag), and drives the working-indicator + completion-chime lifecycle via a managed wrapper + a marker-guarded~/.vibe/hooks.toml. No changes to the Mistral Vibe CLI are required.Everything flows from a single entry in
BUILTIN_TERMINAL_AGENTS; the non-derived touch points (icon, model catalog, desktop hook pipeline, event mapping, docs) are updated to match.Related Issues
closes #5551
Type of Change
Testing
bun run lintclean (0 findings),bun run typecheck28/28,bunx sherifclean,bun install --frozenno changes.agent-command,agent-models(incl.buildAgentModelEnv),map-event-type(before_tool → Start,post_agent_turn → Stop),agent-wrappers(vibe wrapper +~/.vibe/hooks.tomlwriter).VIBE_ACTIVE_MODEL=<model> vibe --trust --auto-approve '<prompt>'; working indicator lights on first tool call, completion chime fires once at idle. Verified the generated wrapper and~/.vibe/hooks.tomlprovisioning, and drove the fullsupersetCLI lifecycle from inside a Vibe session.Screenshots (if applicable)
Additional Notes
mistral-medium-3.5,devstral-small); delivered viaVIBE_ACTIVE_MODELbecause Vibe has no--modelflag.[[mcp_servers]], not.mcp.json), thevibe-acpACP runtime, and optional upstream Vibe hooks (turn-start / permission-requested) for finer working-state fidelity.Summary by cubic
Adds Mistral Vibe (
vibe) as a first-class terminal coding agent with full model selection and lifecycle hooks. Adds a shared notify-hook command and saferhooks.tomlrecovery to protect user-defined hooks.New Features
BUILTIN_TERMINAL_AGENTS, default terminal presets, icon sets, settings search, docs/marketing/mobile.vibe --trust --auto-approve '<prompt>'(positional prompt auto-submits).VIBE_ACTIVE_MODEL; addedmodelEnvandbuildAgentModelEnv, merged into the host launch env.VIBE_ENABLE_EXPERIMENTAL_HOOKSand stampsSUPERSET_AGENT_ID; provisions marker-guarded~/.vibe/hooks.tomlwithbefore_toolandpost_agent_turnhooks; mapped toStart/Stop.AGENTS.mdand.agents/skills/(with--trust); MCP uses TOML[[mcp_servers]]in.vibe/config.toml.hooks.tomlrecovery now preserves user hooks/comments when the end marker is missing by removing only managedsuperset-notify-*tables and stopping at the first foreign TOML table.getManagedNotifyHookCommand/MANAGED_NOTIFY_RELATIVE_PATHinagent-wrappers-common; applied to Claude and Vibe.Migration
vibeis installed andMISTRAL_API_KEYis configured (or runvibe --setup).~/.vibe/hooks.toml. Model picker includesmistral-medium-3.5anddevstral-small.Written for commit f786a22. Summary will update on new commits.
Summary by CodeRabbit
vibe) as a built-in agent across the app, including icons, marketing/UX surfaces, default terminal presets, and quick-add templates.--modelflag.hooks.tomlgeneration to be idempotent and robust against partial writes.hooks.tomlgeneration, and hook event mapping.