Skip to content

Commit e2d4e56

Browse files
committed
Add Mistral Vibe provider
1 parent d9acd8c commit e2d4e56

9 files changed

Lines changed: 713 additions & 3 deletions

File tree

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,15 @@
11
# Changelog
22

3+
## Unreleased
4+
5+
### Added (CLI)
6+
- **Mistral Vibe provider.** CodeBurn now reads Mistral Vibe session folders
7+
from `$VIBE_HOME/logs/session/` or `~/.vibe/logs/session/`, using
8+
`meta.json` for cumulative prompt/completion tokens, model pricing, and
9+
timestamps, and `messages.jsonl` for user prompts and tool calls. Subagent
10+
sessions under a parent session's `agents/` folder are tracked separately.
11+
Closes #283.
12+
313
## 0.9.8 - 2026-05-10
414

515
### Added (CLI)

README.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
<a href="https://github.com/sponsors/iamtoruk"><img src="https://img.shields.io/badge/sponsor-♥-ea4aaa?logo=github" alt="Sponsor" /></a>
1414
</p>
1515

16-
CodeBurn tracks token usage, cost, and performance across **18 AI coding tools**. It breaks down spending by task type, model, tool, project, and provider so you can see exactly where your budget goes.
16+
CodeBurn tracks token usage, cost, and performance across **19 AI coding tools**. It breaks down spending by task type, model, tool, project, and provider so you can see exactly where your budget goes.
1717

