Cherry Studio

  • ID: cherrystudio
  • Store: <app data>/CherryStudio/Data/Agents/.claude/<projects>/<workspace>/<session>.jsonl — ~/Library/Application Support/CherryStudio on macOS, %APPDATA%\CherryStudio on Windows, $XDG_CONFIG_HOME/CherryStudio elsewhere
  • Store (before the upgrade that added Data/Agents): <app data>/CherryStudio/.claude/<projects>/…, read so a store that predates it is not lost
  • Stores of the other agent runtimes: <app data>/CherryStudio/Data/Agents/.pi/sessions/<ts>_<id>.jsonl (pi) and <app data>/CherryStudio/Data/Agents/.dsh/sessions/<workspace>/session-<id>/ (dsh, DSH_HOME set to Data/Agents/.dsh), read by the stock pi and dsh parsers and filed under cherrystudio (#4342)
  • Moved data dir: every directory in app.user_data_path of ~/.cherrystudio/boot-config.json, where the app records a move made in its settings, is read the same way as the default (#4347)
  • Read override: DEJA_CHERRYSTUDIO_ROOTS replaces the root list, the pi and dsh stores included
  • Format: Claude Code's own transcript JSONL; pi and dsh logs for those runtimes
  • Needs: nothing

Cherry Studio runs Claude Code sessions from a desktop app and writes ordinary

Claude Code transcripts under its own app data, so the parsing is Claude's.

Last verified: 2026-10-07

The fixture in this repository sits at fixtures/registry/cherrystudio/projects/… rather than

under a .claude directory: the repository excludes .claude/, so a fixture carrying that

segment is never committed and the tests pass only on the machine that wrote it. The parse does

not depend on the directory shape — the roots come from the environment — and the real layout is

the one above.

Known quirks and drift

  • A snapshot per stream chunk. Cherry Studio appends the same API call three

or four times as the response streams: a new uuid each time, the same

requestId and message.id, and the text growing. Read plainly that is one

reply stored three times in prefixes — a recall can then quote half a sentence

and deja show prints the answer twice before finishing it. The reader

collapses a run by requestId (else message.id, else uuid) and keeps the

longest text. Stock Claude Code writes one record per turn, so its reader is

unchanged and a test pins that.

  • Sessions are their own harness rather than extra Claude roots: a reader should

see which app the work happened in, and deja sources says cherrystudio.

  • Wiring: deja install cherrystudio writes

<config>/deja/cherrystudio-mcp.json and says to import it in Settings → MCP

→ Import from JSON. The app keeps its MCP servers in its own SQLite store — a

drizzle schema seeded from a built-in preset list

(src/main/data/services/McpServerService.ts,

src/main/data/db/seeding/seeders/builtinMcpServerSeeder.ts) — so there is no

config file to write, and writing into a running app's database is not an

installer's job. Its import paths are JSON, DXT and MCPB

(src/renderer/pages/settings/McpSettings/McpServersList.tsx); the MCPB

bundle this project already publishes is the other one and needs nothing new.

  • The skill channel exists, and it is not one of its own: Cherry Studio

discovers the skill directories of the agent CLIs a machine has —

~/.agents/skills, ~/.claude/skills, ~/.codex/skills and a dozen more,

in src/main/ai/skills/systemSkillSources.ts — and lists what it finds for

the user to enable per agent. deja install cherrystudio writes deja's

shared skill into ~/.agents/skills, which is one of those roots, so the

app lists it; enabling it for the agent is the one click left.

  • Auto-recall: deja install cherrystudio-auto writes Claude Code's hooks

into <userData>/Data/Agents/.claude/settings.json, the CLAUDE_CONFIG_DIR

2.0.14 gives the Claude agents it runs (feature.agents.claude.root; a move

in ~/.cherrystudio/boot-config.json moves it). Run with the app's own SDK,

binary, env and options against a stub, SessionStart, UserPromptSubmit,

PreToolUse and PostToolUse output reached the model (#4802). The sealed

built-in Support agent loads no settings, and an external-CLI provider drops

CLAUDE_CONFIG_DIR and reads only the project's, so neither gets them. Slash

commands have no surface for a third party: they are the app's own.

The import file, checked against the app's own validator

Cherry Studio 2.0.14 installed, and its importer read out of the bundle

(Contents/Resources/app.asar, out/renderer/assets/mcp-uMWTyAhi.js):

const McpConfigSchema = object({ mcpServers: record(string(), McpServerConfigSchema) })
// McpServerConfigSchema = object({ id?, name?, type?, description?, url?, baseUrl?,
//   command?, registryUrl?, args?, env?, headers?, … }).strict()
// type ∈ stdio | sse | streamableHttp | inMemory

and the paste handler beside it names each server after its key unless the entry

carries a name. So the file deja writes —

{"mcpServers":{"deja":{"type":"stdio","command":"<abs path>","args":["mcp"]}}}

— validates, and the server arrives called deja.

The schema is .strict(), which is the part worth remembering: one key the app

does not know fails the whole import rather than being dropped. A test pins

deja's key set for that reason (#3674).

What still needs a person: the paste itself, and a Claude account in the app

before it writes any transcript at all.


deja reads this format and 42 others, and turns what it finds into memory your agents can search. See the harness matrix for what is wired where, or install it and search your own history.

Found this useful? Star deja-vu on GitHub.