Skip to content

Add OpenCode (anomalyco/opencode) as the sixth searchable backend#30

Open
tony wants to merge 7 commits into
masterfrom
opencode-support
Open

Add OpenCode (anomalyco/opencode) as the sixth searchable backend#30
tony wants to merge 7 commits into
masterfrom
opencode-support

Conversation

@tony
Copy link
Copy Markdown
Owner

@tony tony commented May 30, 2026

Summary

  • Add OpenCode (the open-source coding agent, anomalyco/opencode, formerly sst/opencode) as an agentgrep backend, searchable from both the CLI and the MCP server.
  • Parse OpenCode's single SQLite store (opencode.db) by joining its relational session → message → part tables — each text-bearing part becomes a record whose kind is role-derived (user → prompt, else history), with the session title, working directory, and the message model/timestamp attached. This makes OpenCode the structural sibling of the existing Grok/Claude/Cursor SQLite adapters, not the JSONL backends.
  • Discover the database under the XDG data root (${XDG_DATA_HOME:-~/.local/share}/opencode/opencode.db), honouring the OPENCODE_DB override (an absolute path is searched from its parent).
  • Catalogue every on-disk OpenCode store — one searchable SQLite store plus documentary descriptors for the legacy per-file JSON layout, config, snapshots, the repo cache, logs, and tool output; auth.json is documented but never indexed.
  • Document the backend with a reference page, support-matrix entry, and storage-catalogue section.

Implements #29.

Changes by area

Search core (src/agentgrep/)

  • store_catalog.py: _OPENCODE_STORESopencode.db (primary chat, searched, SQLite) plus seven documentary rows; catalogue version bumped.
  • __init__.py: discover_opencode_sources (XDG data root + OPENCODE_DB override) and parse_opencode_db, which joins part → message → session and reuses open_readonly_sqlite.
  • stores.py / query/registry.py: opencode added to the agent-name literal and the query agent enum.
  • mcp/: opencode in the MCP agent literals and selectors, opencode.db_sqlite.v1 registered in KNOWN_ADAPTERS, and OpenCode named in the server instructions.

Docs

  • docs/backends/opencode.md: path layout, env overrides, and the session/message/part schema.
  • docs/backends/index.md, docs/dev/storage-catalog.md, README.md, docs/index.md: OpenCode added to the support matrix and agent lists.

Tests

  • tests/test_agentgrep.py: discovery for the default, XDG_DATA_HOME, and OPENCODE_DB layouts, an end-to-end search, and a NamedTuple + test_id parametrized parse matrix over part types, all driven by an inline-built opencode.db.

Design decisions

  • SQLite store discovered by filename, not a glob: the search path runs a ripgrep prefilter over each source's search_root, and ripgrep skips binary files — so a glob-discovered .db (which carries a search_root) would be pruned before the parser runs. Discovering via files=("opencode.db",) gives the source no search_root, routing it straight to direct_source_matches, where source_kind == "sqlite" always parses. This mirrors how the Grok and Cursor SQLite stores are discovered.
  • Three-table join, role-derived kind: OpenCode keeps prompt/reply text in part rows and the role on the message, so the parser joins part → message → session and derives kind from the message role — consistent with every other backend.
  • Text parts only by default: text, reasoning, and subtask parts carry searchable text; tool, file, snapshot, and step-marker parts are metadata and stay out of default search.
  • Working directory from session.directory: stored verbatim, so no hash/path decoding is needed; it is attached to each record's metadata.

Verification

Search the live OpenCode store (the parser-backed search, not raw grep, is the surface for SQLite stores):

$ uv run agentgrep search --agent opencode <term>

Restrict to user prompts:

$ uv run agentgrep search --agent opencode --type prompts <term>

Confirm no glob-based discovery remains for the SQLite store:

$ rg -n 'glob="opencode' src/agentgrep/store_catalog.py

Test plan

  • uv run ruff check . / uv run ruff format . — lint and format clean
  • uv run ty check — strict type checking passes
  • uv run pytest --reruns 0 — full suite green, including the new opencode tests
  • just build-docs — docs build clean (opencode backend page, support matrix, coverage grid)
  • test_discover_opencode_sources_default_xdg_location / _honours_xdg_data_home / _honours_opencode_db_override — discovery across layouts
  • test_search_opencode_sessions — user→prompt, assistant→history with model and directory
  • test_parse_opencode_part — every part type maps correctly or is skipped
  • End-to-end: agentgrep search --agent opencode returns records from the live opencode.db

Closes #29

