From ccc4de8422dd0042c42d7597878460295d51c60e Mon Sep 17 00:00:00 2001 From: Saif Al Naimi Date: Sat, 29 Aug 2026 13:13:34 +0000 Subject: [PATCH 1/2] Add devcontainer config with Bun and build tools --- .devcontainer/devcontainer.json | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .devcontainer/devcontainer.json diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 00000000..6a1bd309 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,8 @@ +{ + "name": "opencode-dev", + "image": "mcr.microsoft.com/devcontainers/base:ubuntu", + "postCreateCommand": "sudo apt-get update && sudo apt-get install -y python3 make g++ && curl -fsSL https://bun.sh/install | bash", + "remoteEnv": { + "PATH": "${containerEnv:PATH}:/home/vscode/.bun/bin" + } +} \ No newline at end of file From 36ebfbeba56270aac91bb9c77eb8942309e049ab Mon Sep 17 00:00:00 2001 From: Saif Al Naimi Date: Wed, 2 Sep 2026 07:20:12 +0000 Subject: [PATCH 2/2] refactor(core): bundle callMcp parameters into an options object Qlty flagged packages/core/src/tool/websearch.ts:152 as "Function with many parameters (count = 6): callMcp" (http, url, tool, args, value, headers). Replace the positional parameter list with a single McpCallOptions object and update both call sites (Exa and Parallel) accordingly. Behavior is unchanged. Validated with `qlty smells --no-snippets packages/core/src/tool/websearch.ts`, which now reports no smells for the file (was 1 before). Covered by the existing packages/core/test/tool-websearch.test.ts suite (12/12 passing), which exercises callMcp indirectly through both providers, including header/body/URL assembly, byte-limit truncation, and timeout handling. --- packages/core/src/tool/websearch.ts | 50 +++++++++++++++++------------ 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/packages/core/src/tool/websearch.ts b/packages/core/src/tool/websearch.ts index 6d622363..3329e7bc 100644 --- a/packages/core/src/tool/websearch.ts +++ b/packages/core/src/tool/websearch.ts @@ -149,14 +149,16 @@ const exaUrl = (apiKey: string | undefined) => { return url.toString() } -const callMcp = ( - http: HttpClient.HttpClient, - url: string, - tool: string, - args: Schema.Struct, - value: Schema.Struct.Type, - headers: Record = {}, -) => +interface McpCallOptions { + readonly http: HttpClient.HttpClient + readonly url: string + readonly tool: string + readonly args: Schema.Struct + readonly value: Schema.Struct.Type + readonly headers?: Record +} + +const callMcp = ({ http, url, tool, args, value, headers = {} }: McpCallOptions) => Effect.gen(function* () { const request = yield* HttpClientRequest.post(url).pipe( HttpClientRequest.accept("application/json, text/event-stream"), @@ -218,29 +220,35 @@ const layer = Layer.effectDiscard( const text = provider === "exa" - ? yield* callMcp(http, exaUrl(config.exaApiKey), "web_search_exa", ExaArgs, { - query: input.query, - type: input.type || "auto", - numResults: input.numResults || 8, - livecrawl: input.livecrawl || "fallback", - contextMaxCharacters: input.contextMaxCharacters, + ? yield* callMcp({ + http, + url: exaUrl(config.exaApiKey), + tool: "web_search_exa", + args: ExaArgs, + value: { + query: input.query, + type: input.type || "auto", + numResults: input.numResults || 8, + livecrawl: input.livecrawl || "fallback", + contextMaxCharacters: input.contextMaxCharacters, + }, }) - : yield* callMcp( + : yield* callMcp({ http, - PARALLEL_URL, - "web_search", - ParallelArgs, - { + url: PARALLEL_URL, + tool: "web_search", + args: ParallelArgs, + value: { objective: input.query, search_queries: [input.query], session_id: context.sessionID, // V2 invocation context does not safely expose the model yet. }, - { + headers: { "User-Agent": `opencode/${InstallationVersion}`, ...(config.parallelApiKey ? { Authorization: `Bearer ${config.parallelApiKey}` } : {}), }, - ) + }) return { provider, text: text ?? NO_RESULTS,