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.
- package/README.md +6 -2
- package/index.ts +83 -42
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# opencode-raven
|
|
2
2
|
|
|
3
|
+
[](https://www.npmjs.com/package/opencode-raven)
|
|
4
|
+
[](https://www.npmjs.com/package/opencode-raven)
|
|
5
|
+
[](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
|
-
##
|
|
315
|
+
## Disclaimer
|
|
312
316
|
|
|
313
|
-
|
|
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
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
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:
|
|
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
|
|
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