agent_harness_adapter_core/opencode/
adapter.rs1use crate::{
12 Result,
13 common::{extension, plugin},
14 harness::{Context, Harness, Part, PartResult, Reads, Scope},
15 hook::{Answer, Event, HookInput, Output, ToolCall, claude_names},
16 integration::{Integration, Item},
17};
18
19#[derive(Debug, Clone, Copy, Default)]
21pub struct OpenCode;
22
23const ID: &str = "opencode";
24
25const LAYOUT: plugin::Layout = plugin::Layout {
32 harness: ID,
33 agent: "OpenCode",
34 types: "@opencode-ai/plugin",
35 project_dir: ".opencode",
36 user_dir: ".config/opencode",
37 config: "opencode.json",
38 also_config: &[],
39};
40
41impl Harness for OpenCode {
42 fn id(&self) -> &str {
43 ID
44 }
45
46 fn hook_events(&self) -> &[Event] {
49 &[
50 Event::SessionStart,
51 Event::PromptSubmit,
52 Event::PreTool,
53 Event::PostTool,
54 Event::Stop,
55 Event::PreCompact,
56 ]
57 }
58
59 fn program(&self) -> Option<&str> {
61 Some("opencode")
62 }
63
64 fn claude_tool(&self, call: &ToolCall) -> ToolCall {
65 claude_names::named(call, plugin::claude_name(&call.name))
66 }
67
68 fn scopes(&self) -> &[Scope] {
70 &[Scope::Project, Scope::User]
71 }
72
73 fn render(&self, integration: &Integration, cx: &Context) -> Result<Vec<Part>> {
78 LAYOUT.render(integration, cx)
79 }
80
81 fn reads(&self, item: Item, cx: &Context) -> Result<Reads> {
82 Ok(LAYOUT.reads(item, cx))
83 }
84
85 fn parse_hook(&self, event: Event, text: &str) -> serde_json::Result<HookInput> {
87 extension::parse(ID, event, text, plugin::tool_kind)
88 }
89
90 fn answer(&self, event: Event, answer: &Answer) -> Result<Output> {
93 extension::answer(ID, event, answer)
94 }
95
96 fn notes(&self, _cx: &Context, parts: &[PartResult]) -> Vec<String> {
98 if parts.iter().any(PartResult::changed) {
99 vec!["restart OpenCode to load the changes".into()]
100 } else {
101 Vec::new()
102 }
103 }
104}
105
106#[cfg(test)]
107#[path = "tests.rs"]
108mod tests;