claude-opencode-sessions 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- claude_opencode_sessions-0.1.0/.gitignore +12 -0
- claude_opencode_sessions-0.1.0/CHANGELOG.rst +24 -0
- claude_opencode_sessions-0.1.0/LICENSE +21 -0
- claude_opencode_sessions-0.1.0/PKG-INFO +287 -0
- claude_opencode_sessions-0.1.0/README.rst +258 -0
- claude_opencode_sessions-0.1.0/pyproject.toml +90 -0
- claude_opencode_sessions-0.1.0/src/claude_opencode_sessions/__init__.py +10 -0
- claude_opencode_sessions-0.1.0/src/claude_opencode_sessions/__main__.py +3 -0
- claude_opencode_sessions-0.1.0/src/claude_opencode_sessions/cli.py +196 -0
- claude_opencode_sessions-0.1.0/src/claude_opencode_sessions/cli_backend.py +140 -0
- claude_opencode_sessions-0.1.0/src/claude_opencode_sessions/errors.py +39 -0
- claude_opencode_sessions-0.1.0/src/claude_opencode_sessions/import_command.py +216 -0
- claude_opencode_sessions-0.1.0/src/claude_opencode_sessions/importer.py +414 -0
- claude_opencode_sessions-0.1.0/src/claude_opencode_sessions/locate.py +90 -0
- claude_opencode_sessions-0.1.0/src/claude_opencode_sessions/models.py +96 -0
- claude_opencode_sessions-0.1.0/src/claude_opencode_sessions/parse.py +224 -0
- claude_opencode_sessions-0.1.0/src/claude_opencode_sessions/py.typed +0 -0
- claude_opencode_sessions-0.1.0/src/claude_opencode_sessions/render.py +169 -0
- claude_opencode_sessions-0.1.0/src/claude_opencode_sessions/scope.py +160 -0
- claude_opencode_sessions-0.1.0/src/claude_opencode_sessions/service.py +149 -0
- claude_opencode_sessions-0.1.0/src/claude_opencode_sessions/sqlite_backend.py +249 -0
- claude_opencode_sessions-0.1.0/src/claude_opencode_sessions/tests/__init__.py +0 -0
- claude_opencode_sessions-0.1.0/src/claude_opencode_sessions/tests/conftest.py +314 -0
- claude_opencode_sessions-0.1.0/src/claude_opencode_sessions/tests/fixtures/schema_1x.sql +76 -0
- claude_opencode_sessions-0.1.0/src/claude_opencode_sessions/tests/fixtures/schema_2x.sql +37 -0
- claude_opencode_sessions-0.1.0/src/claude_opencode_sessions/tests/test_cli.py +140 -0
- claude_opencode_sessions-0.1.0/src/claude_opencode_sessions/tests/test_cli_backend.py +116 -0
- claude_opencode_sessions-0.1.0/src/claude_opencode_sessions/tests/test_importer.py +504 -0
- claude_opencode_sessions-0.1.0/src/claude_opencode_sessions/tests/test_launcher.py +47 -0
- claude_opencode_sessions-0.1.0/src/claude_opencode_sessions/tests/test_locate.py +62 -0
- claude_opencode_sessions-0.1.0/src/claude_opencode_sessions/tests/test_parse.py +120 -0
- claude_opencode_sessions-0.1.0/src/claude_opencode_sessions/tests/test_render.py +107 -0
- claude_opencode_sessions-0.1.0/src/claude_opencode_sessions/tests/test_scope.py +121 -0
- claude_opencode_sessions-0.1.0/src/claude_opencode_sessions/tests/test_sqlite_backend.py +157 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
=========
|
|
2
|
+
Changelog
|
|
3
|
+
=========
|
|
4
|
+
All notable changes to this project are documented here. Versions apply to
|
|
5
|
+
both the PyPI package ``claude-opencode-sessions`` and the Claude Code plugin
|
|
6
|
+
``opencode-sessions``.
|
|
7
|
+
|
|
8
|
+
0.1.0
|
|
9
|
+
=====
|
|
10
|
+
2026-09-30
|
|
11
|
+
|
|
12
|
+
- First release.
|
|
13
|
+
- ``import``, ``list`` and ``doctor`` commands (``claude-opencode-sessions``).
|
|
14
|
+
- Reads opencode's SQLite database directly, read-only: opencode 1.x
|
|
15
|
+
(>= 1.2, ``session``/``message``/``part``) and 2.x
|
|
16
|
+
(``session_v2``/``session_message``), with ``opencode session list`` /
|
|
17
|
+
``opencode export`` as a fallback.
|
|
18
|
+
- Scoping mirrors Claude Code's ``/resume``: current worktree by default,
|
|
19
|
+
``--worktrees``, ``--all``.
|
|
20
|
+
- Claude Code plugin ``opencode-sessions`` with ``/opencode-sessions:import``.
|
|
21
|
+
It imports the repository's opencode sessions as Claude Code conversations,
|
|
22
|
+
so they open with ``/resume``. It runs in a ``UserPromptExpansion`` hook
|
|
23
|
+
without a model call, and never creates duplicates (content-addressed
|
|
24
|
+
session ids, plus ``(1)``, ``(2)``… suffixes for changed sessions).
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Artur Barseghyan
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: claude-opencode-sessions
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Import your opencode sessions for the current repository into Claude Code, so they open with /resume.
|
|
5
|
+
Project-URL: Homepage, https://github.com/barseghyanartur/claude-plugin-opencode-sessions
|
|
6
|
+
Project-URL: Repository, https://github.com/barseghyanartur/claude-plugin-opencode-sessions
|
|
7
|
+
Project-URL: Issues, https://github.com/barseghyanartur/claude-plugin-opencode-sessions/issues
|
|
8
|
+
Project-URL: Changelog, https://github.com/barseghyanartur/claude-plugin-opencode-sessions/blob/main/CHANGELOG.rst
|
|
9
|
+
Author-email: Artur Barseghyan <artur.barseghyan@gmail.com>
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: agents,claude,claude-code,llm,opencode,plugin,sessions
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
14
|
+
Classifier: Environment :: Console
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: Operating System :: MacOS
|
|
17
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
25
|
+
Classifier: Topic :: Software Development
|
|
26
|
+
Classifier: Typing :: Typed
|
|
27
|
+
Requires-Python: >=3.10
|
|
28
|
+
Description-Content-Type: text/x-rst
|
|
29
|
+
|
|
30
|
+
========================
|
|
31
|
+
claude-opencode-sessions
|
|
32
|
+
========================
|
|
33
|
+
Bring your `opencode <https://opencode.ai>`_ sessions into **Claude Code**.
|
|
34
|
+
|
|
35
|
+
In Claude Code, run ``/opencode-sessions:import``. Your opencode sessions for
|
|
36
|
+
the current repository become regular Claude Code conversations, titled
|
|
37
|
+
``[opencode] …``. Then use ``/resume`` (or ``claude --resume``) to browse
|
|
38
|
+
them, and press Enter to continue one.
|
|
39
|
+
|
|
40
|
+
- **No model call, no tokens.** The import runs in a Claude Code hook and
|
|
41
|
+
prints its report directly. Tokens are only spent once you resume a session
|
|
42
|
+
and start typing, just like any other conversation.
|
|
43
|
+
- **No duplicates, ever.** A session whose content hasn't changed since its
|
|
44
|
+
last import is skipped. A session that changed gets a new copy with the
|
|
45
|
+
next suffix: ``Title``, ``Title (1)``, ``Title (2)``…. Existing files are
|
|
46
|
+
never overwritten.
|
|
47
|
+
- **Only this repository.** Scoping works like ``/resume``: the current git
|
|
48
|
+
worktree by default, or ``--worktrees`` for all worktrees of the repo.
|
|
49
|
+
- Reads opencode's SQLite database **read-only**: opencode 1.2+ (1.x and 2.x
|
|
50
|
+
layouts), with ``opencode export`` as a fallback.
|
|
51
|
+
- Python 3.10+, standard library only. No network access.
|
|
52
|
+
- The same package also works as a command-line tool:
|
|
53
|
+
``claude-opencode-sessions`` runs ``import`` and ``list`` in the terminal.
|
|
54
|
+
|
|
55
|
+
.. contents:: Table of contents
|
|
56
|
+
:depth: 2
|
|
57
|
+
:local:
|
|
58
|
+
|
|
59
|
+
Install
|
|
60
|
+
=======
|
|
61
|
+
As a Claude Code plugin
|
|
62
|
+
-----------------------
|
|
63
|
+
.. code-block:: sh
|
|
64
|
+
|
|
65
|
+
claude plugin marketplace add barseghyanartur/claude-plugin-opencode-sessions
|
|
66
|
+
claude plugin install opencode-sessions@barseghyanartur
|
|
67
|
+
|
|
68
|
+
Or inside a Claude Code session:
|
|
69
|
+
|
|
70
|
+
.. code-block:: text
|
|
71
|
+
|
|
72
|
+
/plugin marketplace add barseghyanartur/claude-plugin-opencode-sessions
|
|
73
|
+
/plugin install opencode-sessions@barseghyanartur
|
|
74
|
+
|
|
75
|
+
Update later with ``claude plugin update opencode-sessions@barseghyanartur``,
|
|
76
|
+
or turn on auto-update for the marketplace under **Marketplaces** in
|
|
77
|
+
``/plugin``.
|
|
78
|
+
|
|
79
|
+
The plugin runs the code bundled in the plugin itself, so it does not need
|
|
80
|
+
the PyPI package. It needs a Python >= 3.10 on ``PATH`` (``python3``,
|
|
81
|
+
``python3.1x``) or `uv <https://docs.astral.sh/uv/>`_. Note that
|
|
82
|
+
``/usr/bin/python3`` on macOS is 3.9. ``brew install python`` or
|
|
83
|
+
``uv python install 3.12`` fixes that, or set ``OCS_PYTHON=/path/to/python``.
|
|
84
|
+
|
|
85
|
+
As a command-line tool
|
|
86
|
+
----------------------
|
|
87
|
+
.. code-block:: sh
|
|
88
|
+
|
|
89
|
+
uv tool install claude-opencode-sessions # or: pipx install claude-opencode-sessions
|
|
90
|
+
uvx claude-opencode-sessions list # one-off run without installing
|
|
91
|
+
|
|
92
|
+
Usage in Claude Code
|
|
93
|
+
====================
|
|
94
|
+
.. code-block:: text
|
|
95
|
+
|
|
96
|
+
/opencode-sessions:import # sessions of the current worktree
|
|
97
|
+
/opencode-sessions:import --worktrees # every worktree of the repository
|
|
98
|
+
/opencode-sessions:import --dry-run # show what would happen
|
|
99
|
+
/opencode-sessions:import --with-tool-output # include (truncated) tool output
|
|
100
|
+
/resume # pick an "[opencode] …" session
|
|
101
|
+
|
|
102
|
+
Example report:
|
|
103
|
+
|
|
104
|
+
.. code-block:: text
|
|
105
|
+
|
|
106
|
+
opencode → Claude Code import · git worktree /Users/me/repos/brrn
|
|
107
|
+
into ~/.claude/projects/-Users-me-repos-brrn
|
|
108
|
+
|
|
109
|
+
imported [opencode] Fixing stalled make armdict-fetch-hy build (1)
|
|
110
|
+
unchanged [opencode] HTTP Request/Response Log Analysis for brrn.ru …
|
|
111
|
+
|
|
112
|
+
1 imported, 1 unchanged · skipped: 1 sub-agent
|
|
113
|
+
Open them with /resume — titles start with "[opencode]".
|
|
114
|
+
|
|
115
|
+
What an imported conversation contains
|
|
116
|
+
--------------------------------------
|
|
117
|
+
- One transcript per opencode session, in Claude Code's own session folder
|
|
118
|
+
for this project (``~/.claude/projects/<project>/<uuid>.jsonl``).
|
|
119
|
+
- User messages and assistant replies as text. Each tool call becomes one
|
|
120
|
+
line (``→ bash: `pytest -x```), and its output is left out unless you pass
|
|
121
|
+
``--with-tool-output``. Reasoning, synthetic and sub-agent content are left
|
|
122
|
+
out.
|
|
123
|
+
- A first line saying which opencode session it came from. Claude reads this
|
|
124
|
+
when you resume, so it knows the context.
|
|
125
|
+
|
|
126
|
+
How duplicates are prevented
|
|
127
|
+
----------------------------
|
|
128
|
+
The Claude session id is ``uuid5(opencode session id + hash of the converted
|
|
129
|
+
content)``. Importing unchanged content maps to a file that already exists,
|
|
130
|
+
so it is skipped. The first line of each imported file records the opencode
|
|
131
|
+
session id, content hash and suffix, so changed content gets the next free
|
|
132
|
+
suffix (the highest existing one + 1). New files are written under a
|
|
133
|
+
temporary name and hard-linked into place, which can't overwrite an existing
|
|
134
|
+
file. Continuing an imported conversation in Claude doesn't count as a
|
|
135
|
+
change: only the opencode side is compared.
|
|
136
|
+
|
|
137
|
+
Claude Code's transcript format is internal and may change between Claude
|
|
138
|
+
Code versions. The importer writes only a minimal, stable subset of it.
|
|
139
|
+
|
|
140
|
+
Command line
|
|
141
|
+
============
|
|
142
|
+
The same package works in the terminal:
|
|
143
|
+
|
|
144
|
+
.. code-block:: sh
|
|
145
|
+
|
|
146
|
+
claude-opencode-sessions import --dry-run # same as /opencode-sessions:import
|
|
147
|
+
claude-opencode-sessions import --worktrees
|
|
148
|
+
claude-opencode-sessions list # sessions in scope, newest first
|
|
149
|
+
claude-opencode-sessions list --all --format json
|
|
150
|
+
claude-opencode-sessions doctor # environment + database report
|
|
151
|
+
|
|
152
|
+
Hidden by default: archived sessions (``--include-archived``) and sub-agent
|
|
153
|
+
sessions (``--include-subagents``).
|
|
154
|
+
|
|
155
|
+
Scoping
|
|
156
|
+
-------
|
|
157
|
+
.. list-table::
|
|
158
|
+
:header-rows: 1
|
|
159
|
+
:widths: 12 20 50 18
|
|
160
|
+
|
|
161
|
+
* - Mode
|
|
162
|
+
- Flag
|
|
163
|
+
- Sessions whose directory is…
|
|
164
|
+
- ``/resume`` equivalent
|
|
165
|
+
* - worktree
|
|
166
|
+
- *(default)*
|
|
167
|
+
- inside the current git worktree (its top-level directory or below).
|
|
168
|
+
Outside git: the current directory or below
|
|
169
|
+
- default view
|
|
170
|
+
* - repo
|
|
171
|
+
- ``--worktrees``
|
|
172
|
+
- inside any worktree of the repository, plus sessions of the same
|
|
173
|
+
opencode project whose worktree was deleted
|
|
174
|
+
- ``Ctrl+W``
|
|
175
|
+
* - all
|
|
176
|
+
- ``--all`` (``list`` only)
|
|
177
|
+
- anywhere
|
|
178
|
+
- ``Ctrl+A``
|
|
179
|
+
|
|
180
|
+
Configuration
|
|
181
|
+
-------------
|
|
182
|
+
.. list-table::
|
|
183
|
+
:header-rows: 1
|
|
184
|
+
:widths: 30 70
|
|
185
|
+
|
|
186
|
+
* - Variable
|
|
187
|
+
- Purpose
|
|
188
|
+
* - ``OPENCODE_SESSIONS_DB``
|
|
189
|
+
- Path to ``opencode.db``. Default:
|
|
190
|
+
``$XDG_DATA_HOME/opencode/opencode.db``, then
|
|
191
|
+
``~/.local/share/opencode/opencode.db``, then ``opencode db path``
|
|
192
|
+
* - ``OCS_PYTHON``
|
|
193
|
+
- Interpreter for the plugin launcher (``scripts/opencode-sessions``)
|
|
194
|
+
|
|
195
|
+
Every command also accepts ``--db PATH`` and ``--backend auto|sqlite|cli``.
|
|
196
|
+
|
|
197
|
+
How it works
|
|
198
|
+
============
|
|
199
|
+
- **sqlite** (default): opens ``opencode.db`` with ``mode=ro``. On 2.x it
|
|
200
|
+
reads ``session_v2`` + ``session_message``. On 1.x it reads ``session`` +
|
|
201
|
+
``message`` + ``part``. When both exist in the same file, sessions are
|
|
202
|
+
merged by id (2.x wins), and messages fall back to the 1.x tables per
|
|
203
|
+
session.
|
|
204
|
+
- **cli** (fallback): used when the database is missing, locked or not
|
|
205
|
+
recognised, or when more than 20% of a session's messages can't be parsed.
|
|
206
|
+
It runs ``opencode session list --format json`` and
|
|
207
|
+
``opencode export <id>``.
|
|
208
|
+
|
|
209
|
+
See `docs/design.md <docs/design.md>`_ for the design and
|
|
210
|
+
`docs/schema-notes.md <docs/schema-notes.md>`_ for the tables and fields
|
|
211
|
+
that are read.
|
|
212
|
+
|
|
213
|
+
Development
|
|
214
|
+
===========
|
|
215
|
+
.. code-block:: sh
|
|
216
|
+
|
|
217
|
+
make install # uv sync (creates .venv with dev tools)
|
|
218
|
+
make test # pytest (tests live in src/claude_opencode_sessions/tests)
|
|
219
|
+
make check # ruff + mypy --strict + pytest + manifest validation
|
|
220
|
+
make run ARGS="list --all"
|
|
221
|
+
make dev # claude --plugin-dir . (live plugin; /reload-plugins after edits)
|
|
222
|
+
make install-local / make uninstall-local
|
|
223
|
+
|
|
224
|
+
Publishing
|
|
225
|
+
==========
|
|
226
|
+
One version number covers both the PyPI package and the plugin. It lives in
|
|
227
|
+
``pyproject.toml`` (managed with ``uv version``), and ``make bump`` copies it
|
|
228
|
+
into ``.claude-plugin/plugin.json``.
|
|
229
|
+
|
|
230
|
+
How each channel works
|
|
231
|
+
----------------------
|
|
232
|
+
- **Claude Code plugin:** there is no registry upload. This repository *is*
|
|
233
|
+
the marketplace (``.claude-plugin/marketplace.json``), so pushing to
|
|
234
|
+
``main`` publishes. Users only receive an update when the ``version`` in
|
|
235
|
+
``plugin.json`` changes, so every release must bump it.
|
|
236
|
+
- **PyPI:** ``claude-opencode-sessions`` is uploaded with ``twine``, using
|
|
237
|
+
the credentials in ``~/.pypirc``, so you don't type them each time.
|
|
238
|
+
- **Anthropic community marketplace (optional):** a reviewed listing in
|
|
239
|
+
``anthropics/claude-plugins-community``, pinned to a commit. Submit through
|
|
240
|
+
the form that ``make submit`` prints the details for.
|
|
241
|
+
|
|
242
|
+
One-time setup
|
|
243
|
+
--------------
|
|
244
|
+
Put API tokens for PyPI and TestPyPI in ``~/.pypirc`` (``chmod 600``):
|
|
245
|
+
|
|
246
|
+
.. code-block:: ini
|
|
247
|
+
|
|
248
|
+
[distutils]
|
|
249
|
+
index-servers =
|
|
250
|
+
pypi
|
|
251
|
+
testpypi
|
|
252
|
+
|
|
253
|
+
[pypi]
|
|
254
|
+
username = __token__
|
|
255
|
+
password = pypi-...
|
|
256
|
+
|
|
257
|
+
[testpypi]
|
|
258
|
+
repository = https://test.pypi.org/legacy/
|
|
259
|
+
username = __token__
|
|
260
|
+
password = pypi-...
|
|
261
|
+
|
|
262
|
+
Release checklist
|
|
263
|
+
-----------------
|
|
264
|
+
.. code-block:: sh
|
|
265
|
+
|
|
266
|
+
make bump BUMP=minor # or BUMP=patch|major, or VERSION=0.2.0
|
|
267
|
+
$EDITOR CHANGELOG.rst # add the release notes
|
|
268
|
+
git commit -am "Release $(uv version --short)"
|
|
269
|
+
make test-release # check, build, twine check, upload to TestPyPI
|
|
270
|
+
make release # the same, upload to PyPI
|
|
271
|
+
make tag # tag vX.Y.Z and push (plugin users get the update)
|
|
272
|
+
|
|
273
|
+
For the community marketplace, run ``make submit`` after the release and
|
|
274
|
+
file the form.
|
|
275
|
+
|
|
276
|
+
Troubleshooting
|
|
277
|
+
===============
|
|
278
|
+
If ``/opencode-sessions:import`` makes Claude answer instead of printing a
|
|
279
|
+
report, the hook didn't run. Check the Python requirement above and run
|
|
280
|
+
``claude-opencode-sessions doctor``, or
|
|
281
|
+
``sh ~/.claude/plugins/…/scripts/opencode-sessions doctor``. It shows the
|
|
282
|
+
Python in use, the database path, the detected layout, row counts and the
|
|
283
|
+
scope roots for the current directory.
|
|
284
|
+
|
|
285
|
+
License
|
|
286
|
+
=======
|
|
287
|
+
MIT
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
========================
|
|
2
|
+
claude-opencode-sessions
|
|
3
|
+
========================
|
|
4
|
+
Bring your `opencode <https://opencode.ai>`_ sessions into **Claude Code**.
|
|
5
|
+
|
|
6
|
+
In Claude Code, run ``/opencode-sessions:import``. Your opencode sessions for
|
|
7
|
+
the current repository become regular Claude Code conversations, titled
|
|
8
|
+
``[opencode] …``. Then use ``/resume`` (or ``claude --resume``) to browse
|
|
9
|
+
them, and press Enter to continue one.
|
|
10
|
+
|
|
11
|
+
- **No model call, no tokens.** The import runs in a Claude Code hook and
|
|
12
|
+
prints its report directly. Tokens are only spent once you resume a session
|
|
13
|
+
and start typing, just like any other conversation.
|
|
14
|
+
- **No duplicates, ever.** A session whose content hasn't changed since its
|
|
15
|
+
last import is skipped. A session that changed gets a new copy with the
|
|
16
|
+
next suffix: ``Title``, ``Title (1)``, ``Title (2)``…. Existing files are
|
|
17
|
+
never overwritten.
|
|
18
|
+
- **Only this repository.** Scoping works like ``/resume``: the current git
|
|
19
|
+
worktree by default, or ``--worktrees`` for all worktrees of the repo.
|
|
20
|
+
- Reads opencode's SQLite database **read-only**: opencode 1.2+ (1.x and 2.x
|
|
21
|
+
layouts), with ``opencode export`` as a fallback.
|
|
22
|
+
- Python 3.10+, standard library only. No network access.
|
|
23
|
+
- The same package also works as a command-line tool:
|
|
24
|
+
``claude-opencode-sessions`` runs ``import`` and ``list`` in the terminal.
|
|
25
|
+
|
|
26
|
+
.. contents:: Table of contents
|
|
27
|
+
:depth: 2
|
|
28
|
+
:local:
|
|
29
|
+
|
|
30
|
+
Install
|
|
31
|
+
=======
|
|
32
|
+
As a Claude Code plugin
|
|
33
|
+
-----------------------
|
|
34
|
+
.. code-block:: sh
|
|
35
|
+
|
|
36
|
+
claude plugin marketplace add barseghyanartur/claude-plugin-opencode-sessions
|
|
37
|
+
claude plugin install opencode-sessions@barseghyanartur
|
|
38
|
+
|
|
39
|
+
Or inside a Claude Code session:
|
|
40
|
+
|
|
41
|
+
.. code-block:: text
|
|
42
|
+
|
|
43
|
+
/plugin marketplace add barseghyanartur/claude-plugin-opencode-sessions
|
|
44
|
+
/plugin install opencode-sessions@barseghyanartur
|
|
45
|
+
|
|
46
|
+
Update later with ``claude plugin update opencode-sessions@barseghyanartur``,
|
|
47
|
+
or turn on auto-update for the marketplace under **Marketplaces** in
|
|
48
|
+
``/plugin``.
|
|
49
|
+
|
|
50
|
+
The plugin runs the code bundled in the plugin itself, so it does not need
|
|
51
|
+
the PyPI package. It needs a Python >= 3.10 on ``PATH`` (``python3``,
|
|
52
|
+
``python3.1x``) or `uv <https://docs.astral.sh/uv/>`_. Note that
|
|
53
|
+
``/usr/bin/python3`` on macOS is 3.9. ``brew install python`` or
|
|
54
|
+
``uv python install 3.12`` fixes that, or set ``OCS_PYTHON=/path/to/python``.
|
|
55
|
+
|
|
56
|
+
As a command-line tool
|
|
57
|
+
----------------------
|
|
58
|
+
.. code-block:: sh
|
|
59
|
+
|
|
60
|
+
uv tool install claude-opencode-sessions # or: pipx install claude-opencode-sessions
|
|
61
|
+
uvx claude-opencode-sessions list # one-off run without installing
|
|
62
|
+
|
|
63
|
+
Usage in Claude Code
|
|
64
|
+
====================
|
|
65
|
+
.. code-block:: text
|
|
66
|
+
|
|
67
|
+
/opencode-sessions:import # sessions of the current worktree
|
|
68
|
+
/opencode-sessions:import --worktrees # every worktree of the repository
|
|
69
|
+
/opencode-sessions:import --dry-run # show what would happen
|
|
70
|
+
/opencode-sessions:import --with-tool-output # include (truncated) tool output
|
|
71
|
+
/resume # pick an "[opencode] …" session
|
|
72
|
+
|
|
73
|
+
Example report:
|
|
74
|
+
|
|
75
|
+
.. code-block:: text
|
|
76
|
+
|
|
77
|
+
opencode → Claude Code import · git worktree /Users/me/repos/brrn
|
|
78
|
+
into ~/.claude/projects/-Users-me-repos-brrn
|
|
79
|
+
|
|
80
|
+
imported [opencode] Fixing stalled make armdict-fetch-hy build (1)
|
|
81
|
+
unchanged [opencode] HTTP Request/Response Log Analysis for brrn.ru …
|
|
82
|
+
|
|
83
|
+
1 imported, 1 unchanged · skipped: 1 sub-agent
|
|
84
|
+
Open them with /resume — titles start with "[opencode]".
|
|
85
|
+
|
|
86
|
+
What an imported conversation contains
|
|
87
|
+
--------------------------------------
|
|
88
|
+
- One transcript per opencode session, in Claude Code's own session folder
|
|
89
|
+
for this project (``~/.claude/projects/<project>/<uuid>.jsonl``).
|
|
90
|
+
- User messages and assistant replies as text. Each tool call becomes one
|
|
91
|
+
line (``→ bash: `pytest -x```), and its output is left out unless you pass
|
|
92
|
+
``--with-tool-output``. Reasoning, synthetic and sub-agent content are left
|
|
93
|
+
out.
|
|
94
|
+
- A first line saying which opencode session it came from. Claude reads this
|
|
95
|
+
when you resume, so it knows the context.
|
|
96
|
+
|
|
97
|
+
How duplicates are prevented
|
|
98
|
+
----------------------------
|
|
99
|
+
The Claude session id is ``uuid5(opencode session id + hash of the converted
|
|
100
|
+
content)``. Importing unchanged content maps to a file that already exists,
|
|
101
|
+
so it is skipped. The first line of each imported file records the opencode
|
|
102
|
+
session id, content hash and suffix, so changed content gets the next free
|
|
103
|
+
suffix (the highest existing one + 1). New files are written under a
|
|
104
|
+
temporary name and hard-linked into place, which can't overwrite an existing
|
|
105
|
+
file. Continuing an imported conversation in Claude doesn't count as a
|
|
106
|
+
change: only the opencode side is compared.
|
|
107
|
+
|
|
108
|
+
Claude Code's transcript format is internal and may change between Claude
|
|
109
|
+
Code versions. The importer writes only a minimal, stable subset of it.
|
|
110
|
+
|
|
111
|
+
Command line
|
|
112
|
+
============
|
|
113
|
+
The same package works in the terminal:
|
|
114
|
+
|
|
115
|
+
.. code-block:: sh
|
|
116
|
+
|
|
117
|
+
claude-opencode-sessions import --dry-run # same as /opencode-sessions:import
|
|
118
|
+
claude-opencode-sessions import --worktrees
|
|
119
|
+
claude-opencode-sessions list # sessions in scope, newest first
|
|
120
|
+
claude-opencode-sessions list --all --format json
|
|
121
|
+
claude-opencode-sessions doctor # environment + database report
|
|
122
|
+
|
|
123
|
+
Hidden by default: archived sessions (``--include-archived``) and sub-agent
|
|
124
|
+
sessions (``--include-subagents``).
|
|
125
|
+
|
|
126
|
+
Scoping
|
|
127
|
+
-------
|
|
128
|
+
.. list-table::
|
|
129
|
+
:header-rows: 1
|
|
130
|
+
:widths: 12 20 50 18
|
|
131
|
+
|
|
132
|
+
* - Mode
|
|
133
|
+
- Flag
|
|
134
|
+
- Sessions whose directory is…
|
|
135
|
+
- ``/resume`` equivalent
|
|
136
|
+
* - worktree
|
|
137
|
+
- *(default)*
|
|
138
|
+
- inside the current git worktree (its top-level directory or below).
|
|
139
|
+
Outside git: the current directory or below
|
|
140
|
+
- default view
|
|
141
|
+
* - repo
|
|
142
|
+
- ``--worktrees``
|
|
143
|
+
- inside any worktree of the repository, plus sessions of the same
|
|
144
|
+
opencode project whose worktree was deleted
|
|
145
|
+
- ``Ctrl+W``
|
|
146
|
+
* - all
|
|
147
|
+
- ``--all`` (``list`` only)
|
|
148
|
+
- anywhere
|
|
149
|
+
- ``Ctrl+A``
|
|
150
|
+
|
|
151
|
+
Configuration
|
|
152
|
+
-------------
|
|
153
|
+
.. list-table::
|
|
154
|
+
:header-rows: 1
|
|
155
|
+
:widths: 30 70
|
|
156
|
+
|
|
157
|
+
* - Variable
|
|
158
|
+
- Purpose
|
|
159
|
+
* - ``OPENCODE_SESSIONS_DB``
|
|
160
|
+
- Path to ``opencode.db``. Default:
|
|
161
|
+
``$XDG_DATA_HOME/opencode/opencode.db``, then
|
|
162
|
+
``~/.local/share/opencode/opencode.db``, then ``opencode db path``
|
|
163
|
+
* - ``OCS_PYTHON``
|
|
164
|
+
- Interpreter for the plugin launcher (``scripts/opencode-sessions``)
|
|
165
|
+
|
|
166
|
+
Every command also accepts ``--db PATH`` and ``--backend auto|sqlite|cli``.
|
|
167
|
+
|
|
168
|
+
How it works
|
|
169
|
+
============
|
|
170
|
+
- **sqlite** (default): opens ``opencode.db`` with ``mode=ro``. On 2.x it
|
|
171
|
+
reads ``session_v2`` + ``session_message``. On 1.x it reads ``session`` +
|
|
172
|
+
``message`` + ``part``. When both exist in the same file, sessions are
|
|
173
|
+
merged by id (2.x wins), and messages fall back to the 1.x tables per
|
|
174
|
+
session.
|
|
175
|
+
- **cli** (fallback): used when the database is missing, locked or not
|
|
176
|
+
recognised, or when more than 20% of a session's messages can't be parsed.
|
|
177
|
+
It runs ``opencode session list --format json`` and
|
|
178
|
+
``opencode export <id>``.
|
|
179
|
+
|
|
180
|
+
See `docs/design.md <docs/design.md>`_ for the design and
|
|
181
|
+
`docs/schema-notes.md <docs/schema-notes.md>`_ for the tables and fields
|
|
182
|
+
that are read.
|
|
183
|
+
|
|
184
|
+
Development
|
|
185
|
+
===========
|
|
186
|
+
.. code-block:: sh
|
|
187
|
+
|
|
188
|
+
make install # uv sync (creates .venv with dev tools)
|
|
189
|
+
make test # pytest (tests live in src/claude_opencode_sessions/tests)
|
|
190
|
+
make check # ruff + mypy --strict + pytest + manifest validation
|
|
191
|
+
make run ARGS="list --all"
|
|
192
|
+
make dev # claude --plugin-dir . (live plugin; /reload-plugins after edits)
|
|
193
|
+
make install-local / make uninstall-local
|
|
194
|
+
|
|
195
|
+
Publishing
|
|
196
|
+
==========
|
|
197
|
+
One version number covers both the PyPI package and the plugin. It lives in
|
|
198
|
+
``pyproject.toml`` (managed with ``uv version``), and ``make bump`` copies it
|
|
199
|
+
into ``.claude-plugin/plugin.json``.
|
|
200
|
+
|
|
201
|
+
How each channel works
|
|
202
|
+
----------------------
|
|
203
|
+
- **Claude Code plugin:** there is no registry upload. This repository *is*
|
|
204
|
+
the marketplace (``.claude-plugin/marketplace.json``), so pushing to
|
|
205
|
+
``main`` publishes. Users only receive an update when the ``version`` in
|
|
206
|
+
``plugin.json`` changes, so every release must bump it.
|
|
207
|
+
- **PyPI:** ``claude-opencode-sessions`` is uploaded with ``twine``, using
|
|
208
|
+
the credentials in ``~/.pypirc``, so you don't type them each time.
|
|
209
|
+
- **Anthropic community marketplace (optional):** a reviewed listing in
|
|
210
|
+
``anthropics/claude-plugins-community``, pinned to a commit. Submit through
|
|
211
|
+
the form that ``make submit`` prints the details for.
|
|
212
|
+
|
|
213
|
+
One-time setup
|
|
214
|
+
--------------
|
|
215
|
+
Put API tokens for PyPI and TestPyPI in ``~/.pypirc`` (``chmod 600``):
|
|
216
|
+
|
|
217
|
+
.. code-block:: ini
|
|
218
|
+
|
|
219
|
+
[distutils]
|
|
220
|
+
index-servers =
|
|
221
|
+
pypi
|
|
222
|
+
testpypi
|
|
223
|
+
|
|
224
|
+
[pypi]
|
|
225
|
+
username = __token__
|
|
226
|
+
password = pypi-...
|
|
227
|
+
|
|
228
|
+
[testpypi]
|
|
229
|
+
repository = https://test.pypi.org/legacy/
|
|
230
|
+
username = __token__
|
|
231
|
+
password = pypi-...
|
|
232
|
+
|
|
233
|
+
Release checklist
|
|
234
|
+
-----------------
|
|
235
|
+
.. code-block:: sh
|
|
236
|
+
|
|
237
|
+
make bump BUMP=minor # or BUMP=patch|major, or VERSION=0.2.0
|
|
238
|
+
$EDITOR CHANGELOG.rst # add the release notes
|
|
239
|
+
git commit -am "Release $(uv version --short)"
|
|
240
|
+
make test-release # check, build, twine check, upload to TestPyPI
|
|
241
|
+
make release # the same, upload to PyPI
|
|
242
|
+
make tag # tag vX.Y.Z and push (plugin users get the update)
|
|
243
|
+
|
|
244
|
+
For the community marketplace, run ``make submit`` after the release and
|
|
245
|
+
file the form.
|
|
246
|
+
|
|
247
|
+
Troubleshooting
|
|
248
|
+
===============
|
|
249
|
+
If ``/opencode-sessions:import`` makes Claude answer instead of printing a
|
|
250
|
+
report, the hook didn't run. Check the Python requirement above and run
|
|
251
|
+
``claude-opencode-sessions doctor``, or
|
|
252
|
+
``sh ~/.claude/plugins/…/scripts/opencode-sessions doctor``. It shows the
|
|
253
|
+
Python in use, the database path, the detected layout, row counts and the
|
|
254
|
+
scope roots for the current directory.
|
|
255
|
+
|
|
256
|
+
License
|
|
257
|
+
=======
|
|
258
|
+
MIT
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling>=1.25"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "claude-opencode-sessions"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Import your opencode sessions for the current repository into Claude Code, so they open with /resume."
|
|
9
|
+
readme = "README.rst"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
license-files = ["LICENSE"]
|
|
13
|
+
authors = [{ name = "Artur Barseghyan", email = "artur.barseghyan@gmail.com" }]
|
|
14
|
+
keywords = ["opencode", "claude", "claude-code", "plugin", "sessions", "llm", "agents"]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 3 - Alpha",
|
|
17
|
+
"Environment :: Console",
|
|
18
|
+
"Intended Audience :: Developers",
|
|
19
|
+
"Operating System :: MacOS",
|
|
20
|
+
"Operating System :: POSIX :: Linux",
|
|
21
|
+
"Programming Language :: Python :: 3",
|
|
22
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
23
|
+
"Programming Language :: Python :: 3.10",
|
|
24
|
+
"Programming Language :: Python :: 3.11",
|
|
25
|
+
"Programming Language :: Python :: 3.12",
|
|
26
|
+
"Programming Language :: Python :: 3.13",
|
|
27
|
+
"Programming Language :: Python :: 3.14",
|
|
28
|
+
"Topic :: Software Development",
|
|
29
|
+
"Typing :: Typed",
|
|
30
|
+
]
|
|
31
|
+
dependencies = []
|
|
32
|
+
|
|
33
|
+
[project.scripts]
|
|
34
|
+
claude-opencode-sessions = "claude_opencode_sessions.cli:main"
|
|
35
|
+
|
|
36
|
+
[project.urls]
|
|
37
|
+
Homepage = "https://github.com/barseghyanartur/claude-plugin-opencode-sessions"
|
|
38
|
+
Repository = "https://github.com/barseghyanartur/claude-plugin-opencode-sessions"
|
|
39
|
+
Issues = "https://github.com/barseghyanartur/claude-plugin-opencode-sessions/issues"
|
|
40
|
+
Changelog = "https://github.com/barseghyanartur/claude-plugin-opencode-sessions/blob/main/CHANGELOG.rst"
|
|
41
|
+
|
|
42
|
+
[dependency-groups]
|
|
43
|
+
dev = [
|
|
44
|
+
"pytest>=8",
|
|
45
|
+
"pytest-cov>=5",
|
|
46
|
+
"ruff>=0.6",
|
|
47
|
+
"mypy>=1.11",
|
|
48
|
+
"twine>=6",
|
|
49
|
+
]
|
|
50
|
+
|
|
51
|
+
[tool.hatch.build.targets.wheel]
|
|
52
|
+
packages = ["src/claude_opencode_sessions"]
|
|
53
|
+
|
|
54
|
+
[tool.hatch.build.targets.sdist]
|
|
55
|
+
include = [
|
|
56
|
+
"src/claude_opencode_sessions",
|
|
57
|
+
"README.rst",
|
|
58
|
+
"CHANGELOG.rst",
|
|
59
|
+
"LICENSE",
|
|
60
|
+
]
|
|
61
|
+
|
|
62
|
+
[tool.pytest.ini_options]
|
|
63
|
+
testpaths = ["src/claude_opencode_sessions/tests"]
|
|
64
|
+
addopts = "-ra"
|
|
65
|
+
|
|
66
|
+
[tool.coverage.run]
|
|
67
|
+
source = ["claude_opencode_sessions"]
|
|
68
|
+
omit = ["*/tests/*"]
|
|
69
|
+
|
|
70
|
+
[tool.coverage.report]
|
|
71
|
+
show_missing = true
|
|
72
|
+
skip_covered = true
|
|
73
|
+
|
|
74
|
+
[tool.ruff]
|
|
75
|
+
line-length = 88
|
|
76
|
+
target-version = "py310"
|
|
77
|
+
src = ["src"]
|
|
78
|
+
|
|
79
|
+
[tool.ruff.lint]
|
|
80
|
+
select = ["E", "F", "W", "I", "B", "UP", "SIM", "RUF", "C4"]
|
|
81
|
+
ignore = ["SIM300", "RUF001"]
|
|
82
|
+
|
|
83
|
+
[tool.ruff.lint.per-file-ignores]
|
|
84
|
+
"src/claude_opencode_sessions/tests/*" = ["E501"]
|
|
85
|
+
|
|
86
|
+
[tool.mypy]
|
|
87
|
+
python_version = "3.10"
|
|
88
|
+
strict = true
|
|
89
|
+
files = ["src/claude_opencode_sessions"]
|
|
90
|
+
exclude = ["src/claude_opencode_sessions/tests/"]
|