Skip to content

Commit 7a012ca

Browse files
committed
fix(tool): ignore invalid custom tool exports
1 parent af06e52 commit 7a012ca

2 files changed

Lines changed: 33 additions & 1 deletion

File tree

packages/opencode/src/tool/registry.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,8 @@ export const layer: Layer.Layer<
201201
// `match` is an absolute filesystem path from `Glob.scanSync(..., { absolute: true })`.
202202
// Import it as `file://` so Node on Windows accepts the dynamic import.
203203
const mod = yield* Effect.promise(() => import(pathToFileURL(match).href))
204-
for (const [id, def] of Object.entries<ToolDefinition>(mod)) {
204+
for (const [id, def] of Object.entries(mod)) {
205+
if (!isPluginTool(def)) continue
205206
custom.push(fromPlugin(id === "default" ? namespace : `${namespace}_${id}`, def))
206207
}
207208
}
@@ -396,6 +397,10 @@ function isZodType(value: unknown): value is z.ZodType {
396397
return typeof value === "object" && value !== null && "_zod" in value
397398
}
398399

400+
function isPluginTool(value: unknown): value is ToolDefinition {
401+
return typeof value === "object" && value !== null && "args" in value && "description" in value && "execute" in value
402+
}
403+
399404
function isJsonSchemaDefinition(value: unknown): value is JSONSchema7Definition {
400405
return typeof value === "boolean" || (typeof value === "object" && value !== null && !Array.isArray(value))
401406
}

packages/opencode/test/tool/registry.test.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,33 @@ describe("tool.registry", () => {
158158
}),
159159
)
160160

161+
it.instance("ignores non-tool exports in .opencode/tool files", () =>
162+
Effect.gen(function* () {
163+
const test = yield* TestInstance
164+
const tool = path.join(test.directory, ".opencode", "tool")
165+
yield* Effect.promise(() => fs.mkdir(tool, { recursive: true }))
166+
yield* Effect.promise(() =>
167+
Bun.write(
168+
path.join(tool, "mixed.ts"),
169+
[
170+
"export const helper = 'not a tool'",
171+
"export default {",
172+
" description: 'mixed tool',",
173+
" args: {},",
174+
" execute: async () => 'ok',",
175+
"}",
176+
"",
177+
].join("\n"),
178+
),
179+
)
180+
181+
const registry = yield* ToolRegistry.Service
182+
const ids = yield* registry.ids()
183+
expect(ids).toContain("mixed")
184+
expect(ids).not.toContain("mixed_helper")
185+
}),
186+
)
187+
161188
it.instance("loads tools from .opencode/tools (plural)", () =>
162189
Effect.gen(function* () {
163190
const test = yield* TestInstance

0 commit comments

Comments
 (0)