# opencode-session

OpenCode SQLite adapter implementing the SessionSource protocol

## Installation

### From DevPi (private registry)

```bash
uv add opencode-session
```

> Requires the DevPi index to be configured in `~/.config/uv/uv.toml`:
> ```toml
> index-strategy = "unsafe-best-match"
>
> [[index]]
> name = "devpi"
> url = "https://pypi.registry.hochguertel.work/root/dev/+simple/"
> ```

### From source (monorepo)

```bash
git clone https://pastoral-oyster.pikapod.net/tools/ai-session-tools.git
cd ai-session-tools
uv sync --all-packages
```

## Usage

`OpenCodeSource` implements the `SessionSource` protocol for OpenCode
sessions. It reads from the SQLite database at
`~/.local/share/opencode/opencode.db`, joining the `session`, `message`, and
`part` tables to reconstruct full message text.

### Minimum supported version

This adapter requires **OpenCode schema migration 9+** (the
`session_message_cursor` migration). Earlier schema versions used `time`
instead of `time_created` columns and are not supported.

The adapter uses **defensive column access** — optional columns like `model`,
`slug`, `cost`, `tokens_*`, and `metadata` are queried only if they exist in
the current schema. Sessions loaded from older DBs will have these fields set
to `None` rather than causing an error.

Use `detect_schema_version()` to check the current DB's schema version:

```python
version = source.detect_schema_version()
print(f"Schema version: {version}")  # e.g. 20
```

The schema version is also included in `SessionExport.metadata["schema_version"]`
when loading sessions.

See [ADR 001](../../docs/adr/001-schema-version-handling.md) for the full
schema version handling strategy.

```python
from opencode_session import OpenCodeSource, OpenCodeConfig
from pathlib import Path

# Use default config (reads from ~/.local/share/opencode/opencode.db)
source = OpenCodeSource()

# Or override the database path
source = OpenCodeSource(OpenCodeConfig(db_path=Path("/custom/opencode.db")))

# List recent OpenCode sessions
sessions = source.list_sessions(limit=10)
for s in sessions:
    print(f"{s.session_id} — {s.title} ({s.message_count} messages)")

# Load a full session with all messages
session = source.load_session("abc-123")
for msg in session.messages:
    print(f"[{msg.role}] {msg.content[:80]}")

# Detect the current/active OpenCode session
session_id = source.detect_current_session()
```

## Development

```bash
# Run tests
uv run pytest libs/opencode-session/tests/

# Lint
uv run ruff check libs/opencode-session/

# Type check
uv run mypy
```

## License

MIT
