The cross-spawn-spawner module has been moved from src/effect/ to src/ to simplify the core package structure. The src/types.d.ts file which contained unused type declarations has also been removed. All imports throughout the codebase have been updated to reflect the new location. This change reduces the package's internal complexity by flattening the module hierarchy and removing dead code, making future maintenance easier.
153 lines
4.7 KiB
TypeScript
153 lines
4.7 KiB
TypeScript
import { afterEach, describe, expect } from "bun:test"
|
|
import path from "path"
|
|
import fs from "fs/promises"
|
|
import { Effect, Layer } from "effect"
|
|
import { Instance } from "../../src/project/instance"
|
|
import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner"
|
|
import { ToolRegistry } from "../../src/tool"
|
|
import { provideTmpdirInstance } from "../fixture/fixture"
|
|
import { testEffect } from "../lib/effect"
|
|
|
|
const node = CrossSpawnSpawner.defaultLayer
|
|
|
|
const it = testEffect(Layer.mergeAll(ToolRegistry.defaultLayer, node))
|
|
|
|
afterEach(async () => {
|
|
await Instance.disposeAll()
|
|
})
|
|
|
|
describe("tool.registry", () => {
|
|
it.live("loads tools from .opencode/tool (singular)", () =>
|
|
provideTmpdirInstance((dir) =>
|
|
Effect.gen(function* () {
|
|
const opencode = path.join(dir, ".opencode")
|
|
const tool = path.join(opencode, "tool")
|
|
yield* Effect.promise(() => fs.mkdir(tool, { recursive: true }))
|
|
yield* Effect.promise(() =>
|
|
Bun.write(
|
|
path.join(tool, "hello.ts"),
|
|
[
|
|
"export default {",
|
|
" description: 'hello tool',",
|
|
" args: {},",
|
|
" execute: async () => {",
|
|
" return 'hello world'",
|
|
" },",
|
|
"}",
|
|
"",
|
|
].join("\n"),
|
|
),
|
|
)
|
|
const registry = yield* ToolRegistry.Service
|
|
const ids = yield* registry.ids()
|
|
expect(ids).toContain("hello")
|
|
}),
|
|
),
|
|
)
|
|
|
|
it.live("loads tools from .opencode/tools (plural)", () =>
|
|
provideTmpdirInstance((dir) =>
|
|
Effect.gen(function* () {
|
|
const opencode = path.join(dir, ".opencode")
|
|
const tools = path.join(opencode, "tools")
|
|
yield* Effect.promise(() => fs.mkdir(tools, { recursive: true }))
|
|
yield* Effect.promise(() =>
|
|
Bun.write(
|
|
path.join(tools, "hello.ts"),
|
|
[
|
|
"export default {",
|
|
" description: 'hello tool',",
|
|
" args: {},",
|
|
" execute: async () => {",
|
|
" return 'hello world'",
|
|
" },",
|
|
"}",
|
|
"",
|
|
].join("\n"),
|
|
),
|
|
)
|
|
const registry = yield* ToolRegistry.Service
|
|
const ids = yield* registry.ids()
|
|
expect(ids).toContain("hello")
|
|
}),
|
|
),
|
|
)
|
|
|
|
it.live("loads tools with external dependencies without crashing", () =>
|
|
provideTmpdirInstance((dir) =>
|
|
Effect.gen(function* () {
|
|
const opencode = path.join(dir, ".opencode")
|
|
const tools = path.join(opencode, "tools")
|
|
yield* Effect.promise(() => fs.mkdir(tools, { recursive: true }))
|
|
yield* Effect.promise(() =>
|
|
Bun.write(
|
|
path.join(opencode, "package.json"),
|
|
JSON.stringify({
|
|
name: "custom-tools",
|
|
dependencies: {
|
|
"@opencode-ai/plugin": "^0.0.0",
|
|
cowsay: "^1.6.0",
|
|
},
|
|
}),
|
|
),
|
|
)
|
|
yield* Effect.promise(() =>
|
|
Bun.write(
|
|
path.join(opencode, "package-lock.json"),
|
|
JSON.stringify({
|
|
name: "custom-tools",
|
|
lockfileVersion: 3,
|
|
packages: {
|
|
"": {
|
|
dependencies: {
|
|
"@opencode-ai/plugin": "^0.0.0",
|
|
cowsay: "^1.6.0",
|
|
},
|
|
},
|
|
},
|
|
}),
|
|
),
|
|
)
|
|
|
|
const cowsay = path.join(opencode, "node_modules", "cowsay")
|
|
yield* Effect.promise(() => fs.mkdir(cowsay, { recursive: true }))
|
|
yield* Effect.promise(() =>
|
|
Bun.write(
|
|
path.join(cowsay, "package.json"),
|
|
JSON.stringify({
|
|
name: "cowsay",
|
|
type: "module",
|
|
exports: "./index.js",
|
|
}),
|
|
),
|
|
)
|
|
yield* Effect.promise(() =>
|
|
Bun.write(
|
|
path.join(cowsay, "index.js"),
|
|
["export function say({ text }) {", " return `moo ${text}`", "}", ""].join("\n"),
|
|
),
|
|
)
|
|
yield* Effect.promise(() =>
|
|
Bun.write(
|
|
path.join(tools, "cowsay.ts"),
|
|
[
|
|
"import { say } from 'cowsay'",
|
|
"export default {",
|
|
" description: 'tool that imports cowsay at top level',",
|
|
" args: { text: { type: 'string' } },",
|
|
" execute: async ({ text }: { text: string }) => {",
|
|
" return say({ text })",
|
|
" },",
|
|
"}",
|
|
"",
|
|
].join("\n"),
|
|
),
|
|
)
|
|
const registry = yield* ToolRegistry.Service
|
|
const ids = yield* registry.ids()
|
|
expect(ids).toContain("cowsay")
|
|
}),
|
|
),
|
|
)
|
|
})
|