feat: add OpenCode host support#86
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughOpenCode support is added across packaging, runtime plumbing, payload shaping, server bridging, CLI install/update/uninstall flows, and documentation. The PR also extends package exports, build steps, and test coverage for the new host. ChangesOpenCode support rollout
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@bin/claude-smart.js`:
- Around line 1829-1853: The install flow in runInstallOpenCode starts the
backend/dashboard before patchOpenCodePluginConfig, so a config failure can
leave services running after the command exits. Move the config patch step
earlier in runInstallOpenCode, or add cleanup in the patch failure path to stop
any services started by
bootstrapPluginRuntime/startBackendService/refreshDashboardService before
exiting. Keep the error handling around opencodeConfigPath(args) and
patchOpenCodePluginConfig so a failed install cannot leave dangling processes.
In `@plugin/opencode/assistant-buffer.ts`:
- Around line 31-40: The empty-string check in textFromPart is incorrectly
treating valid text parts as non-text, which causes later delta updates to be
ignored in the assistant buffer. Update textFromPart and the ignoredPartIDs
handling in assistant-buffer to accept empty text parts as text, so
message.part.delta can continue appending content and assistant.text() is not
left blank when the stream starts empty.
In `@plugin/src/claude_smart/cli.py`:
- Around line 1075-1077: The CLI availability probe in `cli.py` currently
accepts any existing file for `CLAUDE_SMART_CLI_PATH`, which can mark a
non-executable path as usable and fail later. Update the check in the path
handling logic to require both an existing file and executable permission by
adding an `os.access(..., os.X_OK)` validation alongside the current
`Path(...).is_file()` test. Keep the change localized to the
`CLAUDE_SMART_CLI_PATH` lookup path used by the probe.
In `@tests/test_opencode_support.py`:
- Around line 53-57: The tests around runtime host handling are mutating global
state and leaving `claude_smart.runtime` set to `opencode`, which can make later
tests order-dependent. Update the affected test cases in
`test_opencode_support.py` to restore the previous host/environment after each
test, preferably using a fixture or `monkeypatch` cleanup. Make sure the cleanup
covers the runtime helpers involved here, especially `runtime.set_host`,
`runtime.host`, `runtime.is_opencode`, and `runtime.agent_version`.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 57daa469-9cf8-413d-aa9a-33c588308d8f
⛔ Files ignored due to path filters (4)
package-lock.jsonis excluded by!**/package-lock.jsonplugin/opencode/dist/assistant-buffer.jsis excluded by!**/dist/**plugin/opencode/dist/payload.jsis excluded by!**/dist/**plugin/opencode/dist/server.mjsis excluded by!**/dist/**
📒 Files selected for processing (19)
.gitignoreMakefileREADME.mdbin/claude-smart.jspackage.jsonplugin/opencode/assistant-buffer.tsplugin/opencode/package.jsonplugin/opencode/payload.tsplugin/opencode/server.mtsplugin/opencode/tsconfig.jsonplugin/scripts/backend-service.shplugin/scripts/hook_entry.shplugin/src/claude_smart/cli.pyplugin/src/claude_smart/events/stop.pyplugin/src/claude_smart/runtime.pyscripts/setup-claude-smart.shtests/test_install_scripts.pytests/test_opencode_support.pytests/test_stall_banner.py
💤 Files with no reviewable changes (1)
- tests/test_stall_banner.py
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
tests/test_opencode_support.py (1)
26-26: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valuePrefer direct assignment over
setattrwith a constant attribute.Ruff flags this (B010); a plain assignment is equivalent and clearer, and avoids a lint failure if the rule is enforced in CI.
♻️ Proposed change
- setattr(runtime, "_current_host", previous_host) + runtime._current_host = previous_host🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/test_opencode_support.py` at line 26, Replace the use of setattr in the test cleanup with a direct assignment to the _current_host attribute on runtime; this is a simple B010 cleanup. Update the existing restore logic in the test helper around the runtime object so the previous_host value is assigned directly, keeping the behavior the same while satisfying Ruff.Source: Linters/SAST tools
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@tests/test_opencode_support.py`:
- Around line 459-469: The new Node-based test in
test_node_jsonc_parser_skips_comments_after_trailing_commas is missing the same
availability guard used by the other Node tests. Add the shutil_which_node()
None check before calling _run_node_script so the test returns early when Node
is unavailable, and keep the existing result assertions only in the Node-present
path.
---
Nitpick comments:
In `@tests/test_opencode_support.py`:
- Line 26: Replace the use of setattr in the test cleanup with a direct
assignment to the _current_host attribute on runtime; this is a simple B010
cleanup. Update the existing restore logic in the test helper around the runtime
object so the previous_host value is assigned directly, keeping the behavior the
same while satisfying Ruff.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 262eb244-aa90-4f0e-a61d-c10d1722d6f4
⛔ Files ignored due to path filters (2)
plugin/opencode/dist/assistant-buffer.jsis excluded by!**/dist/**plugin/opencode/dist/server.mjsis excluded by!**/dist/**
📒 Files selected for processing (6)
bin/claude-smart.jsplugin/opencode/assistant-buffer.tsplugin/opencode/server.mtsplugin/src/claude_smart/cli.pyscripts/setup-claude-smart.shtests/test_opencode_support.py
🚧 Files skipped from review as they are similar to previous changes (5)
- scripts/setup-claude-smart.sh
- plugin/opencode/assistant-buffer.ts
- plugin/opencode/server.mts
- plugin/src/claude_smart/cli.py
- bin/claude-smart.js
There was a problem hiding this comment.
♻️ Duplicate comments (1)
tests/test_opencode_support.py (1)
676-702: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick winAdd the missing Node-availability guard to this test.
This is still missing the
shutil_which_node()early return used by the sibling Node-backed tests, so_run_node_script(...)can returnNoneand make the suite fail instead of skip on Node-less environments.Suggested fix
def test_node_opencode_payload_normalizes_tool_contracts() -> None: + if shutil_which_node() is None: + return script = f""" import({json.dumps(str(REPO_ROOT / "plugin" / "opencode" / "dist" / "payload.js"))}).then((payload) => {{🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/test_opencode_support.py` around lines 676 - 702, The test test_node_opencode_payload_normalizes_tool_contracts is missing the Node availability skip used by the other Node-backed tests. Add the same early return guard with shutil_which_node() before calling _run_node_script(...), so the test skips cleanly when Node is unavailable instead of asserting on a None result.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Duplicate comments:
In `@tests/test_opencode_support.py`:
- Around line 676-702: The test
test_node_opencode_payload_normalizes_tool_contracts is missing the Node
availability skip used by the other Node-backed tests. Add the same early return
guard with shutil_which_node() before calling _run_node_script(...), so the test
skips cleanly when Node is unavailable instead of asserting on a None result.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 71763641-d59d-4a2a-8f08-014b5194690f
⛔ Files ignored due to path filters (2)
plugin/opencode/dist/payload.jsis excluded by!**/dist/**plugin/opencode/dist/server.mjsis excluded by!**/dist/**
📒 Files selected for processing (8)
README.mdbin/claude-smart.jspackage.jsonplugin/opencode/payload.tsplugin/opencode/server.mtsplugin/pyproject.tomlplugin/src/claude_smart/cli.pytests/test_opencode_support.py
💤 Files with no reviewable changes (1)
- plugin/opencode/payload.ts
✅ Files skipped from review due to trivial changes (2)
- plugin/pyproject.toml
- README.md
🚧 Files skipped from review as they are similar to previous changes (3)
- plugin/opencode/server.mts
- bin/claude-smart.js
- plugin/src/claude_smart/cli.py
85d1cd3 to
8cf914e
Compare
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
tests/test_opencode_support.py (1)
196-289: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick winAdd a fresh-config case for the install path.
_patch_opencode_plugin_config()still falls back to the legacy"plugin"field when neither key exists. Current coverage misses that path, so a new config can be written with the deprecated key without a test failing. Add a no-key install test and assert it writes"plugins"only.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/test_opencode_support.py` around lines 196 - 289, The install-path coverage for _patch_opencode_plugin_config() is missing the fresh-config case where neither "plugin" nor "plugins" exists, so add a test that starts from an empty opencode config and verifies install writes only the modern "plugins" field with claude-smart. Place it alongside the existing tests in test_opencode_support.py and assert the legacy "plugin" key is not introduced when no plugin field is present.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@tests/test_opencode_support.py`:
- Around line 196-289: The install-path coverage for
_patch_opencode_plugin_config() is missing the fresh-config case where neither
"plugin" nor "plugins" exists, so add a test that starts from an empty opencode
config and verifies install writes only the modern "plugins" field with
claude-smart. Place it alongside the existing tests in test_opencode_support.py
and assert the legacy "plugin" key is not introduced when no plugin field is
present.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 5bddeea1-3ecf-4249-afa7-154a66704188
📒 Files selected for processing (4)
README.mdbin/claude-smart.jsplugin/src/claude_smart/cli.pytests/test_opencode_support.py
✅ Files skipped from review due to trivial changes (1)
- README.md
🚧 Files skipped from review as they are similar to previous changes (2)
- plugin/src/claude_smart/cli.py
- bin/claude-smart.js
8cf914e to
edb0b71
Compare
Summary
opencodeCLI instead of requiring Claude Code or Codex.exports/oc-pluginmetadata with committed ESM dist files for OpenCode to load.Changes
plugin/opencode/server.mts, shared adapter helpers, payload normalization, assistant text buffering, and compileddistfiles.opencodein hook/runtime dispatch, adds install/update/uninstall config patching, reuses backend/dashboard services, and routes OpenCode-started extraction throughopencode-claude-compat.opencode.json; existing.opencode/opencode.json*files are honored when no root config exists; global install honorsXDG_CONFIG_HOME; JSONC rewrites create.bak; invalid scalar plugin configs are rejected without overwrite.opencode run --pureextraction model behavior.Test Plan
npm run build:opencodenode --check plugin/scripts/opencode-claude-compat.jsnpx tsc -p plugin/opencode/tsconfig.json --noEmituv run --project plugin pytest tests/test_opencode_support.py -q(63 passed)uv run --project plugin pytest -q(546 passed, 8 warnings)uvx ruff check .PYTHONPATH=plugin/src uvx pyright plugin/src/claude_smart tests/test_opencode_support.pynpm pack --dry-run --jsonverified OpenCodedist/internal.js,dist/server.mjs, scripts, and plugin files are packageduv build --project plugin --out-dir /tmp/claude-smart-uv-build-opencode-final3git diff --checkopencode debug config, installed-package export loading, and OpenCode CLI bridge smoke with stdin prompt piping