opencode-rtk-plugin 0.1.0-beta.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +132 -0
- package/index.js +95 -0
- package/package.json +51 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Nabheet S. Sandhu
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# opencode-rtk-plugin
|
|
2
|
+
|
|
3
|
+
> An [OpenCode](https://opencode.ai) **v2** plugin that routes every shell
|
|
4
|
+
> command through [`rtk`](https://github.com/rtk-ai/rtk), cutting LLM token
|
|
5
|
+
> usage by **60-90%** on common dev commands.
|
|
6
|
+
|
|
7
|
+
OpenCode sends tool output back to the model. `rtk` compresses that output
|
|
8
|
+
(git, grep, ls, test runners, build logs, and more) into a token-efficient
|
|
9
|
+
form before the model ever sees it. This plugin wires the two together.
|
|
10
|
+
|
|
11
|
+
## Why
|
|
12
|
+
|
|
13
|
+
- **Smaller context, lower cost.** Command output is the biggest hidden token
|
|
14
|
+
sink in an agent loop.
|
|
15
|
+
- **No prompt changes.** Rewriting happens in the shell hook, transparently.
|
|
16
|
+
- **Single source of truth.** All rewrite rules live in `rtk rewrite`; this
|
|
17
|
+
plugin is a thin delegating adapter, so rules never drift.
|
|
18
|
+
|
|
19
|
+
## Requirements
|
|
20
|
+
|
|
21
|
+
- OpenCode **v2** (`opencode --version` reports 2.x).
|
|
22
|
+
- `rtk` >= 0.23.0 on `PATH` — see [rtk-ai/rtk](https://github.com/rtk-ai/rtk):
|
|
23
|
+
`brew install rtk`
|
|
24
|
+
|
|
25
|
+
## Install
|
|
26
|
+
|
|
27
|
+
### From npm
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
opencode plugin add opencode-rtk-plugin
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### From GitHub
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
opencode plugin add github:nabheet/opencode-rtk-plugin
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Or add it to `opencode.json(c)` directly:
|
|
40
|
+
|
|
41
|
+
```jsonc
|
|
42
|
+
{
|
|
43
|
+
"$schema": "https://opencode.ai/config.json",
|
|
44
|
+
"plugins": ["opencode-rtk-plugin"]
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Manual
|
|
49
|
+
|
|
50
|
+
Copy this directory to `.opencode/plugins/opencode-rtk-plugin/` (project) or
|
|
51
|
+
`~/.config/opencode/plugins/opencode-rtk-plugin/` (global), then list it:
|
|
52
|
+
|
|
53
|
+
```jsonc
|
|
54
|
+
{
|
|
55
|
+
"plugins": [{ "package": "./.opencode/plugins/opencode-rtk-plugin" }]
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Options
|
|
60
|
+
|
|
61
|
+
```jsonc
|
|
62
|
+
{
|
|
63
|
+
"plugins": [
|
|
64
|
+
{
|
|
65
|
+
"package": "opencode-rtk-plugin",
|
|
66
|
+
"options": {
|
|
67
|
+
"binary": "rtk",
|
|
68
|
+
"timeoutMs": 5000,
|
|
69
|
+
"enabled": true
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
]
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
| Option | Default | Description |
|
|
77
|
+
| ----------- | ------- | ------------------------------------ |
|
|
78
|
+
| `binary` | `"rtk"` | Path or name of the rtk executable. |
|
|
79
|
+
| `timeoutMs` | `5000` | Per-rewrite timeout in milliseconds. |
|
|
80
|
+
| `enabled` | `true` | Set `false` to disable the plugin. |
|
|
81
|
+
|
|
82
|
+
## How it works
|
|
83
|
+
|
|
84
|
+
The plugin registers one OpenCode v2 shell hook:
|
|
85
|
+
|
|
86
|
+
```js
|
|
87
|
+
ctx.shell.hook("create.before", (event) => {
|
|
88
|
+
// event.command is the command about to run; mutate it to rewrite
|
|
89
|
+
})
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
For each shell command it runs `rtk rewrite <command>` and replaces the command
|
|
93
|
+
with rtk's output when it differs. If `rtk` is missing, times out, or errors,
|
|
94
|
+
the original command runs unchanged.
|
|
95
|
+
|
|
96
|
+
## Compatibility
|
|
97
|
+
|
|
98
|
+
- Targets **OpenCode v2** (`plugins` config + `id`/`setup` plugin definition).
|
|
99
|
+
OpenCode v1 used a different plugin API and is not supported.
|
|
100
|
+
- `rtk` ships hooks for Claude Code, Codex, Cursor, Gemini CLI, Copilot and
|
|
101
|
+
others. OpenCode is not among them, so this plugin fills that gap.
|
|
102
|
+
|
|
103
|
+
## Prereleases
|
|
104
|
+
|
|
105
|
+
Every push to a PR publishes an installable beta to the npm `beta` dist-tag:
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
opencode plugin add opencode-rtk-plugin@beta
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Releases (maintainers)
|
|
112
|
+
|
|
113
|
+
All publishing happens in GitHub Actions via npm **trusted publishing** (OIDC
|
|
114
|
+
provenance). Never publish from a local shell.
|
|
115
|
+
|
|
116
|
+
`.github/workflows/ci.yml` is the single workflow file (npm allows one trusted
|
|
117
|
+
publisher per package; the workflow filename must stay `ci.yml`):
|
|
118
|
+
|
|
119
|
+
- `test` — lint + plugin-export smoke check on `main` and PRs (Node 24).
|
|
120
|
+
- push to `main` → `publish` job publishes the next patch to `latest`, pushes a
|
|
121
|
+
`vX.Y.Z` tag, and opens a GitHub release. The version is derived from the
|
|
122
|
+
last published `latest`, so repeated merges never collide; an intentional
|
|
123
|
+
minor/major bump in `package.json` is honored.
|
|
124
|
+
- tag `v*-beta*` → `publish` job publishes `beta` + a prerelease GitHub release.
|
|
125
|
+
- every PR push → `prerelease` job publishes `<version>-beta.<run_number>` to
|
|
126
|
+
`beta` (same-repo, non-draft, non-dependabot PRs; re-runs deduped).
|
|
127
|
+
- manual dispatch → next `-beta.X` staging publish from `main`, no git change.
|
|
128
|
+
|
|
129
|
+
## License
|
|
130
|
+
|
|
131
|
+
MIT — see [LICENSE](./LICENSE). `rtk` itself is Apache-2.0 and is **not**
|
|
132
|
+
bundled or vendored here.
|
package/index.js
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
// opencode-rtk — OpenCode v2 plugin
|
|
2
|
+
//
|
|
3
|
+
// Rewrites shell commands through `rtk rewrite` so command output reaches the
|
|
4
|
+
// model in a token-efficient form. All rewrite logic lives in rtk itself; this
|
|
5
|
+
// plugin is a thin delegating adapter for OpenCode's shell hook.
|
|
6
|
+
//
|
|
7
|
+
// Requires: rtk (https://github.com/rtk-ai/rtk)
|
|
8
|
+
|
|
9
|
+
import { execFile } from "node:child_process";
|
|
10
|
+
|
|
11
|
+
const DEFAULT_TIMEOUT_MS = 5000;
|
|
12
|
+
const MAX_TIMEOUT_MS = 600_000;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Run `rtk rewrite <command>` and resolve to the rewritten command, or null.
|
|
16
|
+
*
|
|
17
|
+
* rtk signals the result on STDOUT, not via exit status: since v0.34.0
|
|
18
|
+
* `rtk rewrite` exits 3 with the rewritten command on stdout (its
|
|
19
|
+
* permission-verdict protocol). So the exit code must be ignored and the
|
|
20
|
+
* decision made on stdout alone.
|
|
21
|
+
*
|
|
22
|
+
* Never rejects and never hangs: a spawn failure, timeout, or empty/unchanged
|
|
23
|
+
* output resolves to null so the original command runs.
|
|
24
|
+
*/
|
|
25
|
+
function rewriteCommand(binary, command, timeoutMs) {
|
|
26
|
+
return new Promise((resolve) => {
|
|
27
|
+
let settled = false;
|
|
28
|
+
let timer;
|
|
29
|
+
const done = (value) => {
|
|
30
|
+
if (settled) return;
|
|
31
|
+
settled = true;
|
|
32
|
+
if (timer) clearTimeout(timer);
|
|
33
|
+
resolve(value);
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
let child;
|
|
37
|
+
try {
|
|
38
|
+
// timeout: 0 disables execFile's own SIGTERM-only timeout; we enforce a
|
|
39
|
+
// hard SIGKILL deadline below so a SIGTERM-ignoring rtk cannot hang.
|
|
40
|
+
child = execFile(
|
|
41
|
+
binary,
|
|
42
|
+
["rewrite", command],
|
|
43
|
+
{ timeout: 0, encoding: "utf8" },
|
|
44
|
+
(_err, stdout) => {
|
|
45
|
+
const out = String(stdout ?? "").trim();
|
|
46
|
+
done(out && out !== command ? out : null);
|
|
47
|
+
}
|
|
48
|
+
);
|
|
49
|
+
} catch {
|
|
50
|
+
return done(null);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
child.on("error", () => done(null));
|
|
54
|
+
|
|
55
|
+
timer = setTimeout(() => {
|
|
56
|
+
try {
|
|
57
|
+
child.kill("SIGKILL");
|
|
58
|
+
} catch {
|
|
59
|
+
/* already gone */
|
|
60
|
+
}
|
|
61
|
+
done(null);
|
|
62
|
+
}, timeoutMs + 250);
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function resolveBinary(value) {
|
|
67
|
+
return typeof value === "string" && value.trim() !== "" ? value : "rtk";
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function resolveTimeout(value) {
|
|
71
|
+
if (Number.isInteger(value) && value >= 1 && value <= MAX_TIMEOUT_MS) return value;
|
|
72
|
+
return DEFAULT_TIMEOUT_MS;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export default {
|
|
76
|
+
id: "rtk",
|
|
77
|
+
|
|
78
|
+
async setup(ctx) {
|
|
79
|
+
const options = ctx.options ?? {};
|
|
80
|
+
if (options.enabled === false) return;
|
|
81
|
+
|
|
82
|
+
const binary = resolveBinary(options.binary);
|
|
83
|
+
const timeoutMs = resolveTimeout(options.timeoutMs);
|
|
84
|
+
|
|
85
|
+
await ctx.shell.hook("create.before", async (event) => {
|
|
86
|
+
try {
|
|
87
|
+
if (!event || typeof event.command !== "string" || !event.command) return;
|
|
88
|
+
const rewritten = await rewriteCommand(binary, event.command, timeoutMs);
|
|
89
|
+
if (rewritten) event.command = rewritten;
|
|
90
|
+
} catch {
|
|
91
|
+
// Never fail the shell call: any error leaves the command unchanged.
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
},
|
|
95
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "opencode-rtk-plugin",
|
|
3
|
+
"version": "0.1.0-beta.7",
|
|
4
|
+
"description": "OpenCode v2 plugin that rewrites shell commands through rtk to cut LLM token usage by 60-90%.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "index.js",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./index.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"index.js",
|
|
12
|
+
"README.md",
|
|
13
|
+
"LICENSE"
|
|
14
|
+
],
|
|
15
|
+
"keywords": [
|
|
16
|
+
"opencode",
|
|
17
|
+
"opencode-plugin",
|
|
18
|
+
"opencode-v2",
|
|
19
|
+
"rtk",
|
|
20
|
+
"token-optimization",
|
|
21
|
+
"token-savings",
|
|
22
|
+
"llm",
|
|
23
|
+
"ai-coding-agent",
|
|
24
|
+
"context-window",
|
|
25
|
+
"shell"
|
|
26
|
+
],
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"author": "Nabheet S. Sandhu",
|
|
29
|
+
"repository": {
|
|
30
|
+
"type": "git",
|
|
31
|
+
"url": "git+https://github.com/nabheet/opencode-rtk-plugin.git"
|
|
32
|
+
},
|
|
33
|
+
"bugs": {
|
|
34
|
+
"url": "https://github.com/nabheet/opencode-rtk-plugin/issues"
|
|
35
|
+
},
|
|
36
|
+
"homepage": "https://github.com/nabheet/opencode-rtk-plugin#readme",
|
|
37
|
+
"engines": {
|
|
38
|
+
"node": ">=18"
|
|
39
|
+
},
|
|
40
|
+
"publishConfig": {
|
|
41
|
+
"access": "public"
|
|
42
|
+
},
|
|
43
|
+
"scripts": {
|
|
44
|
+
"lint": "markdownlint-cli2 \"**/*.md\"",
|
|
45
|
+
"test": "node --check index.js && node --test test/*.test.mjs",
|
|
46
|
+
"check": "npm run lint && npm test"
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"markdownlint-cli2": "^0.23.0"
|
|
50
|
+
}
|
|
51
|
+
}
|