| 186 | } |
| 187 | |
| 188 | char *cbm_client_adapter_opencode(const char *binary_path) { |
| 189 | if (!binary_path || !binary_path[0]) { |
| 190 | return NULL; |
| 191 | } |
| 192 | char bin[CBM_SZ_1K]; |
| 193 | if (!cbm_client_adapter_escape_js(binary_path, bin, sizeof(bin))) { |
| 194 | return NULL; |
| 195 | } |
| 196 | |
| 197 | adapter_sb_t sb = {0}; |
| 198 | emit_header(&sb, "OpenCode"); |
| 199 | sb_append(&sb, "// OpenCode already reaches every tool over MCP; this adds only the\n" |
| 200 | "// automatic graph lookup before a grep/glob, which other clients get\n" |
| 201 | "// through their own hook configuration.\n"); |
| 202 | sb_append(&sb, "import { spawn } from 'node:child_process';\n\n"); |
| 203 | sb_append(&sb, "const BIN = '"); |
| 204 | sb_append(&sb, bin); |
| 205 | sb_append(&sb, "';\n\n"); |
| 206 | |
| 207 | /* hook-augment requires hook_event_name and accepts Grep/Glob only under |
| 208 | * PreToolUse; omitting it makes the whole hook a silent no-op. */ |
| 209 | sb_append(&sb, "function augment(tool, args) {\n" |
| 210 | " return new Promise((resolve) => {\n" |
| 211 | " const child = spawn(BIN, ['hook-augment'], {\n" |
| 212 | " stdio: ['pipe', 'pipe', 'ignore'],\n" |
| 213 | " env: { ...process.env, CBM_LOG_LEVEL: 'error' },\n" |
| 214 | " });\n" |
| 215 | " let out = '';\n" |
| 216 | " child.stdout.on('data', (d) => (out += d.toString()));\n" |
| 217 | " child.on('error', () => resolve(''));\n" |
| 218 | " child.on('close', () => resolve(out));\n" |
| 219 | " child.stdin.end(JSON.stringify({\n" |
| 220 | " hook_event_name: 'PreToolUse',\n" |
| 221 | " tool_name: tool,\n" |
| 222 | " tool_input: args ?? {},\n" |
| 223 | " }));\n" |
| 224 | " });\n" |
| 225 | "}\n\n"); |
| 226 | |
| 227 | sb_append(&sb, |
| 228 | "export const CodebaseMemory = async () => ({\n" |
| 229 | " 'tool.execute.after': async (input, output) => {\n" |
| 230 | " const tool = input?.tool === 'grep' ? 'Grep' : input?.tool === 'glob' ? 'Glob' " |
| 231 | ": null;\n" |
| 232 | " if (!tool) return;\n" |
| 233 | " const extra = await augment(tool, output?.args);\n" |
| 234 | " if (extra && typeof output?.output === 'string') {\n" |
| 235 | " output.output += '\\n' + extra;\n" |
| 236 | " }\n" |
| 237 | " },\n" |
| 238 | "});\n"); |
| 239 | |
| 240 | if (sb.failed) { |
| 241 | free(sb.buf); |
| 242 | return NULL; |
| 243 | } |
| 244 | return sb.buf; |
| 245 | } |
no test coverage detected