tony added 7 commits May 30, 2026 20:30
why: Adding OpenCode (anomalyco/opencode, issue #29) as a searchable
backend starts by teaching every agent-name surface that "opencode"
exists. This commit is inert on its own — opencode is recognized but
has no stores yet, so discovery and parsing are never reached — which
isolates the literal change from the catalog/discovery/parser layers
that follow.

what:
- Add "opencode" to the AgentName literal in stores.py, __init__.py,
  and mcp/_library.py, plus the AgentSelector literal and
  AGENT_CHOICES.
- Add "opencode" to the five MCP model agent literals and the query
  registry's agent enum_values (and its docstring values line).
- Mention OpenCode in the MCP server-instruction header and trigger
  scope.
- Add "opencode" to the package description and keywords.
why: The catalogue is agentgrep's single source of truth for where
agent data lives and what shape it takes. OpenCode gets one searchable
store (its SQLite database) plus documentary descriptors for every
other on-disk artifact, so the catalogue stays a complete inventory
even for data agentgrep never searches.

what:
- Add _OPENCODE_OBSERVED_AT and the _OPENCODE_STORES tuple: opencode.db
  (PRIMARY_CHAT, searched, SQLite, adapter opencode.db_sqlite.v1) plus
  documentary rows for the legacy JSON layout, config, auth (PRIVATE
  credentials), snapshots, repos, logs, and tool output.
- Splice _OPENCODE_STORES into CATALOG, bump catalog_version to 10, and
  advance captured_at to the OpenCode observation date.
- Register opencode.db_sqlite.v1 in the MCP KNOWN_ADAPTERS tuple.
- Add "opencode" to the test-side KNOWN_AGENTS so the catalogue
  invariants cover the new rows.
why: With OpenCode in the catalogue, discovery needs to resolve its XDG
data directory and enumerate the SQLite databases there. OpenCode keeps
its store at opencode.db (or opencode-<channel>.db) under
${XDG_DATA_HOME}/opencode, and OPENCODE_DB can relocate it.

what:
- Add discover_opencode_sources, resolving the data root via
  resolve_env_root("XDG_DATA_HOME", ...) + the opencode segment and
  discovering opencode*.db. When OPENCODE_DB points at an absolute file,
  its parent directory becomes the search root (matching how OpenCode
  replaces the default database location).
- Wire the "opencode" branch into discover_sources.
why: Discovered opencode.db files need an adapter to turn their
relational rows into normalized search records. OpenCode keeps the
prompt and reply text in part rows, so the parser joins each part up to
its message (for role) and session (for title, working directory, and
timestamp).

what:
- Add parse_opencode_db plus _opencode_json_object and
  _opencode_part_text helpers, reusing open_readonly_sqlite and
  sqlite_table_names. A single part -> message -> session join emits one
  record per text-bearing part: text/reasoning carry their text and
  subtask its prompt; kind is role-derived (user -> prompt, else
  history); the session title, model, and unix-ms timestamp are
  attached, with the working directory in metadata. Degrades gracefully
  when tables are missing or the database is unreadable.
- Dispatch opencode.db_sqlite.v1 in iter_source_records and register it
  in ITER_SOURCE_RECORD_ADAPTERS.
why: The opencode backend needs the same test depth as the SQLite-based
Grok backend — discovery across its layouts and the per-part parse
behaviour — so regressions surface before release.

what:
- Add a helper that builds a minimal opencode.db (session/message/part)
  inline, following the Grok session_search test.
- Cover discovery at the default ~/.local/share/opencode location, the
  XDG_DATA_HOME override (with a decoy), and the absolute OPENCODE_DB
  override, plus an end-to-end search asserting user->prompt and
  assistant->history with the model and working directory attached.
- Add a NamedTuple + test_id parametrized parse matrix over part types:
  text/reasoning/subtask are searchable; tool, file, step markers, and
  empty text are skipped. Register "opencode" in the test-local
  AgentName.
why: Each backend gets a reference page documenting its layout and
record schema, and the support matrix and agent lists must name
OpenCode so readers can find it.

what:
- Add docs/backends/opencode.md: base path, env overrides, and the
  SQLite session/message/part schema with the searchable part types.
- Add an OpenCode card and toctree entry to the backend index and an
  OpenCode section to the storage-catalogue dev page.
- Name OpenCode in the README and docs landing agent lists.
- Extend the backend-grid and coverage-grid doc tests to cover opencode.
why: Record the new OpenCode backend for the unreleased version so
readers know agentgrep now searches anomalyco/opencode.

what:
- Add an "OpenCode backend (#29)" deliverable under What's new for the
  unreleased 0.1.0a8 section, describing the single SQLite store, the
  part-join read path, the env overrides, and the catalogued
  documentary stores.
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.

backend: add OpenCode (anomalyco/opencode) as a searchable backend

1 participant