feat: opencode plugin bridge for Copilot CLI hooks#972
Conversation
…s to opencode hooks - Bridge plugin (copilot-tools-bridge.ts) maps tool.execute.before/after, event, chat.message, tool.use, doom_loop, session.start/stop, task to Copilot CLI Python rules via hook_runner.py subprocess - Install script (install.py) deploys plugin + MCP server to opencode config - 18 tests covering hook runner JSON compat, session lifecycle, error handling, install idempotency
There was a problem hiding this comment.
Pull request overview
This PR adds an opencode plugin + installer that bridges opencode plugin events to this repo’s Copilot CLI hook runner (hooks/hook_runner.py) via a Python subprocess, plus a dedicated test suite intended to validate compatibility and installation.
Changes:
- Added a Bun/TypeScript opencode plugin that calls
hook_runner.pyfor pre/post tool hooks, chat prompts, and session lifecycle signals. - Added a Python installer that copies the plugin into
~/.config/opencode/plugins/and registers this repo’s MCP server inopencode.jsonc. - Added a new Python test file to validate hook JSON compatibility and installer behavior.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 11 comments.
| File | Description |
|---|---|
opencode-plugin/copilot-tools-bridge.ts |
Implements the opencode → Copilot hooks bridge by spawning hook_runner.py and translating selected events/payloads. |
opencode-plugin/install.py |
Installs the plugin and updates opencode MCP configuration. |
tests/test_opencode_bridge.py |
Adds tests for bridge JSON compatibility and installer/MCP configuration behavior. |
cb3a4db to
4b1b7e2
Compare
…andbox, unused import, Windows guard, event coverage
|
✅ All 11 review comments addressed: copilot-tools-bridge.ts:
install.py:
test_opencode_bridge.py:
CI is fully green across all 9 platforms/versions. Please re-review. |
…tform paths, safe config write, accurate docstrings
- callHookRunner(): add 15s AbortSignal.timeout to prevent hanging - Add explicit task hook handler for opencode task events - test_opencode_bridge: register atexit cleanup for isolated HOME - Replace hard-coded /tmp paths with tempfile.gettempdir() - Add task and tool.use to hook export assertion test
- install_mcp(): return False when MCP entry already exists to prevent unnecessary config write - --remove: remove empty-mcp cleanup branch that rewrote config when no mcp section existed - task handler: add throw e + return to actually block deny decisions - Sync local installed plugin
|
/copilot-review |
- _strip_jsonc(): replace regex with string-aware trailing comma removal - Remove unused re import - install_mcp(): use setdefault for mcp + type guard for non-dict values - Remove unused _SANDBOX_ENV global - test_plugin_matches_source: compare sha256 hash of full content - Add hashlib import
|
/copilot-review |
- XDG_CONFIG_HOME: guard against empty string (Path('') → CWD)
- Extract _get_mcp() helper for non-dict type guard
- show_status()/remove_all(): use _get_mcp() to avoid crash on non-dict mcp
- load_config(): warn on OSError instead of silent fallback
- test_session_start_returns_context: accept JSON or text status output
|
@copilot review please |
Re-reviewed the latest branch state: the previously raised bridge/install/test issues appear addressed, and targeted validation is green ( |
Summary
Bridge plugin that maps 9 Copilot CLI hook events to opencode's plugin system via Python subprocess.
Changes
opencode-plugin/copilot-tools-bridge.ts— TypeScript Plugin for opencode that translates 9 hook events (tool.execute.before/after,event,chat.message,tool.use,doom_loop,session.start/stop,task) to Python rule evaluation viahook_runner.pyopencode-plugin/install.py— Installs bridge plugin to~/.config/opencode/plugins/and registers MCP servertests/test_opencode_bridge.py— 18 tests covering hook runner JSON compat, session lifecycle, error handling, install idempotency (all pass)Architecture
Bun.spawn+Blob.stream()stdin for Python subprocess (avoids shell escaping)tool.execute.beforecan block tool use via throwingchat.messageparts mutable for briefing injection (synthetic parts)Testing
python3 tests/test_quality_gates.py— 429/429 passedpython3 tests/test_opencode_bridge.py— 18/18 passed