Skip to content

feat: opencode + cursor-agent adapters (sqlite + transcripts)#28

Merged
0bserver07 merged 1 commit intomainfrom
wave3/opencode-cursor-agent
May 1, 2026
Merged

feat: opencode + cursor-agent adapters (sqlite + transcripts)#28
0bserver07 merged 1 commit intomainfrom
wave3/opencode-cursor-agent

Conversation

@0bserver07
Copy link
Copy Markdown
Owner

Summary

Two new beta-flagged source adapters land in this PR, taking the StackUnderflow supported provider count to 8 of 16 from the codeburn catalog (this PR alone).

  • OpenCode adapter (stackunderflow/adapters/opencode.py, gated by STACKUNDERFLOW_BETA_OPENCODE=1) — multi-DB discovery under $XDG_DATA_HOME/opencode/ (or ~/.local/share/opencode/); reads session / message / part tables; encodes session id as f"{db_basename}:{session.id}" to avoid cross-DB UUID collisions; collapses the 5-key OpenCode token shape onto our canonical 4-key shape (reasoning folds into output, cache.{read,write} map to cache_read/cache_create); embedded message.cost is stamped on record.raw["embedded_cost"] for parity checks.
  • Cursor Agent adapter (stackunderflow/adapters/cursor_agent.py, gated by STACKUNDERFLOW_BETA_CURSOR_AGENT=1) — hybrid: reads transcripts from ~/.cursor/projects/{project}/agent-transcripts/ in two formats (legacy text with user: / A: / [Tool call] markers, and Composer 2 JSONL), auto-detected by extension. An optional SQLite attribution DB (~/.cursor/ai-tracking/ai-code-tracking.db, conversation_summaries table) enriches the model name; missing DB falls back to "cursor-agent". Tokens are estimated as len(text)//4 and every Record carries record.raw["cost_source"]="estimated". seq is the byte offset of the line / record start so resume works across both formats.
  • Both pricers registered in infra/providers/__init__.py. OpenCodePricer and CursorAgentPricer route by model prefix: claude-*AnthropicPricer, gpt-* / codex-*OpenAIPricer, unknown → None (cost layer surfaces "no rate" rather than mispricing). OpenCodePricer.supports_per_message_tokens() is True; CursorAgentPricer is False (estimated tokens).
  • Both adapters default OFF. macOS-only for v1 on Cursor Agent; OpenCode is OS-portable via XDG_DATA_HOME. Adapter docstring + _beta_enabled wiring updated in stackunderflow/adapters/__init__.py.

Test plan

  • pytest tests/ -q586 passed, 2 skipped (baseline was 527; 59 new tests covering both adapters via targeted assertions and the inherited AdapterContract mixin, plus pricer routing tests for each provider).
  • ruff check stackunderflow/ --select E,F --exclude="*/build.py" — clean.
  • Beta-flag check: STACKUNDERFLOW_BETA_OPENCODE=1 STACKUNDERFLOW_BETA_CURSOR_AGENT=1 python -c "from stackunderflow.adapters import registered; print([a.name for a in registered()])"['claude', 'codex', 'opencode', 'cursor-agent'].
  • Manual smoke test against a real OpenCode install (deferred — synthetic SQLite fixture covers the schema).
  • Manual smoke test against a real Cursor Agent install (deferred — synthetic text + JSONL fixtures cover both formats).

CHANGELOG [Unreleased] ### Added updated with one entry covering both adapters, the SQLite + multi-format complexity, env vars, and the cost_source / embedded_cost stamps.

🤖 Generated with Claude Code

Adds two beta-flagged source adapters bringing supported provider count
to 8/16 from the codeburn catalog.

OpenCode (STACKUNDERFLOW_BETA_OPENCODE=1):
- Multi-DB discovery under $XDG_DATA_HOME/opencode/ (or
  ~/.local/share/opencode/) — scans for opencode*.db files.
- Reads session / message / part tables; joins parts onto messages to
  assemble content_text and tool names.
- Cross-DB session_id encoding: f"{db_basename}:{session.id}" so two
  DB files with overlapping inner UUIDs don't collide.
- Token mapping: input_tokens=tokens.input, output_tokens=tokens.output
  + tokens.reasoning, cache_read_tokens=tokens.cache.read,
  cache_create_tokens=tokens.cache.write.
- Embedded message.cost stamped onto record.raw["embedded_cost"] for
  parity checks against the recomputed value.

Cursor Agent (STACKUNDERFLOW_BETA_CURSOR_AGENT=1):
- Hybrid adapter reading transcripts from
  ~/.cursor/projects/{project}/agent-transcripts/.
- Two formats auto-detected by extension: legacy text (user: / A: /
  [Tool call] / [Tool result] markers) and Composer 2 JSONL.
- Tokens estimated as len(text)//4; every Record gets
  record.raw["cost_source"]="estimated".
- Optional SQLite metadata enrichment from
  ~/.cursor/ai-tracking/ai-code-tracking.db (conversation_summaries
  table) for the model name; falls back to "cursor-agent" literal.
- seq is byte offset for both formats.

Pricers (registered in infra/providers/__init__.py):
- OpenCodePricer: claude-* → AnthropicPricer, gpt-* / codex-* →
  OpenAIPricer, unknown → None. supports_per_message_tokens() = True.
- CursorAgentPricer: claude-* / gpt-* delegation, literal cursor-agent
  fallback returns None. supports_per_message_tokens() = False
  (estimated counts).

Both adapters default OFF. macOS-only for v1 on Cursor Agent; OpenCode
is OS-portable via XDG_DATA_HOME. codeburn-catalog §11 / §5.

Tests: 527 → 586 (59 new), all green. Lint clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@0bserver07 0bserver07 force-pushed the wave3/opencode-cursor-agent branch from a1a770a to 386a8bf Compare May 1, 2026 00:30
@0bserver07 0bserver07 merged commit fa89985 into main May 1, 2026
9 checks passed
0bserver07 added a commit that referenced this pull request May 1, 2026
Wave 3 closes the codeburn parity gap. Adds 12 new beta adapters across
five PRs covering all remaining providers in codeburn's catalog:

- #27 KiloCode, Roo Code (Cline parser reuse)
- #28 OpenCode (SQLite session/message/part), Cursor Agent (text+JSONL+SQLite)
- #29 Qwen, Gemini (JSONL with normalization)
- #30 Copilot (legacy + VS Code transcripts), Codeium (discovery stub),
  Continue (defensive SQLite parser)
- #31 Droid, Kiro, OpenClaw, Pi/OMP (JSONL family)

All beta-flagged off by default. 805 backend tests passing (was 527 in v0.4.0).

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant