opencode-raven 2.1.4 → 2.1.5

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.
Files changed (3) hide show
  1. package/README.md +6 -2
  2. package/index.ts +83 -42
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # opencode-raven
2
2
 
3
+ [![npm version](https://img.shields.io/npm/v/opencode-raven.svg)](https://www.npmjs.com/package/opencode-raven)
4
+ [![npm downloads](https://img.shields.io/npm/dm/opencode-raven.svg)](https://www.npmjs.com/package/opencode-raven)
5
+ [![license](https://img.shields.io/npm/l/opencode-raven.svg)](LICENSE)
6
+
3
7
  <table>
4
8
  <tr>
5
9
  <td width="40%"><img src="Raven.png" alt="Raven" width="100%" /></td>
@@ -308,6 +312,6 @@ Raven itself has access to these tools, blocked for other agents when configured
308
312
 
309
313
  Raven returns compact findings: answer, sources, relevant details, recommended next step, and uncertainty.
310
314
 
311
- ## License
315
+ ## Disclaimer
312
316
 
313
- MIT
317
+ opencode-raven is not built by the OpenCode team and is not affiliated with OpenCode in any way.
package/index.ts CHANGED
@@ -475,12 +475,12 @@ export default ((input: PluginInput) => {
475
475
  } catch { /* non-fatal: generated metadata can be refreshed */ }
476
476
  }
477
477
 
478
- function saveDynamicGuidance() {
479
- try {
480
- mkdirSync(ravenConfigDir, { recursive: true })
481
- writeFileSync(dynamicGuidanceFile, `## Raven on-demand MCP capabilities\n\n${onDemandMcpGuidance("delegate")}\n`, "utf-8")
482
- } catch { /* non-fatal: guidance still exists in tool/subagent paths */ }
483
- }
478
+ function saveDynamicGuidance() {
479
+ try {
480
+ mkdirSync(ravenConfigDir, { recursive: true })
481
+ writeFileSync(dynamicGuidanceFile, `## Raven on-demand MCP capabilities\n\n${onDemandMcpGuidance("delegate", dynamicGuidanceMcpOrder())}\n`, "utf-8")
482
+ } catch { /* non-fatal: guidance still exists in tool/subagent paths */ }
483
+ }
484
484
 
485
485
  function cleanupOldRootFiles() {
486
486
  try {
@@ -615,32 +615,73 @@ Then restart opencode for the model change to take effect. If the task can be co
615
615
  return extra ? `${prompt}\n\nAdditional user instructions:\n${extra}` : prompt
616
616
  }
617
617
 
618
- function truncateText(value: unknown, max = 240): string {
619
- const text = String(value ?? "").replace(/\s+/g, " ").trim()
620
- return text.length > max ? `${text.slice(0, Math.max(0, max - 3))}...` : text
621
- }
622
-
623
- function onDemandMcpGuidance(target: "delegate" | "raven" = "delegate"): string {
624
- const servers = config.onDemandMcpServers ?? {}
625
- const activeEntries = activeOnDemandMcpEntries()
626
- const entries = activeEntries.filter(([name]) => onDemandMcpRuntimeStatus(name) === "ready")
627
- if (!Object.keys(servers).length) return "No on-demand MCPs are configured."
628
- if (!activeEntries.length) return "No enabled on-demand MCPs are configured."
629
- if (!entries.length) return "No loaded on-demand MCPs are available."
630
-
631
- const detail = config.onDemandMcpDescriptionDetail ?? "full"
632
- const lines = entries.map(([name, server]) => {
633
- const metadata = mcpMetadata[name] ?? {}
634
- const userDescription = truncateText(server.description, 220)
635
- const generated = truncateText(metadata.autoGeneratedDescription, 260)
636
- const parts = [userDescription, generated ? `Discovered capabilities: ${generated}` : ""].filter(Boolean)
637
- const summary = parts.length ? parts.join(" ") : "Description discovery pending."
638
- if (detail === "minimized" || !metadata.autoGeneratedTools?.length) return `- ${name}: ${summary}`
639
- const tools = metadata.autoGeneratedTools
640
- .map((item) => `${item.name}${item.description ? ` - ${truncateText(item.description, 140)}` : ""}`)
641
- .join("; ")
642
- return `- ${name}: ${summary} Tools: ${tools}`
643
- })
618
+ function compactText(value: unknown): string {
619
+ return String(value ?? "").replace(/\s+/g, " ").trim()
620
+ }
621
+
622
+ function truncateText(value: unknown, max = 240): string {
623
+ const text = compactText(value)
624
+ return text.length > max ? `${text.slice(0, Math.max(0, max - 3))}...` : text
625
+ }
626
+
627
+ function dynamicGuidanceMcpOrder(): string[] | undefined {
628
+ try {
629
+ if (!existsSync(dynamicGuidanceFile)) return undefined
630
+ const names: string[] = []
631
+ for (const line of readFileSync(dynamicGuidanceFile, "utf-8").split("\n")) {
632
+ const name = line.match(/^-\s+([^:]+):/)?.[1]?.trim()
633
+ if (name && !names.includes(name)) names.push(name)
634
+ }
635
+ return names
636
+ } catch {
637
+ return undefined
638
+ }
639
+ }
640
+
641
+ function orderedOnDemandMcpEntries(entries: [string, OnDemandMcpServer][], preferredOrder?: string[]): [string, OnDemandMcpServer][] {
642
+ if (!preferredOrder?.length) return entries
643
+ const order = new Map(preferredOrder.map((name, index) => [name.toLowerCase(), index]))
644
+ const existing = entries
645
+ .filter(([name]) => order.has(name.toLowerCase()))
646
+ .sort(([left], [right]) => order.get(left.toLowerCase())! - order.get(right.toLowerCase())!)
647
+ const added = entries.filter(([name]) => !order.has(name.toLowerCase()))
648
+ return [...existing, ...added]
649
+ }
650
+
651
+ function fullGuidanceText(value: unknown): string {
652
+ return compactText(value)
653
+ }
654
+
655
+ function firstSentenceText(value: unknown): string {
656
+ const text = compactText(value)
657
+ const period = text.indexOf(".")
658
+ return period === -1 ? text : text.slice(0, period + 1)
659
+ }
660
+
661
+ function onDemandMcpGuidance(target: "delegate" | "raven" = "delegate", preferredOrder?: string[]): string {
662
+ const servers = config.onDemandMcpServers ?? {}
663
+ const activeEntries = activeOnDemandMcpEntries()
664
+ const entries = orderedOnDemandMcpEntries(
665
+ activeEntries.filter(([name]) => onDemandMcpRuntimeStatus(name) === "ready"),
666
+ preferredOrder,
667
+ )
668
+ if (!Object.keys(servers).length) return "No on-demand MCPs are configured."
669
+ if (!activeEntries.length) return "No enabled on-demand MCPs are configured."
670
+ if (!entries.length) return "No loaded on-demand MCPs are available."
671
+
672
+ const detail = config.onDemandMcpDescriptionDetail ?? "full"
673
+ const lines = entries.map(([name, server]) => {
674
+ const metadata = mcpMetadata[name] ?? {}
675
+ const userDescription = firstSentenceText(server.description)
676
+ const generated = firstSentenceText(metadata.autoGeneratedDescription)
677
+ const parts = [userDescription, generated ? `Discovered capabilities: ${generated}` : ""].filter(Boolean)
678
+ const summary = parts.length ? parts.join(" ") : "Description discovery pending."
679
+ if (detail === "minimized" || !metadata.autoGeneratedTools?.length) return `- ${name}: ${summary}`
680
+ const tools = metadata.autoGeneratedTools
681
+ .map((item) => `${item.name}${item.description ? ` - ${firstSentenceText(item.description)}` : ""}`)
682
+ .join("; ")
683
+ return `- ${name}: ${summary} Tools: ${tools}`
684
+ })
644
685
  if (target === "raven") {
645
686
  return `Raven can handle these on-demand MCP capabilities through raven_mcp:\n${lines.join("\n")}`
646
687
  }
@@ -863,11 +904,11 @@ Then restart opencode for the model change to take effect. If the task can be co
863
904
 
864
905
  function generatedToolsFromMcpTools(tools: any[]): OnDemandMcpToolInfo[] {
865
906
  return tools
866
- .filter((item) => item?.name)
867
- .map((item) => ({
868
- name: String(item.name),
869
- ...(item.description ? { description: truncateText(item.description, 500) } : {}),
870
- }))
907
+ .filter((item) => item?.name)
908
+ .map((item) => ({
909
+ name: String(item.name),
910
+ ...(item.description ? { description: compactText(item.description) } : {}),
911
+ }))
871
912
  }
872
913
 
873
914
  function estimateAvoidedMcpSchemaBytes(tools: any[]): number {
@@ -876,11 +917,11 @@ Then restart opencode for the model change to take effect. If the task can be co
876
917
  }
877
918
 
878
919
  function synthesizeMcpDescription(serverName: string, tools: OnDemandMcpToolInfo[]): string {
879
- if (!tools.length) return `${serverName} exposes on-demand MCP capabilities. Use list_tools for available operations.`
880
- const descriptions = tools.map((item) => item.description || item.name).filter(Boolean)
881
- const joined = descriptions.slice(0, 6).join("; ")
882
- return truncateText(`${serverName} provides on-demand MCP tools for ${joined}.`, 300)
883
- }
920
+ if (!tools.length) return `${serverName} exposes on-demand MCP capabilities. Use list_tools for available operations.`
921
+ const descriptions = tools.map((item) => item.description || item.name).filter(Boolean)
922
+ const joined = descriptions.slice(0, 6).join("; ")
923
+ return compactText(`${serverName} provides on-demand MCP tools for ${joined}.`)
924
+ }
884
925
 
885
926
  async function refreshOnDemandMcpDescriptions(force = false, serverName?: string): Promise<{ refreshed: string[]; failed: string[] }> {
886
927
  const entries = activeOnDemandMcpEntries()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-raven",
3
- "version": "2.1.4",
3
+ "version": "2.1.5",
4
4
  "description": "Ready-to-use search tools and optional on-demand MCPs for OpenCode, routed through Raven to save your main model's context and cost.",
5
5
  "main": "./index.ts",
6
6
  "exports": {