Conversation
Replaces the three-line summary with a richer report:
- Per-source file counts during the scan phase
- Live progress bar (TTY-only, 100ms throttle, sub-char eighth-block fill)
- Splits skipped into already_ingested / empty / duplicate_id so the
number is actionable instead of opaque
- Total messages ingested + elapsed time
Before:
Scanning 2 source(s)...
Found 4650 session file(s)
Ingested: 4313, Skipped: 337, Errors: 0
After:
Scanning 2 source(s)...
4,294 in /Users/me/.claude/projects
356 in /Users/me/.codex/sessions
Found 4,650 session file(s)
[████████████████████] 100% · 4,650/4,650
Ingested 4,313 session(s) (812,540 messages) in 47.2s
Skipped 337: 337 empty
Ingest Cursor session transcripts. Cursor records are {role, message}
with no `type` field, and content blocks share Claude Code's
{type:"text", text} shape, so the existing text extractors are reused.
Metadata absent from Cursor transcripts is derived after parsing:
- session id from the filename UUID
- start/end timestamps from file birthtime/mtime (Cursor stores none)
- project name from Cursor's encoded directory string (used verbatim)
`~/.cursor/projects` is not added to default sources; it stays opt-in
and is documented in the README along with the format's caveats.
Adds parser tests, an ingest regression test, fixtures, and docs.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Overlap with #18 (Cursor session support)Both PRs add a session format to
Applying keep-both to those two hunks: 139 tests pass, 0 fail, typecheck clean — 15 OpenCode tests and 8 Cursor tests green side by side. No semantic collision, which was the real risk given both add a format to the same parser. The detection predicates are mutually exclusive:
Worth knowing for whoever merges second: #18 changes Merge order doesn't matter. |
Overlap with #16 (detailed ingest output)A second overlap, alongside the #18 note above. #16 touches This one is slightly more involved than #18. Both PRs rewrite the same region of the
They are still compatible in substance: the OpenCode block runs before scanning and only extends Whoever merges second resolves it. No semantic conflict — #16 changes how ingest reports progress, #20 changes what there is to ingest. |
OpenCode source adapter ----------------------- OpenCode keeps sessions in a single SQLite database rather than one file per session, so it cannot be scanned like the Claude Code and Codex sources. An opt-in adapter exports each session into a staging directory of JSONL files during `ingest`, which the normal scanner then picks up — leaving dedup, --force, grouping, summarization and the web UI untouched. Three things the implementation has to work around, each found empirically against OpenCode 1.18.0: - `opencode session list` only reports sessions belonging to the current working directory's project, so no single cwd can enumerate them all. Sessions are enumerated from OpenCode's database; transcripts still come from `opencode export`, which works from anywhere. That keeps the fragile part (transcript shape) on the supported CLI, with only a five-column query touching the schema. - `opencode export` yields empty stdout over a pipe but is complete when redirected to a file, and a single export can run to megabytes. Both call sites go through one runToFile helper. - is_subagent was derived purely from Claude Code's /subagents/ path convention, so flat-staged files always read as 0. Formats that state it outright now win; Claude Code keeps path detection, because its parentSessionId also covers continuations and would otherwise misflag them. Projects are keyed off each session's working directory, so OpenCode and Claude Code work in the same repo on the same day lands in one journal entry. Configurable summary provider ----------------------------- Summaries were hardcoded to Claude Haiku via the Agent SDK. A summary_provider config block now selects any OpenAI-compatible endpoint, including a local llama.cpp server. Absent config keeps the previous behaviour. The Claude auth preflight is skipped for non-Anthropic providers, which authenticate with their own key and would otherwise be blocked on every run. Entries record the model that actually produced them. API keys are referenced by environment variable name and never stored in config. Reasoning models spend completion tokens before emitting content, so an exhausted budget fails loudly rather than writing an empty summary. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
b882612 to
2ad49f2
Compare
Adds OpenCode as a session source, and makes the model that writes journal summaries configurable. They're together because the first makes the second worth having — a local model is a reasonable choice once the corpus includes local-agent sessions.
Branches from
mainand depends on nothing else.OpenCode source adapter (opt-in)
OpenCode keeps sessions in a single SQLite database rather than one file per session, so it can't be scanned like the Claude Code and Codex sources. The adapter exports each session into a staging directory of JSONL files during
ingest, which the normal scanner picks up — dedup,--force, grouping, summarization and the web UI are untouched. Absent config, nothing changes.Three constraints worth a reviewer's attention, each found empirically against OpenCode 1.18.0 rather than assumed:
opencode session listis scoped to the current directory's project. 5356 bytes of output from/tmp, 0 bytes from inside another repo. No single cwd can enumerate every project, so sessions are enumerated from OpenCode's database. Transcripts still come fromopencode export, which works from anywhere — the fragile part (transcript shape) stays on the supported CLI, and only a five-column query touches the schema.opencode exportreturns empty stdout over a pipe but is complete when redirected to a file, and one export can run to megabytes. Both call sites go through a singlerunToFilehelper, covered by a 2MB regression test.is_subagentwas derived purely from Claude Code's/subagents/path convention, so flat-staged files always read as 0. Formats that state it outright now win. Claude Code deliberately keeps path detection: itsparentSessionIdalso covers continuations, so switching it to "has a parent" would misflag them.Projects are keyed off each session's working directory, so OpenCode and Claude Code work in the same repo on the same day lands in one journal entry rather than two disconnected ones.
Configurable summary provider
Summaries were hardcoded to Claude Haiku through the Agent SDK. A
summary_providerblock now selects any OpenAI-compatible endpoint. Absent config keeps existing behaviour exactly.Testing
131 tests pass, typecheck clean. Every change was written test-first; the OpenAI-compatible provider is tested against a real local server rather than a mocked
fetch.Exercised end-to-end against a live corpus: 96 OpenCode sessions across 16 projects, 61 subagents correctly flagged, summarized by a local Gemma served via llama.cpp. The token-budget error path fired on a real transcript and was resolved by raising the budget — that failure mode is not theoretical.
🤖 Generated with Claude Code