@genspark/opencode-conversation-archiver
v0.3.2
Published
Archive OpenCode conversations as readable Markdown in a git repository
Readme
OpenCode conversation archiver
An OpenCode server plugin that archives every top-level conversation after each completed turn. It keeps one Markdown file per session in a git repository and records only user-visible text:
- user text parts;
- assistant text parts;
- context-compaction boundaries.
Reasoning, tool calls, tool outputs, file URLs, synthetic text, ignored text, and child/subagent sessions are excluded.
Install
opencode plugin -g @genspark/[email protected]Completely quit and restart OpenCode after installation. The first completed
turn is archived locally under ~/opencode-conversations with no additional
setup.
See the User Guide for Second Brain connection, sharing one remote repository with Claude Code, management commands, privacy details, upgrades, troubleshooting, and removal.
Install From Source
Build the package:
cd toolkits/opencode-conversation-archiver
npm install
npm run buildRegister the built server entry in opencode.json:
{
"$schema": "https://opencode.ai/config.json",
"plugin": [
"./toolkits/opencode-conversation-archiver/dist/server.js"
]
}The path is relative to the config file. Quit and restart OpenCode after adding
or updating a plugin because configuration is loaded only at startup. To get
the immediate /new title reset when installing from source, also register
./toolkits/opencode-conversation-archiver/dist/tui.js in the plugin array of
tui.json. The opencode plugin installer registers both npm entry points
automatically.
For package distribution, the package exposes OpenCode's native ./server
entry and declares compatibility with OpenCode 1.17.18 through <2.
Default Behavior
- Archive repo:
~/opencode-conversations - Runtime state:
~/.config/opencode/conversation-archiver/ - Mode:
auto - Trigger: canonical
session.statusevent withstatus.type === "idle" - Auto mode: write, commit, and launch a detached push after every changed turn
- Manual mode: write locally without commit or push
- No remote: commits remain local and push failure never interrupts OpenCode
Terminal Notifications
The plugin emits OSC 9999 genterm-notify sequences (base64 JSON) to the
controlling terminal, wrapped in tmux passthrough under tmux. Each payload
carries the pane's tmux coordinates
(tmux.{socket,session,windowId,windowIndex,windowName}) so GenTerminal can
join tmux.session against its managed session record. Since 0.3.2 the TUI
plugin owns title-changing notifications: it reports each distinct
non-placeholder session.updated title and observes the native session.new
command to immediately report the directory basename (body: "New
conversation"), mirroring the cc plugin's /clear reset, while returning
control to OpenCode's native handler. The server still reports each archived
turn for inbox, activity, and unread tracking, but uses a dedicated
<session-id>:idle source ID and the non-renaming OpenCode placeholder so a
delayed old archive cannot restore an old title after /new. The command keeps
OpenCode's native command-palette and /new//clear slash metadata.
OPENCODE_ARCHIVE_NO_NOTIFY=1
disables all notifications; OPENCODE_ARCHIVE_NOTIFY_TTY=<file> redirects the
raw sequences to a file (the seam the tests use).
The server event hook owns a per-session archive queue because OpenCode
intentionally does not await plugin event callbacks. The TUI owns one ordered
notification queue so /new and subsequent model titles cannot reorder.
Both entry points drain their pending work during a clean shutdown. Git and
tmux commands use asynchronous child processes so archive work does not block
OpenCode's event loop.
Management Tools
The plugin exposes these native tools:
| Tool | Purpose |
| --- | --- |
| conversation_archive_status | Mode, repo, redacted remote, commits, pending changes |
| conversation_archive_doctor | Read-only dependency, repo, remote-auth, and activity diagnosis |
| conversation_archive_current | Archive the current live session immediately |
| conversation_archive_backfill | Archive all top-level sessions visible in the current OpenCode project |
Users can ask OpenCode to invoke these tools naturally, for example: "diagnose my conversation archive". The two write tools request explicit permission. Credential-bearing and repository-management operations are CLI-only so their arguments never enter an LLM-visible conversation or tool call.
OpenCode 1.17.18 does not expose a direct, no-LLM server slash-command handler.
Markdown commands always enter the prompt pipeline and command.execute.before
cannot short-circuit it. For deterministic automation, use the bundled CLI:
opencode-conversation-archiver status
opencode-conversation-archiver doctor
opencode-conversation-archiver manual
opencode-conversation-archiver auto
opencode-conversation-archiver upload
opencode-conversation-archiver repo ~/Documents/opencode-conversations
opencode-conversation-archiver connect '<sb-connect-link>'backfill and current remain live tools because they require the injected
OpenCode SDK and current session identity.
Second Brain
The CLI connect supports:
- A one-time
.../sb-connect/<code>URL. - No arguments, using
GSK_API_KEYor the credential created bygsk login. - An explicit HTTP(S) git remote and token.
The git credential is stored at
~/.config/opencode/conversation-archiver/git-credentials with mode 0600,
outside the archive repository. The remote URL shown by status, doctor, and logs
is credential-redacted. Connected archives are confined to the opencode/
subdirectory by default with sparse checkout.
Configuration
Runtime configuration:
{
"mode": "auto",
"repo": "/home/you/opencode-conversations",
"subdir": "opencode"
}Environment overrides:
OPENCODE_ARCHIVE_MODEOPENCODE_ARCHIVE_REPOOPENCODE_ARCHIVE_NO_NOTIFY
For isolated tests or embedded installations, pass appDir as plugin options:
{
"plugin": [
[
"@genspark/opencode-conversation-archiver",
{ "appDir": "/custom/runtime-state" }
]
]
}Preservation Guarantees
- Message and compaction part IDs make overlapping/repeated idle events idempotent.
- Cumulative text for an existing message ID can grow without duplicating a turn.
- Session state is append-only and outside the git repo.
- Title changes rename the archive while preserving git history where possible.
- A collision index prevents one session from overwriting another.
- A never-shrink guard diverts to a suffixed file if disk content is richer than local state, preserving both copies instead of dropping turns.
- Archive and push operations use cross-process lock directories.
- Remote pushes serialize, pull with rebase, and abort a failed in-progress rebase.
Development
npm test
npm run typecheck
npm run build
npm pack --dry-runLocal End-to-End Verification
The release was verified against a real local OpenCode 1.17.18 process, not
only mocked SDK tests. An isolated opencode run session using Claude Haiku
completed with ARCHIVER_E2E_OK; the plugin then created a titled Markdown
archive containing one user and one assistant turn and committed it to the
isolated archive git repository. The temporary plugin config and archive paths
were supplied through OPENCODE_CONFIG_CONTENT, appDir, and
OPENCODE_ARCHIVE_REPO, so the normal OpenCode configuration and archive were
not modified.
