refactor(core): migrate glob tool to internal plugin (#34946)

This commit is contained in:
Kit Langton
2026-07-02 10:39:04 -04:00
committed by GitHub
parent dbaa53329c
commit 016d296f7c
4 changed files with 42 additions and 72 deletions
+33 -52
View File
@@ -3,7 +3,7 @@ export * as PluginInternal from "./internal"
import { makeLocationNode } from "../effect/app-node" import { makeLocationNode } from "../effect/app-node"
import { httpClient } from "../effect/app-node-platform" import { httpClient } from "../effect/app-node-platform"
import type { PluginContext } from "@opencode-ai/plugin/v2/effect" import type { PluginContext } from "@opencode-ai/plugin/v2/effect"
import { Effect, Layer, Scope } from "effect" import { Context, Effect, Layer, Scope } from "effect"
import { AgentV2 } from "../agent" import { AgentV2 } from "../agent"
import { Catalog } from "../catalog" import { Catalog } from "../catalog"
import { CommandV2 } from "../command" import { CommandV2 } from "../command"
@@ -27,6 +27,7 @@ import { PluginV2 } from "../plugin"
import { PluginRuntime } from "../plugin/runtime" import { PluginRuntime } from "../plugin/runtime"
import { PermissionV2 } from "../permission" import { PermissionV2 } from "../permission"
import { Reference } from "../reference" import { Reference } from "../reference"
import { Ripgrep } from "../ripgrep"
import { Shell } from "../shell" import { Shell } from "../shell"
import { SkillV2 } from "../skill" import { SkillV2 } from "../skill"
import { State } from "../state" import { State } from "../state"
@@ -40,6 +41,7 @@ import { ProviderPlugins } from "./provider"
import { SdkPlugins } from "./sdk" import { SdkPlugins } from "./sdk"
import { SkillPlugin } from "./skill" import { SkillPlugin } from "./skill"
import { VariantPlugin } from "./variant" import { VariantPlugin } from "./variant"
import { GlobTool } from "../tool/glob"
import { ShellTool } from "../tool/shell" import { ShellTool } from "../tool/shell"
import { SubagentTool } from "../tool/subagent" import { SubagentTool } from "../tool/subagent"
@@ -61,6 +63,7 @@ export type Requirements =
| PermissionV2.Service | PermissionV2.Service
| PluginRuntime.Service | PluginRuntime.Service
| Reference.Service | Reference.Service
| Ripgrep.Service
| Shell.Service | Shell.Service
| SkillV2.Service | SkillV2.Service
| Tools.Service | Tools.Service
@@ -76,59 +79,35 @@ export function define<R>(plugin: Plugin<R>) {
const layer = Layer.effectDiscard( const layer = Layer.effectDiscard(
Effect.gen(function* () { Effect.gen(function* () {
const catalog = yield* Catalog.Service
const commands = yield* CommandV2.Service
const plugin = yield* PluginV2.Service const plugin = yield* PluginV2.Service
const sdkPlugins = yield* SdkPlugins.Service const sdkPlugins = yield* SdkPlugins.Service
const integration = yield* Integration.Service const services = Context.mergeAll(
const agents = yield* AgentV2.Service Context.make(Catalog.Service, yield* Catalog.Service),
const config = yield* Config.Service Context.make(CommandV2.Service, yield* CommandV2.Service),
const location = yield* Location.Service Context.make(Integration.Service, yield* Integration.Service),
const modelsDev = yield* ModelsDev.Service Context.make(AgentV2.Service, yield* AgentV2.Service),
const npm = yield* Npm.Service Context.make(Config.Service, yield* Config.Service),
const events = yield* EventV2.Service Context.make(Location.Service, yield* Location.Service),
const fs = yield* FSUtil.Service Context.make(ModelsDev.Service, yield* ModelsDev.Service),
const filesystem = yield* FileSystem.Service Context.make(Npm.Service, yield* Npm.Service),
const global = yield* Global.Service Context.make(EventV2.Service, yield* EventV2.Service),
const http = yield* HttpClient.HttpClient Context.make(FSUtil.Service, yield* FSUtil.Service),
const mutation = yield* LocationMutation.Service Context.make(FileSystem.Service, yield* FileSystem.Service),
const permission = yield* PermissionV2.Service Context.make(Global.Service, yield* Global.Service),
const skill = yield* SkillV2.Service Context.make(HttpClient.HttpClient, yield* HttpClient.HttpClient),
const reference = yield* Reference.Service Context.make(LocationMutation.Service, yield* LocationMutation.Service),
const shell = yield* Shell.Service Context.make(PermissionV2.Service, yield* PermissionV2.Service),
const tools = yield* Tools.Service Context.make(SkillV2.Service, yield* SkillV2.Service),
const runtime = yield* PluginRuntime.Service Context.make(Reference.Service, yield* Reference.Service),
const add = <R>(input: Plugin<R>) => { Context.make(Ripgrep.Service, yield* Ripgrep.Service),
const loaded = { Context.make(Shell.Service, yield* Shell.Service),
id: input.id, Context.make(Tools.Service, yield* Tools.Service),
effect: (context: PluginContext) => Context.make(PluginRuntime.Service, yield* PluginRuntime.Service),
input )
.effect(context) const add = (input: Plugin<Requirements | Scope.Scope>) =>
.pipe( plugin.add(PluginV2.ID.make(input.id), (context: PluginContext) =>
Effect.provideService(Catalog.Service, catalog), input.effect(context).pipe(Effect.provide(services)),
Effect.provideService(CommandV2.Service, commands), )
Effect.provideService(Integration.Service, integration),
Effect.provideService(AgentV2.Service, agents),
Effect.provideService(Config.Service, config),
Effect.provideService(Location.Service, location),
Effect.provideService(ModelsDev.Service, modelsDev),
Effect.provideService(Npm.Service, npm),
Effect.provideService(EventV2.Service, events),
Effect.provideService(FSUtil.Service, fs),
Effect.provideService(FileSystem.Service, filesystem),
Effect.provideService(Global.Service, global),
Effect.provideService(HttpClient.HttpClient, http),
Effect.provideService(LocationMutation.Service, mutation),
Effect.provideService(PermissionV2.Service, permission),
Effect.provideService(SkillV2.Service, skill),
Effect.provideService(Reference.Service, reference),
Effect.provideService(Shell.Service, shell),
Effect.provideService(Tools.Service, tools),
Effect.provideService(PluginRuntime.Service, runtime),
),
}
return plugin.add(PluginV2.ID.make(loaded.id), loaded.effect)
}
yield* State.batch( yield* State.batch(
Effect.gen(function* () { Effect.gen(function* () {
@@ -138,6 +117,7 @@ const layer = Layer.effectDiscard(
yield* add(SkillPlugin.Plugin) yield* add(SkillPlugin.Plugin)
yield* add(ModelsDevPlugin) yield* add(ModelsDevPlugin)
yield* add(ConfigExternalPlugin.Plugin) yield* add(ConfigExternalPlugin.Plugin)
yield* add(GlobTool.Plugin)
yield* add(ShellTool.Plugin) yield* add(ShellTool.Plugin)
yield* add(SubagentTool.Plugin) yield* add(SubagentTool.Plugin)
yield* add(ConfigAgentPlugin.Plugin) yield* add(ConfigAgentPlugin.Plugin)
@@ -175,6 +155,7 @@ export const node = makeLocationNode({
PermissionV2.node, PermissionV2.node,
SkillV2.node, SkillV2.node,
Reference.node, Reference.node,
Ripgrep.node,
Shell.node, Shell.node,
ToolRegistry.toolsNode, ToolRegistry.toolsNode,
PluginRuntime.node, PluginRuntime.node,
-2
View File
@@ -4,7 +4,6 @@ import { makeLocationNode } from "../effect/app-node"
import { Context, Layer } from "effect" import { Context, Layer } from "effect"
import { ApplyPatchTool } from "./apply-patch" import { ApplyPatchTool } from "./apply-patch"
import { EditTool } from "./edit" import { EditTool } from "./edit"
import { GlobTool } from "./glob"
import { GrepTool } from "./grep" import { GrepTool } from "./grep"
import { QuestionTool } from "./question" import { QuestionTool } from "./question"
import { ReadTool } from "./read" import { ReadTool } from "./read"
@@ -38,7 +37,6 @@ export const node = makeLocationNode({
deps: [ deps: [
ApplyPatchTool.node, ApplyPatchTool.node,
EditTool.node, EditTool.node,
GlobTool.node,
GrepTool.node, GrepTool.node,
QuestionTool.node, QuestionTool.node,
ReadTool.node, ReadTool.node,
+7 -15
View File
@@ -1,17 +1,15 @@
export * as GlobTool from "./glob" export * as GlobTool from "./glob"
import { ToolFailure } from "@opencode-ai/llm" import { ToolFailure } from "@opencode-ai/llm"
import { Effect, Layer, Schema } from "effect" import type { PluginContext } from "@opencode-ai/plugin/v2/effect"
import { Effect, Schema } from "effect"
import path from "path" import path from "path"
import { makeLocationNode } from "../effect/app-node"
import { FileSystem } from "../filesystem" import { FileSystem } from "../filesystem"
import { Location } from "../location" import { Location } from "../location"
import { Ripgrep } from "../ripgrep" import { Ripgrep } from "../ripgrep"
import { RelativePath } from "../schema" import { RelativePath } from "../schema"
import { PermissionV2 } from "../permission" import { PermissionV2 } from "../permission"
import { ToolRegistry } from "./registry"
import { Tool } from "./tool" import { Tool } from "./tool"
import { Tools } from "./tools"
export const name = "glob" export const name = "glob"
@@ -35,14 +33,14 @@ export const toModelOutput = (output: ModelOutput) => {
} }
/** Glob leaf that defaults its filesystem root to the active Location. */ /** Glob leaf that defaults its filesystem root to the active Location. */
const layer = Layer.effectDiscard( export const Plugin = {
Effect.gen(function* () { id: "core-glob-tool",
const tools = yield* Tools.Service effect: Effect.fn("GlobTool.Plugin")(function* (ctx: PluginContext) {
const ripgrep = yield* Ripgrep.Service const ripgrep = yield* Ripgrep.Service
const location = yield* Location.Service const location = yield* Location.Service
const permission = yield* PermissionV2.Service const permission = yield* PermissionV2.Service
yield* tools yield* ctx.tool
.register({ .register({
[name]: Tool.make({ [name]: Tool.make({
description: description:
@@ -96,10 +94,4 @@ const layer = Layer.effectDiscard(
}) })
.pipe(Effect.orDie) .pipe(Effect.orDie)
}), }),
) }
export const node = makeLocationNode({
name: "tool/glob",
layer,
deps: [ToolRegistry.node, Ripgrep.node, Location.node, PermissionV2.node],
})
+2 -3
View File
@@ -24,9 +24,7 @@ import { EventV2 } from "../src/event"
import { Reference } from "../src/reference" import { Reference } from "../src/reference"
import { ToolRegistry } from "../src/tool/registry" import { ToolRegistry } from "../src/tool/registry"
const it = testEffect( const it = testEffect(AppNodeBuilder.build(LayerNode.group([Database.node, EventV2.node, LocationServiceMap.node])))
AppNodeBuilder.build(LayerNode.group([Database.node, EventV2.node, LocationServiceMap.node])),
)
describe("LocationServiceMap", () => { describe("LocationServiceMap", () => {
it.live("reuses cached services for constructed and decoded location refs", () => it.live("reuses cached services for constructed and decoded location refs", () =>
@@ -75,6 +73,7 @@ describe("LocationServiceMap", () => {
const catalog = yield* Catalog.Service const catalog = yield* Catalog.Service
yield* catalog.transform((editor) => editor.provider.update(ProviderV2.ID.make("test"), () => {})) yield* catalog.transform((editor) => editor.provider.update(ProviderV2.ID.make("test"), () => {}))
const registry = yield* ToolRegistry.Service const registry = yield* ToolRegistry.Service
yield* waitForTool(registry, "glob")
yield* waitForTool(registry, "shell") yield* waitForTool(registry, "shell")
yield* waitForTool(registry, "subagent") yield* waitForTool(registry, "subagent")
return { return {