From 5aca384a1cda2a1b4b3e7a83a30cdb9b61235d0f Mon Sep 17 00:00:00 2001 From: omartelo Date: Mon, 10 Aug 2026 23:06:12 -0300 Subject: [PATCH] feat: report from opencode and Crush sessions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two more harnesses, and neither fits the script model the other two share — so each is met where it is rather than bent into the shape of the others. **opencode** runs no hook commands: a plugin there is a JavaScript module its server imports. Its client is one file, opencode/lich.js, posting the same four reports to the same endpoints off the event bus — the session id from `session.created`, `busy`/`done` from `session.status`, `waiting` from `permission.asked`, the tool line from `tool.execute.before`, the label from `session.updated`, and the git-status refresh from `file.edited`. Installing it is dropping the file in ~/.config/opencode/plugin/; there is no marketplace. Three things the module has to get right, all of them measured rather than assumed: - Sub-sessions are dropped by their `parentID`. The `task` tool runs a sub-agent as its own session, and its `idle` would otherwise end the turn on the card while the real one is still running. - The title a session is created with is a placeholder built from its timestamp, so the module holds that first value and reports only a title that differs from it — no matching on its wording. - opencode awaits its plugin hooks, so no report is ever awaited and nothing throws. That is this client's version of "always exit 0", and a test holds it: a listener that takes 3s must not hold the turn. **Crush** runs the existing scripts unchanged — its hooks are Claude Code-compatible down to the stdin payload — but `PreToolUse` is the only event it has. Only the two reports that event can honour are registered: the session id, and the git-status refresh on write tools. A `busy` with no `Stop` behind it would pin a spinner to a card, so state and title stay unregistered until Crush ships an end-of-turn event, at which point report-state.sh and report-tool.sh are already here. Crush has no plugin system either, so hooks/crush-hooks.json is a block to merge into the user's own crush.json, with where the clone's path goes. The suite splits so both clients answer to the same lines: tests/contract.mjs holds lich's fixtures, the assertions and the stub; tests/opencode.test.mjs drives the module through the payloads a real opencode run emits, including the ones it must drop. --- CHANGELOG.md | 27 +++ CLAUDE.md | 30 ++- README.md | 39 +++- docs/providers.md | 123 ++++++++-- docs/session-start.md | 35 ++- docs/session-state.md | 38 +-- docs/session-title.md | 14 +- docs/session-touched.md | 20 +- hooks/crush-hooks.json | 17 ++ opencode/lich.js | 108 +++++++++ opencode/package.json | 5 + tests/contract.mjs | 180 ++++++++++++++ tests/fixtures/session-start.jsonl | 2 + tests/hooks.test.mjs | 258 ++++++-------------- tests/opencode.test.mjs | 362 +++++++++++++++++++++++++++++ 15 files changed, 1005 insertions(+), 253 deletions(-) create mode 100644 hooks/crush-hooks.json create mode 100644 opencode/lich.js create mode 100644 opencode/package.json create mode 100644 tests/contract.mjs create mode 100644 tests/opencode.test.mjs diff --git a/CHANGELOG.md b/CHANGELOG.md index c6afedf..f26afd8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,33 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- **opencode sessions report to lich.** opencode runs no hook commands — a + plugin there is a module its server imports — so its client is one file, + `opencode/lich.js`, sending the same four reports to the same endpoints: the + session id off `session.created`, `busy`/`done` off `session.status`, + `waiting` off `permission.asked`, the tool line off `tool.execute.before`, the + card's label off `session.updated`, and the git-status refresh off + `file.edited`. Install it by dropping the file in `~/.config/opencode/plugin/` + — there is no marketplace to go through. Sub-sessions (the `task` tool) are + filtered out by their `parentID`, so a sub-agent finishing does not look like + the turn ending. +- **Crush sessions report their id and refresh git status.** Crush's hooks are + Claude Code-compatible, so the existing scripts run unchanged — but + `PreToolUse` is its only event, so only the two reports it can honour are + registered. `hooks/crush-hooks.json` is the block to merge into your own + `crush.json`, with `` where the clone's path goes. A Crush card + shows no spinner and keeps the name lich gave it; both arrive the day Crush + ships an end-of-turn event. + +### Changed + +- The test suite is split: `tests/contract.mjs` holds lich's fixtures and the + assertions, and both clients — the hook scripts and the opencode module — + answer to the same lines. `tests/opencode.test.mjs` drives the module through + the events a real opencode run emits. + ## [0.7.0] - 2026-08-10 ### Added diff --git a/CLAUDE.md b/CLAUDE.md index 56dab0d..5da6912 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,6 +1,6 @@ # lich-plugin -The agent side of the integration with **lich** — a harness that orchestrates agent CLI sessions. This companion plugin is how lich observes and acts inside a running session. It ships hooks — each following a contract documented in `docs/` — plus skills for work that targets lich itself. One plugin, packaged for **Claude Code** and **OpenAI Codex** from the same repository root. +The agent side of the integration with **lich** — a harness that orchestrates agent CLI sessions. This companion plugin is how lich observes and acts inside a running session. It ships hooks — each following a contract documented in `docs/` — plus skills for work that targets lich itself. One plugin, packaged for **Claude Code**, **OpenAI Codex**, **opencode** and **Crush** from the same repository root. ## Structure @@ -11,12 +11,21 @@ The agent side of the integration with **lich** — a harness that orchestrates .agents/plugins/marketplace.json # marketplace, Codex hooks/hooks.json # hook registration, Claude Code hooks/codex-hooks.json # hook registration, Codex -hooks/ # hook scripts ($CLAUDE_PLUGIN_ROOT/hooks/