Summary
The cline provider silently reports zero usage for sessions created by the Cline CLI (npm cline, currently 3.0.49). It only discovers the VS Code extension's tasks/<taskId>/ui_messages.json layout. The CLI writes a completely different layout — ~/.cline/data/sessions/<sessionId>/ — which nothing in codeburn reads.
There is no warning and no partial data — --verbose says nothing either. The provider is registered and "supported", so the report looks correct while the tokens are simply missing.
On this machine that is 5 sessions from a single afternoon: 174,544 input / 10,202 output / 127,410 cache-read tokens, $0.035 — invisible.
Environment
|
|
| codeburn |
main @ 2de4d100bf746b0830b1d43c6118a91d853628b5 (0.9.19) |
| Cline CLI |
cline@3.0.49 (npm, global) |
| OS |
macOS 26.6 (25G72), arm64 |
| Node |
v24.18.0 |
| History size |
du -sh ~/.cline/data → 5.2M (sessions/ → 292K) |
What codeburn scans today
src/providers/cline.ts:55-58 builds exactly two roots:
const baseDirs = configuredDirs ?? [
getVSCodeGlobalStoragePath(EXTENSION_ID), // saoudrizwan.claude-dev
getClineDataPath(), // ~/.cline/data
]
Both are handed to discoverClineTasks, which per docs/providers/cline.md includes a task only when it has a ui_messages.json — i.e. it requires <root>/tasks/<taskId>/ui_messages.json.
~/.cline/data/tasks/ does not exist on a CLI install. There is no tasks/ directory at all.
What the Cline CLI actually writes
~/.cline/data/
sessions/<sessionId>/
<sessionId>.json # session metadata + rolled-up usage
<sessionId>.messages.json # per-message metrics
db/sessions.db # SQLite mirror of the same rows (WAL)
settings/ locks/ cache/ logs/
<sessionId>.json (redacted, real file):
<sessionId>.messages.json — { version, updated_at, agent, sessionId, messages[], system_prompt }, where each assistant message carries its own metrics:
The SQLite mirror at ~/.cline/data/db/sessions.db has one sessions row per session with the same fields plus parent_session_id, parent_agent_id, agent_id, is_subagent, conversation_id, metadata_json, messages_path.
Everything codeburn needs — provider, model, timestamps, project path, git branch, per-call token splits, cache reads/writes, cost — is already there. It is just never opened.
If you want to confirm the layout without my machine: npm i -g cline@3.0.49 and run one prompt, or grep the shipped bundle — node_modules/@cline/core/dist/index.js contains the sessions path construction, the <id>.messages.json filename, and the messages_path field. Note that CLINE_DIR and CLINE_DATA_DIR relocate the root, so discovery should respect them rather than hardcoding ~/.cline (there is also CLINE_TEAM_DATA_DIR for team mode).
Repro
$ ls ~/.cline/data/sessions | wc -l
5
$ npx tsx src/cli.ts export --format json --provider cline --from 2026-08-01 --to 2026-08-02 -o /tmp/cline.json
Exported (2026-08-01 to 2026-08-02) to: /tmp/cline.json
$ python3 -c "import json;d=json.load(open('/tmp/cline.json'));print(d['summary']);print(len(d['sessions']),'sessions',len(d['records']),'records')"
[{'Period': '2026-08-01 to 2026-08-02', 'Cost (USD)': 0, 'Saved (USD)': 0, 'API Calls': 0, 'Sessions': 0, 'Projects': 0}]
0 sessions 0 records
Ground truth for the same window, read straight from the session metadata:
$ sqlite3 ~/.cline/data/db/sessions.db \
"select session_id, json_extract(metadata_json,'$.usage.inputTokens'),
json_extract(metadata_json,'$.usage.outputTokens'),
json_extract(metadata_json,'$.totalCost') from sessions;"
1785699299865_ynuy2|5483|133|0.0081984
1785700832224_ss0xl|0|0|0
1785700997640_0bvrp|0|0|0
1785701013051_rhwoy|0|0|0
1785701058566_vnwtz|169061|10069|0.0268881228
Totals: 174,544 in / 10,202 out / $0.0351 — reported by codeburn as $0.00.
Minimal discovery probe
Plants four fixtures under a temp $HOME and asks the provider what it finds. Run from the repo root with npx tsx:
import { mkdirSync, writeFileSync, rmSync } from 'fs'
import { join } from 'path'
import { tmpdir } from 'os'
const HOME = join(tmpdir(), 'cb-cline-probe')
rmSync(HOME, { recursive: true, force: true })
process.env.HOME = HOME
const EXT = 'saoudrizwan.claude-dev'
const uiMsgs = JSON.stringify([{ ts: 1785701064304, type: 'say', say: 'api_req_started',
text: JSON.stringify({ tokensIn: 100, tokensOut: 10, cost: 0.001, request: 'x' }) }])
const apiHist = JSON.stringify([{ role: 'assistant', content: [{ type: 'text', text: 'hi' }] }])
// A) extension/tasks layout in all three VS Code variants
for (const [variant, taskId] of [['Code','task-stable'],['Code - Insiders','task-insiders'],['VSCodium','task-vscodium']] as const) {
const d = join(HOME, 'Library', 'Application Support', variant, 'User', 'globalStorage', EXT, 'tasks', taskId)
mkdirSync(d, { recursive: true })
writeFileSync(join(d, 'ui_messages.json'), uiMsgs)
writeFileSync(join(d, 'api_conversation_history.json'), apiHist)
}
// B) Cline CLI 3.0.49 layout
const sid = '1785701058566_vnwtz'
const sd = join(HOME, '.cline', 'data', 'sessions', sid)
mkdirSync(sd, { recursive: true })
writeFileSync(join(sd, `${sid}.json`), JSON.stringify({ version: 1, session_id: sid, source: 'cli',
status: 'completed', provider: 'cline-pass', model: 'z-ai/glm-5.2', cwd: HOME, workspace_root: HOME,
started_at: '2026-08-02T20:04:18.628Z', ended_at: '2026-08-02T20:08:27.768Z',
metadata: { totalCost: 0.0268881228, usage: { inputTokens: 169061, outputTokens: 10069,
cacheReadTokens: 127360, cacheWriteTokens: 0, totalCost: 0.0268881228 } },
messages_path: join(sd, `${sid}.messages.json`) }))
writeFileSync(join(sd, `${sid}.messages.json`), JSON.stringify({ version: 1, agent: 'lead', sessionId: sid,
messages: [{ id: 'msg_1', role: 'assistant', content: 'hi', ts: 1785701064304,
modelInfo: { id: 'z-ai/glm-5.2', provider: 'cline-pass' },
metrics: { inputTokens: 6937, outputTokens: 213, cacheReadTokens: 0, cacheWriteTokens: 0, cost: 0.002108502 } }] }))
const { createClineProvider } = await import('./src/providers/cline.js')
for (const f of await createClineProvider().discoverSessions()) console.log('-', f.path.replace(HOME, '~'))
Actual output — 4 fixtures planted, 1 discovered:
- ~/Library/Application Support/Code/User/globalStorage/saoudrizwan.claude-dev/tasks/task-stable
Secondary finding: only the stable VS Code variant is scanned
Visible in the same probe run, and independent of the CLI issue — happy to split this into its own issue if you prefer.
src/providers/cline.ts:56 calls the singular getVSCodeGlobalStoragePath(EXTENSION_ID), which returns only paths[0] (vscode-cline-parser.ts:41-43), and then passes it as an explicit overrideDir. That bypasses getVSCodeGlobalStoragePaths(), which would also have returned Code - Insiders and VSCodium (vscode-cline-parser.ts:15-31).
Sibling providers do not do this — roo-code.ts:20 and kilo-code.ts:38 pass overrideDir straight through (undefined by default), so they scan all three. Swapping the extension id in the probe above to rooveterinaryinc.roo-cline and calling createRooCodeProvider() finds 3 of 3 with identical fixtures, versus Cline's 1 of 3.
So for Cline users on Insiders or VSCodium, the globalStorage copy is missed entirely. Whether that means zero data depends on whether their Cline version also mirrors tasks into ~/.cline/data/tasks/, which is still scanned — so this is "silently partial" rather than guaranteed-empty, and it varies by version.
Worth noting because #230 asked for VSCodium support and getVSCodeGlobalStoragePaths duly covers it — Cline is just the one provider that routes around it.
Suggested fix
Deferring to your judgement on shape — two options:
- Third root inside the existing
cline provider. discoverSessions already merges roots and dedups by task id; a sessions/-layout branch would need its own parser, so the provider stops being a thin wrapper over the shared Cline-family parser. There is precedent — kilo-code.ts:38-46 already merges discoverClineTasks with discoverSqliteSessions in one provider and dispatches to the matching parser per source.
- A separate
cline-cli provider. Keeps vscode-cline-parser.ts untouched (docs/providers/cline.md explicitly asks not to fork it while Roo Code / KiloCode share it), at the cost of splitting one tool across two provider names in reports and filters.
I lean toward (2) for parser hygiene, but (1) if you would rather users see a single cline row.
Either way, notes from the data:
- JSON over SQLite.
sessions.db is live and WAL-mode; the per-session JSON is self-contained and needs no native dep. sqlite-session-parser.ts exists if you would rather go the DB route, but the JSON path avoids the lazy-load requirement in CONTRIBUTING.md for native deps.
- The metadata file alone gives session-level totals, so a cheap first cut is possible without parsing messages; per-call records need
<sessionId>.messages.json.
- Subagents are already modelled —
is_subagent, parent_session_id, parent_agent_id — which lines up with the subagent-fold work in recent PRs.
- Model strings are inconsistent across sessions from the same run:
z-ai/glm-5.2, cline-pass/glm-5.2, and bare GLM-5.2 all appear in my five sessions. Whatever normalization you choose will need to survive that, or pricing lookups will miss.
provider is the upstream LLM route, not the tool (cline-pass here), so it should not be confused with codeburn's own provider name.
Prior art
#130 asked for Cline support generically and was closed 2026-05-16; #312 implemented the extension layout and #836 refactored it into core. All three predate the CLI layout, and I could not find an existing issue covering it. Shape-wise this is the same class as #850, #873, and #626 — discovery missing a real on-disk session variant.
Per CONTRIBUTING.md I am commenting before coding rather than sending an unsolicited PR: I use the Cline CLI daily and have real sessions to test against, so I am happy to implement whichever shape you prefer, with a fixture-based test under tests/providers/. Just say which.
Summary
The
clineprovider silently reports zero usage for sessions created by the Cline CLI (npmcline, currently 3.0.49). It only discovers the VS Code extension'stasks/<taskId>/ui_messages.jsonlayout. The CLI writes a completely different layout —~/.cline/data/sessions/<sessionId>/— which nothing in codeburn reads.There is no warning and no partial data —
--verbosesays nothing either. The provider is registered and "supported", so the report looks correct while the tokens are simply missing.On this machine that is 5 sessions from a single afternoon: 174,544 input / 10,202 output / 127,410 cache-read tokens, $0.035 — invisible.
Environment
main@2de4d100bf746b0830b1d43c6118a91d853628b5(0.9.19)cline@3.0.49(npm, global)du -sh ~/.cline/data→ 5.2M (sessions/→ 292K)What codeburn scans today
src/providers/cline.ts:55-58builds exactly two roots:Both are handed to
discoverClineTasks, which perdocs/providers/cline.mdincludes a task only when it has aui_messages.json— i.e. it requires<root>/tasks/<taskId>/ui_messages.json.~/.cline/data/tasks/does not exist on a CLI install. There is notasks/directory at all.What the Cline CLI actually writes
<sessionId>.json(redacted, real file):{ "version": 1, "session_id": "1785701058566_vnwtz", "source": "cli", "started_at": "2026-08-02T20:04:18.628Z", "ended_at": "2026-08-02T20:08:27.768Z", "status": "completed", "exit_code": 0, "interactive": false, "provider": "cline-pass", "model": "z-ai/glm-5.2", "cwd": "/Users/<user>/dev/<org>/<repo>", "workspace_root": "/Users/<user>/dev/<org>/<repo>", "metadata": { "git": { "url": "<redacted>", "branch": "main" }, "title": "<first line of prompt>", "totalCost": 0.0268881228, "usage": { "inputTokens": 169061, "outputTokens": 10069, "cacheReadTokens": 127360, "cacheWriteTokens": 0, "totalCost": 0.0268881228 } }, "messages_path": "/Users/<user>/.cline/data/sessions/1785701058566_vnwtz/1785701058566_vnwtz.messages.json" }<sessionId>.messages.json—{ version, updated_at, agent, sessionId, messages[], system_prompt }, where each assistant message carries its own metrics:{ "id": "msg_ZLpzRugU", "role": "assistant", "content": "<trimmed>", "ts": 1785701064304, "modelInfo": { "id": "z-ai/glm-5.2", "provider": "cline-pass" }, "metrics": { "inputTokens": 6937, "outputTokens": 213, "cacheReadTokens": 0, "cacheWriteTokens": 0, "cost": 0.002108502 } }The SQLite mirror at
~/.cline/data/db/sessions.dbhas onesessionsrow per session with the same fields plusparent_session_id,parent_agent_id,agent_id,is_subagent,conversation_id,metadata_json,messages_path.Everything codeburn needs — provider, model, timestamps, project path, git branch, per-call token splits, cache reads/writes, cost — is already there. It is just never opened.
If you want to confirm the layout without my machine:
npm i -g cline@3.0.49and run one prompt, or grep the shipped bundle —node_modules/@cline/core/dist/index.jscontains thesessionspath construction, the<id>.messages.jsonfilename, and themessages_pathfield. Note thatCLINE_DIRandCLINE_DATA_DIRrelocate the root, so discovery should respect them rather than hardcoding~/.cline(there is alsoCLINE_TEAM_DATA_DIRfor team mode).Repro
Ground truth for the same window, read straight from the session metadata:
Totals: 174,544 in / 10,202 out / $0.0351 — reported by codeburn as $0.00.
Minimal discovery probe
Plants four fixtures under a temp
$HOMEand asks the provider what it finds. Run from the repo root withnpx tsx:Actual output — 4 fixtures planted, 1 discovered:
Secondary finding: only the stable VS Code variant is scanned
Visible in the same probe run, and independent of the CLI issue — happy to split this into its own issue if you prefer.
src/providers/cline.ts:56calls the singulargetVSCodeGlobalStoragePath(EXTENSION_ID), which returns onlypaths[0](vscode-cline-parser.ts:41-43), and then passes it as an explicitoverrideDir. That bypassesgetVSCodeGlobalStoragePaths(), which would also have returnedCode - InsidersandVSCodium(vscode-cline-parser.ts:15-31).Sibling providers do not do this —
roo-code.ts:20andkilo-code.ts:38passoverrideDirstraight through (undefined by default), so they scan all three. Swapping the extension id in the probe above torooveterinaryinc.roo-clineand callingcreateRooCodeProvider()finds 3 of 3 with identical fixtures, versus Cline's 1 of 3.So for Cline users on Insiders or VSCodium, the globalStorage copy is missed entirely. Whether that means zero data depends on whether their Cline version also mirrors tasks into
~/.cline/data/tasks/, which is still scanned — so this is "silently partial" rather than guaranteed-empty, and it varies by version.Worth noting because #230 asked for VSCodium support and
getVSCodeGlobalStoragePathsduly covers it — Cline is just the one provider that routes around it.Suggested fix
Deferring to your judgement on shape — two options:
clineprovider.discoverSessionsalready merges roots and dedups by task id; asessions/-layout branch would need its own parser, so the provider stops being a thin wrapper over the shared Cline-family parser. There is precedent —kilo-code.ts:38-46already mergesdiscoverClineTaskswithdiscoverSqliteSessionsin one provider and dispatches to the matching parser per source.cline-cliprovider. Keepsvscode-cline-parser.tsuntouched (docs/providers/cline.mdexplicitly asks not to fork it while Roo Code / KiloCode share it), at the cost of splitting one tool across two provider names in reports and filters.I lean toward (2) for parser hygiene, but (1) if you would rather users see a single
clinerow.Either way, notes from the data:
sessions.dbis live and WAL-mode; the per-session JSON is self-contained and needs no native dep.sqlite-session-parser.tsexists if you would rather go the DB route, but the JSON path avoids the lazy-load requirement inCONTRIBUTING.mdfor native deps.<sessionId>.messages.json.is_subagent,parent_session_id,parent_agent_id— which lines up with the subagent-fold work in recent PRs.z-ai/glm-5.2,cline-pass/glm-5.2, and bareGLM-5.2all appear in my five sessions. Whatever normalization you choose will need to survive that, or pricing lookups will miss.provideris the upstream LLM route, not the tool (cline-passhere), so it should not be confused with codeburn's own provider name.Prior art
#130 asked for Cline support generically and was closed 2026-05-16; #312 implemented the extension layout and #836 refactored it into core. All three predate the CLI layout, and I could not find an existing issue covering it. Shape-wise this is the same class as #850, #873, and #626 — discovery missing a real on-disk session variant.
Per
CONTRIBUTING.mdI am commenting before coding rather than sending an unsolicited PR: I use the Cline CLI daily and have real sessions to test against, so I am happy to implement whichever shape you prefer, with a fixture-based test undertests/providers/. Just say which.