1818
Everything runs locally. No wrapper, no proxy, no API keys. CodeBurn reads session data directly from disk and prices every call using [LiteLLM](https://github.com/BerriAI/litellm).
1919

@@ -103,6 +103,7 @@ Arrow keys switch between Today, 7 Days, 30 Days, Month, and 6 Months (use `--fr
103103
| <img src="assets/providers/cursor.jpg" width="28" /> | Cursor | Yes | [cursor.md](docs/providers/cursor.md) |
104104
| <img src="assets/providers/cursor-agent.jpg" width="28" /> | cursor-agent | Yes | [cursor-agent.md](docs/providers/cursor-agent.md) |
105105
| <img src="assets/providers/gemini.png" width="28" /> | Gemini CLI | Yes | [gemini.md](docs/providers/gemini.md) |
106+
| <img src="assets/providers/mistral-vibe.svg" width="28" /> | Mistral Vibe | Yes | [mistral-vibe.md](docs/providers/mistral-vibe.md) |
106107
| <img src="assets/providers/copilot.jpg" width="28" /> | GitHub Copilot | Yes | [copilot.md](docs/providers/copilot.md) |
107108
| <img src="assets/providers/kiro.png" width="28" /> | Kiro | Yes | [kiro.md](docs/providers/kiro.md) |
108109
| <img src="assets/providers/opencode.png" width="28" /> | OpenCode | Yes | [opencode.md](docs/providers/opencode.md) |
@@ -131,6 +132,8 @@ The `--provider` flag filters any command to a single provider: `codeburn report
131132

132133
**Gemini CLI** stores sessions as single JSON files. Each session embeds real token counts (input, output, cached, thoughts) per message, so no estimation is needed. Gemini reports input tokens inclusive of cached; CodeBurn subtracts cached from input before pricing to avoid double charging.
133134

135+
**Mistral Vibe** stores sessions as folders under `~/.vibe/logs/session/` (or `$VIBE_HOME/logs/session/`). CodeBurn reads cumulative prompt/completion totals and model pricing from `meta.json`, then reads `messages.jsonl` for the first user prompt and assistant tool calls. Subagent sessions under `agents/` are counted as separate Vibe sessions.
136+
134137
**Kiro** stores conversations as `.chat` JSON files. Token counts are estimated from content length. The underlying model is not exposed, so sessions are labeled `kiro-auto` and costed at Sonnet rates.
135138

136139
**GitHub Copilot** reads from both `~/.copilot/session-state/` (legacy CLI) and VS Code's `workspaceStorage/*/GitHub.copilot-chat/transcripts/`. The VS Code format has no explicit token counts; tokens are estimated from content length and the model is inferred from tool call ID prefixes.
@@ -376,6 +379,8 @@ These are starting points, not verdicts. A 60% cache hit on a single experimenta
376379

377380
**Gemini CLI** stores sessions as single JSON files at `~/.gemini/tmp/<project>/chats/session-*.json`. Each session embeds real token counts (input, output, cached, thoughts) per message. Gemini reports input tokens inclusive of cached; CodeBurn subtracts cached from input before pricing to avoid double charging.
378381

382+
**Mistral Vibe** stores session folders at `~/.vibe/logs/session/`. Each folder contains `meta.json` with cumulative prompt/completion token totals, model pricing, timestamps, and working directory, plus `messages.jsonl` with user prompts and assistant tool calls. CodeBurn emits one record per Vibe session because the source data is cumulative, not per assistant turn.
383+
379384
**OpenClaw** stores agent sessions as JSONL at `~/.openclaw/agents/*.jsonl`. Also checks legacy paths `.clawdbot`, `.moltbot`, `.moldbot`. Token usage comes from assistant message `usage` blocks; model from `modelId` or `message.model` fields.
380385

381386
**Roo Code / KiloCode** are Cline-family VS Code extensions. CodeBurn reads `ui_messages.json` from each task directory in VS Code's `globalStorage`, filtering `type: "say"` entries with `say: "api_req_started"` to extract token counts.
@@ -391,6 +396,7 @@ CodeBurn deduplicates messages (by API message ID for Claude, by cumulative toke
391396
| `CODEX_HOME` | Override Codex data directory (default: `~/.codex`) |
392397
| `FACTORY_DIR` | Override Droid data directory (default: `~/.factory`) |
393398
| `QWEN_DATA_DIR` | Override Qwen data directory (default: `~/.qwen/projects`) |
399+
| `VIBE_HOME` | Override Mistral Vibe home directory (default: `~/.vibe`) |
394400

395401
## Sponsoring CodeBurn
396402

assets/providers/mistral-vibe.svg

Lines changed: 12 additions & 0 deletions
Loading

docs/providers/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ For the architectural picture, see `../architecture.md`.
1717
| [Gemini](gemini.md) | JSON / JSONL | `src/providers/gemini.ts` | none |
1818
| [KiloCode](kilo-code.md) | JSON | `src/providers/kilo-code.ts` | `tests/providers/kilo-code.test.ts` |
1919
| [Kiro](kiro.md) | JSON | `src/providers/kiro.ts` | `tests/providers/kiro.test.ts` |
20+
| [Mistral Vibe](mistral-vibe.md) | JSON / JSONL | `src/providers/mistral-vibe.ts` | `tests/providers/mistral-vibe.test.ts` |
2021
| [OpenClaw](openclaw.md) | JSONL | `src/providers/openclaw.ts` | `tests/providers/openclaw.test.ts` |
2122
| [Pi](pi.md) | JSONL | `src/providers/pi.ts` | `tests/providers/pi.test.ts` |
2223
| [OMP](omp.md) | JSONL | `src/providers/pi.ts` | `tests/providers/omp.test.ts` |

docs/providers/mistral-vibe.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# Mistral Vibe
2+
3+
Mistral Vibe CLI.
4+
5+
- **Source:** `src/providers/mistral-vibe.ts`
6+
- **Loading:** eager (`src/providers/index.ts`)
7+
- **Test:** `tests/providers/mistral-vibe.test.ts`
8+
9+
## Where it reads from
10+
11+
`$VIBE_HOME/logs/session/` when `VIBE_HOME` is set, otherwise `~/.vibe/logs/session/`.
12+
13+
## Storage format
14+
15+
Vibe 2.x stores each session as a directory:
16+
17+
- `meta.json` contains session metadata, cumulative token totals, active model config, model prices, timestamps, working directory, and available tools.
18+
- `messages.jsonl` contains non-system messages and assistant `tool_calls`.
19+
20+
Subagent traces are stored under a parent session's `agents/` folder with the same `meta.json` / `messages.jsonl` shape, so CodeBurn scans those one level down as separate sessions.
21+
22+
## Caching
23+
24+
None.
25+
26+
## Deduplication
27+
28+
Per `mistral-vibe:<session_id>`.
29+
30+
## Quirks
31+
32+
- **Usage is cumulative per session.** Vibe does not write per-assistant-message token usage into `messages.jsonl`; token counts come from `meta.json.stats.session_prompt_tokens` and `session_completion_tokens`. CodeBurn emits one usage record per Vibe session.
33+
- **Cost prefers Vibe's own model prices.** `meta.json.stats.input_price_per_million` and `output_price_per_million` are used first, with the active model config as a fallback. LiteLLM pricing is only used when Vibe provides no price data.
34+
- **Project names come from metadata.** Discovery uses `meta.json.environment.working_directory` and falls back to the session directory name if that field is missing.
35+
- **Tool calls come from messages.** Assistant `tool_calls[*].function.name` is normalized to the standard CodeBurn names (`bash` to `Bash`, `search_replace` to `Edit`, etc.). Bash commands are extracted from `function.arguments.command`.
36+
37+
## When fixing a bug here
38+
39+
1. Reproduce with a fixture that has both `meta.json` and `messages.jsonl`; both files are required for current Vibe sessions.
40+
2. If the bug is "wrong total", check `meta.json.stats` first. `messages.jsonl` is only for prompts and tool calls.
41+
3. If a future Vibe release adds per-turn usage, add tests before changing the one-record-per-session behavior so historical sessions continue to parse correctly.

src/providers/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { droid } from './droid.js'
55
import { gemini } from './gemini.js'
66
import { kiloCode } from './kilo-code.js'
77
import { kiro } from './kiro.js'
8+
import { mistralVibe } from './mistral-vibe.js'
89
import { openclaw } from './openclaw.js'
910
import { pi, omp } from './pi.js'
1011
import { qwen } from './qwen.js'
@@ -101,7 +102,7 @@ async function loadCrush(): Promise<Provider | null> {
101102
}
102103
}
103104

104-
const coreProviders: Provider[] = [claude, codex, copilot, droid, gemini, kiloCode, kiro, openclaw, pi, omp, qwen, rooCode]
105+
const coreProviders: Provider[] = [claude, codex, copilot, droid, gemini, kiloCode, kiro, mistralVibe, openclaw, pi, omp, qwen, rooCode]
105106

106107
export async function getAllProviders(): Promise<Provider[]> {
107108
const [ag, gs, cursor, opencode, cursorAgent, crush] = await Promise.all([loadAntigravity(), loadGoose(), loadCursor(), loadOpenCode(), loadCursorAgent(), loadCrush()])

0 commit comments

Comments
 (0)