Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion hooks/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ Returns `{}` when no rewrite (Cursor requires JSON for all paths).

### OpenCode (TypeScript Plugin)

Mutates `args.command` in-place via the zx library:
**v1 (default)**: Mutates `args.command` in-place via the zx `$` helper:

```typescript
const result = await $`rtk rewrite ${command}`.quiet().nothrow()
Expand All @@ -197,6 +197,16 @@ if (rewritten && rewritten !== command) {
}
```

**v2**: Uses `Plugin.define` + `ctx.tool.hook("execute.before", ...)` — spawns `rtk rewrite` directly since v2 context does not provide `$`:

```typescript
const proc = spawn("rtk", ["rewrite", command])
proc.stdout.on("data", (d) => { out += d })
proc.on("close", () => resolve(out.trim()))
```

Installed with `rtk init -g --opencode` (v1) or `rtk init -g --opencode-v2` (v2).

### Hermes (Python Plugin)

Mutates `args["command"]` in-place via the `pre_tool_call` hook:
Expand Down
13 changes: 12 additions & 1 deletion hooks/opencode/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,19 @@

## Specifics

- TypeScript plugin using the zx library (not a shell hook)
### OpenCode v1 (default)

- TypeScript plugin using the `zx` library (bun-shell `$` helper)
- Intercepts `tool.execute.before` events, calls `rtk rewrite` as a subprocess
- Uses `.quiet().nothrow()` to silently ignore failures
- Mutates `args.command` in-place if rewrite differs from original
- Installed to `~/.config/opencode/plugins/rtk.ts` by `rtk init -g --opencode`

### OpenCode v2

- TypeScript plugin using the `Plugin.define` API (`@opencode-ai/plugin` v2)
- Registers `ctx.tool.hook("execute.before", ...)` instead of returning a hooks object
- Calls `rtk rewrite` via `child_process.spawn` (v2 context does not provide `$`)
- Uses `spawn` + stdout capture instead of exit codes (`rtk rewrite` exits non-zero on success)
- Mutates `event.input.command` in-place if rewrite differs from original
- Installed to `~/.config/opencode/plugins/rtk.ts` by `rtk init -g --opencode-v2`
45 changes: 45 additions & 0 deletions hooks/opencode/rtk-v2.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { Plugin } from "@opencode-ai/plugin"
import { spawn } from "child_process"

// RTK OpenCode v2 plugin — rewrites commands to use rtk for token savings.
// Requires: rtk >= 0.23.0 in PATH.
//
// OpenCode 2.0 uses a new plugin API (Plugin.define + ctx.tool.hook) and does
// not provide the v1 bun-shell `$` helper, so this variant spawns `rtk`
// directly. `rtk rewrite` exits non-zero even on success, so we read stdout
// from the child instead of relying on exit codes.
//
// This is a thin delegating plugin: all rewrite logic lives in `rtk rewrite`,
// which is the single source of truth (src/discover/registry.rs).
// To add or change rewrite rules, edit the Rust registry — not this file.

function rtkRewrite(command: string): Promise<string> {
return new Promise((resolve) => {
const proc = spawn("rtk", ["rewrite", command])
let out = ""
proc.stdout.on("data", (d) => {
out += d
})
proc.on("error", () => resolve(""))
proc.on("close", () => resolve(out.trim()))
})
}

export default Plugin.define({
id: "rtk.rewrite",
setup: async (ctx) => {
await ctx.tool.hook("execute.before", async (event) => {
const tool = String(event.tool ?? "").toLowerCase()
if (tool !== "bash" && tool !== "shell") return
if (!event.input || typeof event.input !== "object") return

const command = (event.input as Record<string, unknown>).command
if (typeof command !== "string" || !command) return

const rewritten = await rtkRewrite(command)
if (rewritten && rewritten !== command) {
;(event.input as Record<string, unknown>).command = rewritten
}
})
},
})
Loading