codeburn/docs
Nihal Jain bae016c050 feat(copilot): track GitHub Copilot JetBrains IDE usage
## What & why

The JetBrains Copilot plugin (IntelliJ, PyCharm, RubyMine, …) stores its
chat/agent sessions under `~/.config/github-copilot/<ide>/<kind>/<storeId>/` —
a location none of the existing Copilot sources (CLI JSONL, VS Code chat
sessions/transcripts, OTel SQLite) read. As a result all JetBrains Copilot
usage was silently uncounted in every CodeBurn report. This adds a reader for
that store so those sessions are discovered, priced, and attributed to the
right project.

## How it works

- **Reader.** The store's session content is a Nitrite `.db` — an H2 MVStore of
  Java-serialized documents. It is scanned as `latin1` for byte-offset
  stability: no Java deserializer, no new dependency, and it is not SQLite so
  `node:sqlite` is not involved.
- **Reply text.** Assistant replies live in nested-escaped
  `{"__first__":{"type":"Subgraph"…}}` blobs. The text is recovered by
  unescaping one level at a time and, at the depth where the Markdown record's
  `data` field is a well-formed one-level-escaped JSON document, reading it
  structurally — so a reply containing its own quotes is never truncated or
  duplicated (which would otherwise inflate the estimate).
- **Tokens/cost.** The store records no token counts, so output tokens are
  estimated from the reply text (`CHARS_PER_TOKEN = 4`, re-decoded
  latin1→utf8 so multibyte replies count by codepoint) and every call is marked
  `costIsEstimated`. Failed generations (error status, no reply) are billed $0.
- **Sessions.** One `.db` holds many chat tabs; turns are grouped back to their
  conversation GUID so the UI shows one session per tab, deduped by reply
  content per conversation.
- **Project attribution**, most authoritative first:
  1. the plugin-recorded `projectName` field (JetBrains Copilot 1.12+), joined
     across kind dirs by store id — the billable turns live in
     `chat-agent-sessions`, but the label is usually written into the sibling
     `chat-sessions`/`chat-edit-sessions` store. Read length-delimited and
     re-decoded latin1→utf8 so non-ASCII repo names round-trip.
  2. the `.git` repo root of a referenced `file://` path.
  3. a generic `copilot-jetbrains` bucket when neither signal exists.
  The conversation title is a chat-thread name, not a project, so it is kept
  out of the project field and surfaced as the session label instead.

Override the JetBrains github-copilot root with
`CODEBURN_COPILOT_JETBRAINS_DIR`.

## Docs

- `docs/providers/copilot.md` — full JetBrains section (store layout, latin1
  scan, reply extraction, projectName precedence + cross-kind join).
- `docs/providers/README.md` — Copilot storage updated to note the Nitrite .db.

## How to verify

- `npm test -- copilot` and `npx tsc --noEmit` (fixtures reproduce the real
  nested-escaped .db framing, including quote- and multibyte-bearing replies).
- End to end against a real install:
  `CODEBURN_CACHE_DIR=$(mktemp -d) node dist/cli.js status --provider copilot \
     --period all --format menubar-json`
  — JetBrains sessions appear By-Project under their real repo names.
- Set `CODEBURN_COPILOT_JETBRAINS_DIR` to a fixture root to parse a controlled
  store without touching the real config dir.
2026-07-03 16:11:27 +05:30
..
design docs(mcp): add implementation plan for codeburn MCP server 2026-06-02 01:33:08 -07:00
providers feat(copilot): track GitHub Copilot JetBrains IDE usage 2026-07-03 16:11:27 +05:30
architecture.md Merge main into feat/kimi-code-provider to resolve conflicts 2026-05-16 07:25:12 -07:00