opencode-raven 1.2.2 → 1.2.4
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/README.md +19 -9
- package/Raven.md +3 -8
- package/index.ts +72 -28
- package/mcp-guidance.md +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
<td><img src="Raven.png" alt="Raven" width="768" /></td>
|
|
6
6
|
<td>
|
|
7
7
|
<strong>Search-first subagent for <a href="https://opencode.ai">opencode</a></strong><br/>
|
|
8
|
-
Intercepts search tool calls and routes them to a
|
|
8
|
+
Intercepts search tool calls and routes them to a Raven agent with full local filesystem access plus Context7, Exa AI, and Grep.app MCPs.
|
|
9
9
|
</td>
|
|
10
10
|
</tr>
|
|
11
11
|
</table>
|
|
@@ -16,7 +16,7 @@ Search is the most common thing agents do — and the most wasteful. Every searc
|
|
|
16
16
|
|
|
17
17
|
1. **Cost** — Use a free model like `opencode/deepseek-v4-flash-free` for all search, saving your expensive model's context for actual work.
|
|
18
18
|
2. **Reliability** — Hard-enforced interception. Other plugins suggest delegation; Raven *blocks* search tools for non-Raven agents and redirects them. No more agents ignoring your instructions and searching directly.
|
|
19
|
-
3. **Simplicity** — One plugin, one agent, auto-configured. No bundled agents or features you don't need. Works with any agent or workflow. Just add it to `opencode.jsonc` and restart.
|
|
19
|
+
3. **Simplicity** — One plugin, one agent, auto-configured. No bundled agents or features you don't need. Call Raven directly with `@Raven` or let agents use `raven_seek`. Works with any agent or workflow. Just add it to `opencode.jsonc` and restart.
|
|
20
20
|
|
|
21
21
|
## Install
|
|
22
22
|
|
|
@@ -40,18 +40,23 @@ Restart opencode.
|
|
|
40
40
|
|
|
41
41
|
| Command | Action |
|
|
42
42
|
|---------|--------|
|
|
43
|
-
| `/raven` | Show status — enabled/disabled,
|
|
43
|
+
| `/raven` | Show status — enabled/disabled, model, reasoning effort, timeout |
|
|
44
44
|
| `/raven on` | Enable search tool redirection (default) |
|
|
45
45
|
| `/raven off` | Disable interception — all agents can use search tools directly |
|
|
46
46
|
| `/raven model <name>` | Change Raven's model (requires restart) |
|
|
47
47
|
| `/raven effort <value>` | Change Raven's reasoning effort (requires restart) |
|
|
48
|
+
| `/raven timeout <seconds>` | Change raven_seek timeout (min 10s, takes effect immediately) |
|
|
48
49
|
| `/raven stats` | Show context processed (session + all-time, bytes + tokens) |
|
|
49
50
|
|
|
50
51
|
Config persists across restarts in `~/.config/opencode/raven-config.json` (global, shared across all projects). Auto-created on first run.
|
|
51
52
|
|
|
53
|
+
## Direct access
|
|
54
|
+
|
|
55
|
+
You can call Raven directly with `@Raven` in any opencode chat. The Raven agent runs with full filesystem and MCP access — no permission prompts.
|
|
56
|
+
|
|
52
57
|
## raven_seek
|
|
53
58
|
|
|
54
|
-
When search tools are blocked, agents use **`raven_seek`** — a tool that
|
|
59
|
+
When search tools are blocked, agents use **`raven_seek`** — a unified tool that handles ALL search types (local codebase, web, docs, GitHub examples). Output includes elapsed time and tokens processed.
|
|
55
60
|
|
|
56
61
|
```
|
|
57
62
|
raven_seek(query: "how to use useEffect cleanup")
|
|
@@ -71,7 +76,8 @@ Located at `~/.config/opencode/raven-config.json`. Auto-created on first run. Ed
|
|
|
71
76
|
"model": "opencode/deepseek-v4-flash-free",
|
|
72
77
|
"reasoning_effort": "low",
|
|
73
78
|
"excludeAgents": [],
|
|
74
|
-
"excludeTools": []
|
|
79
|
+
"excludeTools": [],
|
|
80
|
+
"timeout": 180
|
|
75
81
|
}
|
|
76
82
|
```
|
|
77
83
|
|
|
@@ -82,7 +88,8 @@ Located at `~/.config/opencode/raven-config.json`. Auto-created on first run. Ed
|
|
|
82
88
|
| `reasoning_effort` | *(from Raven.md)* | Override Raven's reasoning effort (e.g. `"low"`, `"medium"`, `"high"`) |
|
|
83
89
|
| `excludeAgents` | `[]` | Agents that bypass search tool blocking (case-insensitive). e.g. `["librarian", "explorer"]` |
|
|
84
90
|
| `excludeTools` | `[]` | Tools that never get blocked. e.g. `["glob", "webfetch"]` |
|
|
85
|
-
| `
|
|
91
|
+
| `timeout` | `180` | Max seconds for a `raven_seek` call. On timeout the session is kept for inspection. |
|
|
92
|
+
| `stats` | *(auto)* | Session + global context processed by Raven (bytes + tokens). Managed automatically. |
|
|
86
93
|
|
|
87
94
|
### MCP servers
|
|
88
95
|
|
|
@@ -124,10 +131,11 @@ To disable an MCP entirely:
|
|
|
124
131
|
| Hook | What it does |
|
|
125
132
|
|------|--------------|
|
|
126
133
|
| `config` | Registers Raven agent, adds Context7/Exa/Grep.app MCPs, loads MCP guidance |
|
|
127
|
-
| `tool` | Registers `raven_seek` —
|
|
134
|
+
| `tool` | Registers `raven_seek` — creates Raven sessions with timeout, error recovery, timing, and session tree visibility. Tracks context processed for stats (both `raven_seek` and direct `@Raven`). |
|
|
128
135
|
| `chat.message` | Tracks agent ↔ session mapping for allowlist and Raven exclusion |
|
|
129
|
-
| `command.execute.before` | Handles `/raven on\|off\|model\|effort\|status` |
|
|
136
|
+
| `command.execute.before` | Handles `/raven on\|off\|model\|effort\|timeout\|stats\|status` |
|
|
130
137
|
| `tool.execute.before` | Blocks search tools for non-Raven, non-excluded agents (respects `excludeTools`). Injects `<raven_guidance>` into subagent prompts. |
|
|
138
|
+
| `tool.execute.after` | Counts output bytes from direct `@Raven` calls for accurate stats. |
|
|
131
139
|
|
|
132
140
|
### Blocked tools (redirected except for Raven and any agents in `excludeAgents`)
|
|
133
141
|
|
|
@@ -150,6 +158,7 @@ To disable an MCP entirely:
|
|
|
150
158
|
|---------|----------|
|
|
151
159
|
| Content search | `rg`, `grep`, `egrep`, `fgrep`, `git grep`, `ack`, `ag`, `findstr`, `Select-String` |
|
|
152
160
|
| Filesystem exploration | `Get-ChildItem`, `gci`, `find -name`, `find -type`, `ls -R`, `dir /s` |
|
|
161
|
+
| Shell bypass | `cmd /c dir`, `cmd /c findstr`, `cmd /c find`, `cmd /c where`, `cmd /c tree` |
|
|
153
162
|
|
|
154
163
|
**Unrestricted**: `read`, `task`, `subtask`, `raven_seek`, and non-search `bash` commands.
|
|
155
164
|
|
|
@@ -164,7 +173,8 @@ Raven itself has access to these tools (blocked for other agents by the plugin):
|
|
|
164
173
|
| Tool / MCP | Purpose |
|
|
165
174
|
|------------|---------|
|
|
166
175
|
| `read`, `glob`, `grep`, `list` | Local codebase inspection |
|
|
167
|
-
| `bash` (`rg`, `grep`, `
|
|
176
|
+
| `bash` (all commands) | Full local shell access (`rg`, `grep`, `dir`, `ls`, `Get-ChildItem`, `find`, etc.) |
|
|
177
|
+
| `external_directory` | Allowed — no permission prompts when accessing paths outside the workspace |
|
|
168
178
|
| Context7 | Library/framework/SDK/API docs |
|
|
169
179
|
| Exa AI | Web search, news, pages, products |
|
|
170
180
|
| Grep.app | Public GitHub examples |
|
package/Raven.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Search-only agent for web, docs, code, examples, and Unity project inspection.
|
|
3
3
|
mode: subagent
|
|
4
|
-
hidden:
|
|
4
|
+
hidden: false
|
|
5
5
|
model: opencode/deepseek-v4-flash-free
|
|
6
6
|
reasoning_effort: low
|
|
7
7
|
permission:
|
|
@@ -10,14 +10,9 @@ permission:
|
|
|
10
10
|
grep: allow
|
|
11
11
|
list: allow
|
|
12
12
|
edit: deny
|
|
13
|
-
bash:
|
|
14
|
-
|
|
15
|
-
"rg *": allow
|
|
16
|
-
"grep *": allow
|
|
17
|
-
"git grep *": allow
|
|
18
|
-
"*": deny
|
|
19
|
-
|
|
13
|
+
bash: allow
|
|
20
14
|
task: deny
|
|
15
|
+
external_directory: allow
|
|
21
16
|
---
|
|
22
17
|
|
|
23
18
|
You are Raven.
|
package/index.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { Plugin, PluginInput } from "@opencode-ai/plugin"
|
|
2
2
|
import { tool } from "@opencode-ai/plugin"
|
|
3
|
-
import { readFileSync, writeFileSync, existsSync, mkdirSync } from "node:fs"
|
|
3
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync, appendFileSync } from "node:fs"
|
|
4
4
|
import { join } from "node:path"
|
|
5
|
-
import { homedir } from "node:os"
|
|
5
|
+
import { homedir, tmpdir } from "node:os"
|
|
6
6
|
|
|
7
7
|
// ── Resolve paths relative to this package (works in node_modules/) ──
|
|
8
8
|
const PKG_DIR = import.meta.dirname!
|
|
@@ -40,7 +40,7 @@ const SEARCH_TOOLS = [
|
|
|
40
40
|
]
|
|
41
41
|
|
|
42
42
|
// ── Bash commands that look like search workarounds ──
|
|
43
|
-
const SEARCH_BASH_RE = /\b(rg|ripgrep|grep|egrep|fgrep|git\s+grep|ack|ag\b|findstr|Select-String|Get-ChildItem|gci\b|dir\b\s+[/-][sS]|ls\b\s+-[rR]|find\b\s+.*-name|find\b\s+.*-type)\b/
|
|
43
|
+
const SEARCH_BASH_RE = /\b(rg|ripgrep|grep|egrep|fgrep|git\s+grep|ack|ag\b|findstr|Select-String|Get-ChildItem|gci\b|dir\b\s+[/-][sS]|ls\b\s+-[rR]|find\b\s+.*-name|find\b\s+.*-type)\b/i
|
|
44
44
|
|
|
45
45
|
// Strip quoted content to avoid false positives (e.g. echo "use grep here")
|
|
46
46
|
function stripHeredocs(cmd: string): string {
|
|
@@ -55,9 +55,11 @@ function stripQuotedContent(cmd: string): string {
|
|
|
55
55
|
|
|
56
56
|
function isSearchBash(tool: string, args: any): boolean {
|
|
57
57
|
if (tool !== "bash") return false
|
|
58
|
-
const
|
|
58
|
+
const raw = String(args?.command ?? "")
|
|
59
|
+
const cmd = stripQuotedContent(raw)
|
|
59
60
|
const desc = stripQuotedContent(String(args?.description ?? ""))
|
|
60
|
-
|
|
61
|
+
const lower = raw.toLowerCase().trim()
|
|
62
|
+
return SEARCH_BASH_RE.test(cmd) || SEARCH_BASH_RE.test(desc) || /^cmd\s+\/c\s+"?(dir|findstr|find|where|tree)\b/.test(lower)
|
|
61
63
|
}
|
|
62
64
|
|
|
63
65
|
// ── Config file shape ──
|
|
@@ -67,6 +69,7 @@ interface RavenConfig {
|
|
|
67
69
|
reasoning_effort?: string
|
|
68
70
|
excludeAgents?: string[]
|
|
69
71
|
excludeTools?: string[]
|
|
72
|
+
timeout?: number
|
|
70
73
|
stats?: { bytes: number }
|
|
71
74
|
}
|
|
72
75
|
|
|
@@ -80,6 +83,7 @@ const DEFAULT_CONFIG: RavenConfig = {
|
|
|
80
83
|
reasoning_effort: fm.reasoning_effort,
|
|
81
84
|
excludeAgents: [],
|
|
82
85
|
excludeTools: [],
|
|
86
|
+
timeout: 180,
|
|
83
87
|
}
|
|
84
88
|
|
|
85
89
|
function parseRavenMd(raw: string): { frontmatter: Record<string, any>; prompt: string } {
|
|
@@ -135,6 +139,8 @@ function parseYaml(yaml: string): Record<string, any> {
|
|
|
135
139
|
value = true
|
|
136
140
|
} else if (value === "false") {
|
|
137
141
|
value = false
|
|
142
|
+
} else if (/^\d+$/.test(value)) {
|
|
143
|
+
value = parseInt(value, 10)
|
|
138
144
|
}
|
|
139
145
|
current[key] = value
|
|
140
146
|
}
|
|
@@ -175,6 +181,7 @@ export default ((input: PluginInput) => {
|
|
|
175
181
|
reasoning_effort: raw.reasoning_effort,
|
|
176
182
|
excludeAgents: Array.isArray(raw.excludeAgents) ? raw.excludeAgents : [],
|
|
177
183
|
excludeTools: Array.isArray(raw.excludeTools) ? raw.excludeTools : [],
|
|
184
|
+
timeout: typeof raw.timeout === "number" ? raw.timeout : undefined,
|
|
178
185
|
stats: raw.stats || undefined,
|
|
179
186
|
}
|
|
180
187
|
}
|
|
@@ -193,6 +200,7 @@ export default ((input: PluginInput) => {
|
|
|
193
200
|
|
|
194
201
|
let config = loadConfig()
|
|
195
202
|
const ravenSessions = new Set<string>()
|
|
203
|
+
const ravenTaskCalls = new Set<string>()
|
|
196
204
|
const sessionAgents = new Map<string, string>()
|
|
197
205
|
|
|
198
206
|
// ── Check if an agent is excluded from Raven enforcement (case-insensitive) ──
|
|
@@ -202,7 +210,7 @@ export default ((input: PluginInput) => {
|
|
|
202
210
|
return config.excludeAgents.some((a) => a.toLowerCase() === lower)
|
|
203
211
|
}
|
|
204
212
|
|
|
205
|
-
const REROUTE_MSG = "Search tools are blocked. Use raven_seek(query=\"...\")
|
|
213
|
+
const REROUTE_MSG = "Search tools are blocked. Use raven_seek(query=\"...\") for all searches — local codebase, web, docs, and GitHub examples."
|
|
206
214
|
|
|
207
215
|
// ── Context processed by raven_seek ──
|
|
208
216
|
let sessionBytes = 0
|
|
@@ -274,15 +282,20 @@ export default ((input: PluginInput) => {
|
|
|
274
282
|
// Register raven_seek tool — lets agents with task:false still search through Raven
|
|
275
283
|
tool: {
|
|
276
284
|
"raven_seek": tool({
|
|
277
|
-
description: "
|
|
285
|
+
description: "Unified search tool — use only when task delegation to Raven (subagent_type=\"raven\") is unavailable. Handles ALL searches: local codebase, web, docs, and GitHub examples via Context7, Exa AI, and Grep.app.",
|
|
278
286
|
args: {
|
|
279
287
|
query: tool.schema.string().describe("What to search for — be specific about what you need (docs, code examples, web info, etc.)"),
|
|
280
288
|
},
|
|
281
289
|
async execute(args, context) {
|
|
290
|
+
const started = Date.now()
|
|
291
|
+
const timeout = (config.timeout ?? 180) * 1000
|
|
282
292
|
try {
|
|
283
293
|
// Create a Raven session
|
|
284
294
|
const session = await client.session.create({
|
|
285
|
-
body: {
|
|
295
|
+
body: {
|
|
296
|
+
parentID: context.sessionID,
|
|
297
|
+
title: `raven_seek: ${args.query.slice(0, 80)}`,
|
|
298
|
+
},
|
|
286
299
|
})
|
|
287
300
|
|
|
288
301
|
const sessionId = (session as any)?.data?.id ?? (session as any)?.id
|
|
@@ -290,14 +303,32 @@ export default ((input: PluginInput) => {
|
|
|
290
303
|
return { title: "Raven Seek", output: "Failed to create Raven session." }
|
|
291
304
|
}
|
|
292
305
|
|
|
293
|
-
//
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
306
|
+
// Emit sessionId so the TUI renders a clickable delegation box
|
|
307
|
+
context.metadata({ metadata: { sessionId } })
|
|
308
|
+
|
|
309
|
+
// Log session for debugging
|
|
310
|
+
try {
|
|
311
|
+
const logFile = join(tmpdir(), "raven-sessions.log")
|
|
312
|
+
const ts = new Date().toISOString()
|
|
313
|
+
const q = String(args.query).slice(0, 100)
|
|
314
|
+
appendFileSync(logFile, `${ts} ${sessionId} "${q}"\n`)
|
|
315
|
+
} catch { /* non-fatal */ }
|
|
316
|
+
|
|
317
|
+
// Send the query to Raven with timeout
|
|
318
|
+
const result = await Promise.race([
|
|
319
|
+
client.session.prompt({
|
|
320
|
+
path: { id: sessionId },
|
|
321
|
+
body: {
|
|
322
|
+
agent: "raven",
|
|
323
|
+
parts: [{ type: "text", text: args.query }],
|
|
324
|
+
},
|
|
325
|
+
}),
|
|
326
|
+
new Promise<never>((_, reject) =>
|
|
327
|
+
setTimeout(() => reject(new Error(`Raven timed out after ${timeout / 1000}s — session kept: ${sessionId}`)), timeout)
|
|
328
|
+
),
|
|
329
|
+
])
|
|
330
|
+
|
|
331
|
+
const elapsed = ((Date.now() - started) / 1000).toFixed(1)
|
|
301
332
|
|
|
302
333
|
// Extract text from the response
|
|
303
334
|
const parts = (result as any)?.data?.parts ?? []
|
|
@@ -306,25 +337,21 @@ export default ((input: PluginInput) => {
|
|
|
306
337
|
.map((p: any) => p.text)
|
|
307
338
|
const output = textParts.join("\n") || "Raven returned no results."
|
|
308
339
|
|
|
309
|
-
// Clean up the session
|
|
310
|
-
try {
|
|
311
|
-
await client.session.delete({ path: { id: sessionId } })
|
|
312
|
-
} catch { /* non-fatal */ }
|
|
313
|
-
|
|
314
340
|
// Track context saved
|
|
315
341
|
addBytes(output.length)
|
|
316
342
|
|
|
317
|
-
return { title: "Raven Seek", output }
|
|
343
|
+
return { title: "Raven Seek", metadata: { sessionId }, output: `${output}\n\n*Raven searched for ${elapsed}s — ${formatBytes(output.length)}, ~${formatTokens(output.length)} tokens*` }
|
|
318
344
|
} catch (err: any) {
|
|
345
|
+
const elapsed = ((Date.now() - started) / 1000).toFixed(1)
|
|
319
346
|
const msg = String(err?.message ?? err ?? "").toLowerCase()
|
|
320
347
|
const hint =
|
|
321
348
|
/rate.?limit|too many requests|429/i.test(msg) ? "Raven rate limited — wait 30s then retry with a narrower query."
|
|
322
349
|
: /quota|usage.?limit|billing|insufficient.*(?:credit|balance|quota)/i.test(msg) ? "Raven API quota exhausted — proceed without search, tell user what's missing."
|
|
323
350
|
: /token|context.?length|too large|too long/i.test(msg) ? "Raven query too large — shorten your query and retry."
|
|
324
351
|
: /model|unavailable|down|not found/i.test(msg) ? "Raven model unavailable — retry later, or proceed without search."
|
|
325
|
-
: /timeout|timed.?out|
|
|
352
|
+
: /timeout|timed.?out|session kept/i.test(msg) ? err.message
|
|
326
353
|
: `Raven search failed. Proceed without search — note gaps for the user. [${err.message || err}]`
|
|
327
|
-
return { title: "Raven Seek", output: hint }
|
|
354
|
+
return { title: "Raven Seek", output: `${hint}\n\n*Attempt took ${elapsed}s*` }
|
|
328
355
|
}
|
|
329
356
|
},
|
|
330
357
|
}),
|
|
@@ -340,7 +367,7 @@ export default ((input: PluginInput) => {
|
|
|
340
367
|
}
|
|
341
368
|
},
|
|
342
369
|
|
|
343
|
-
// /raven on|off|model <name>|status
|
|
370
|
+
// /raven on|off|model <name>|effort <value>|timeout <seconds>|stats|status
|
|
344
371
|
"command.execute.before"(input: any, output: any) {
|
|
345
372
|
if (input.command !== "raven") return
|
|
346
373
|
output.parts.length = 0
|
|
@@ -375,11 +402,21 @@ export default ((input: PluginInput) => {
|
|
|
375
402
|
saveConfig(config)
|
|
376
403
|
output.parts.push({ type: "text", text: `Raven reasoning effort set to: ${effort}\nRestart opencode for the change to take effect.` })
|
|
377
404
|
}
|
|
405
|
+
} else if (arg.startsWith("timeout ")) {
|
|
406
|
+
const secs = parseInt(raw.slice(8).trim(), 10)
|
|
407
|
+
if (!secs || secs < 10) {
|
|
408
|
+
output.parts.push({ type: "text", text: `Usage: /raven timeout <seconds>\nMust be at least 10. Current: ${config.timeout ?? 180}s` })
|
|
409
|
+
} else {
|
|
410
|
+
config.timeout = secs
|
|
411
|
+
saveConfig(config)
|
|
412
|
+
output.parts.push({ type: "text", text: `Raven timeout set to ${secs}s. Takes effect immediately.` })
|
|
413
|
+
}
|
|
378
414
|
} else {
|
|
379
415
|
const enabled = config.enabled ? "enabled" : "disabled"
|
|
380
416
|
const model = config.model || fm.model || "(default)"
|
|
381
417
|
const effort = config.reasoning_effort || fm.reasoning_effort || "(default)"
|
|
382
|
-
|
|
418
|
+
const timeout = config.timeout ?? 180
|
|
419
|
+
output.parts.push({ type: "text", text: `Raven is ${enabled}. Model: ${model}. Reasoning: ${effort}. Timeout: ${timeout}s\n\nCommands:\n /raven on — enable search interception\n /raven off — disable search interception\n /raven model <name> — change Raven's model (requires restart)\n /raven effort <value> — change Raven's reasoning effort (requires restart)\n /raven timeout <seconds> — change raven_seek timeout\n /raven stats — show blocked calls and context saved` })
|
|
383
420
|
}
|
|
384
421
|
},
|
|
385
422
|
|
|
@@ -392,11 +429,14 @@ export default ((input: PluginInput) => {
|
|
|
392
429
|
// ── Subagent prompt injection: inject Raven guidance into every subagent ──
|
|
393
430
|
if ((input.tool === "task" || input.tool === "subtask") && output.args) {
|
|
394
431
|
const subagentType = input.tool === "task" ? (output.args.subagent_type ?? "") : ""
|
|
432
|
+
if (subagentType === "raven") {
|
|
433
|
+
ravenTaskCalls.add(input.callID)
|
|
434
|
+
}
|
|
395
435
|
if (subagentType !== "raven" && !isExcluded(subagentType)) {
|
|
396
436
|
const field = ["prompt", "description", "request", "objective", "query"].find(
|
|
397
437
|
(f) => f in output.args
|
|
398
438
|
) ?? "prompt"
|
|
399
|
-
output.args[field] = `${output.args[field] ?? ""}\n\n<raven_guidance>\nSearch tools (grep, glob,
|
|
439
|
+
output.args[field] = `${output.args[field] ?? ""}\n\n<raven_guidance>\nSearch tools (grep, glob, ls, dir, bash search commands) are blocked. Use raven_seek(query=\"...\") for ALL searches — local codebase, web, docs, and GitHub examples.\n</raven_guidance>`
|
|
400
440
|
}
|
|
401
441
|
}
|
|
402
442
|
|
|
@@ -410,7 +450,11 @@ export default ((input: PluginInput) => {
|
|
|
410
450
|
},
|
|
411
451
|
|
|
412
452
|
"tool.execute.after"(input: any, output: any) {
|
|
413
|
-
|
|
453
|
+
if (ravenTaskCalls.has(input.callID)) {
|
|
454
|
+
ravenTaskCalls.delete(input.callID)
|
|
455
|
+
const outputLen = String(output.output ?? "").length
|
|
456
|
+
if (outputLen > 0) addBytes(outputLen)
|
|
457
|
+
}
|
|
414
458
|
},
|
|
415
459
|
}
|
|
416
460
|
}) satisfies Plugin
|
package/mcp-guidance.md
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-raven",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.4",
|
|
4
4
|
"description": "Search-first subagent for opencode — intercepts search tools and routes them through a hidden Raven agent with Context7, Exa AI, and Grep.app MCPs",
|
|
5
5
|
"main": "./index.ts",
|
|
6
6
|
"exports": {
|