refactor(core): migrate built-in tools to internal plugins (#34956)
This commit is contained in:
parent
5657cec4f2
commit
88dc960af8
25 changed files with 315 additions and 273 deletions
|
|
@ -1,18 +1,16 @@
|
|||
export * as ApplyPatchTool from "./apply-patch"
|
||||
|
||||
import type { PluginContext } from "@opencode-ai/plugin/v2/effect"
|
||||
import { ToolFailure } from "@opencode-ai/llm"
|
||||
import { FileDiff } from "@opencode-ai/schema/file-diff"
|
||||
import { createTwoFilesPatch, diffLines } from "diff"
|
||||
import { Effect, Layer, Schema } from "effect"
|
||||
import { makeLocationNode } from "../effect/app-node"
|
||||
import { Effect, Schema } from "effect"
|
||||
import { FileMutation } from "../file-mutation"
|
||||
import { FSUtil } from "../fs-util"
|
||||
import { LocationMutation } from "../location-mutation"
|
||||
import { Patch } from "../patch"
|
||||
import { PermissionV2 } from "../permission"
|
||||
import { ToolRegistry } from "./registry"
|
||||
import { Tool } from "./tool"
|
||||
import { Tools } from "./tools"
|
||||
|
||||
export const name = "apply_patch"
|
||||
|
||||
|
|
@ -56,15 +54,15 @@ type Prepared =
|
|||
readonly after: string
|
||||
})
|
||||
|
||||
const layer = Layer.effectDiscard(
|
||||
Effect.gen(function* () {
|
||||
const tools = yield* Tools.Service
|
||||
export const Plugin = {
|
||||
id: "core-apply-patch-tool",
|
||||
effect: Effect.fn("ApplyPatchTool.Plugin")(function* (ctx: PluginContext) {
|
||||
const mutation = yield* LocationMutation.Service
|
||||
const files = yield* FileMutation.Service
|
||||
const fs = yield* FSUtil.Service
|
||||
const permission = yield* PermissionV2.Service
|
||||
|
||||
yield* tools
|
||||
yield* ctx.tool
|
||||
.register({
|
||||
[name]: Tool.withPermission(
|
||||
Tool.make({
|
||||
|
|
@ -194,13 +192,7 @@ const layer = Layer.effectDiscard(
|
|||
})
|
||||
.pipe(Effect.orDie)
|
||||
}),
|
||||
)
|
||||
|
||||
export const node = makeLocationNode({
|
||||
name: "tool/apply-patch",
|
||||
layer,
|
||||
deps: [ToolRegistry.node, LocationMutation.node, FileMutation.node, FSUtil.node, PermissionV2.node],
|
||||
})
|
||||
}
|
||||
|
||||
function patchFile(change: Prepared): typeof FileDiff.Info.Type {
|
||||
const counts = diffLines(change.before, change.after).reduce(
|
||||
|
|
|
|||
|
|
@ -1,51 +0,0 @@
|
|||
export * as BuiltInTools from "./builtins"
|
||||
|
||||
import { makeLocationNode } from "../effect/app-node"
|
||||
import { Context, Layer } from "effect"
|
||||
import { ApplyPatchTool } from "./apply-patch"
|
||||
import { EditTool } from "./edit"
|
||||
import { GrepTool } from "./grep"
|
||||
import { QuestionTool } from "./question"
|
||||
import { ReadTool } from "./read"
|
||||
import { ReadToolFileSystem } from "./read-filesystem"
|
||||
import { SkillTool } from "./skill"
|
||||
import { TodoWriteTool } from "./todowrite"
|
||||
import { WebFetchTool } from "./webfetch"
|
||||
import { WebSearchTool } from "./websearch"
|
||||
import { WriteTool } from "./write"
|
||||
|
||||
export class Service extends Context.Service<Service, Record<string, never>>()("@opencode/v2/BuiltInTools") {}
|
||||
|
||||
/**
|
||||
* Composes only the shipped Location-scoped built-in tool transforms.
|
||||
* Each tool retains its implementation and focused tests independently. Dynamic
|
||||
* MCP and plugin tools later use separate scoped canonical registrations, while
|
||||
* provider/model filtering belongs to a future materialization phase rather
|
||||
* than this static list. The caller intentionally supplies shared Location
|
||||
* services once to this merged set.
|
||||
*
|
||||
* TODO: Port the remaining launch-follow-up leaves deliberately: edit fuzzy
|
||||
* parity, task, LSP,
|
||||
* repo_clone, repo_overview, plan_exit, and Rune/code mode. Keep MCP and plugin
|
||||
* transforms separate from this static built-in list.
|
||||
*/
|
||||
const layer = Layer.succeed(Service, Service.of({}))
|
||||
|
||||
export const node = makeLocationNode({
|
||||
service: Service,
|
||||
layer,
|
||||
deps: [
|
||||
ApplyPatchTool.node,
|
||||
EditTool.node,
|
||||
GrepTool.node,
|
||||
QuestionTool.node,
|
||||
ReadTool.node,
|
||||
ReadToolFileSystem.node,
|
||||
SkillTool.node,
|
||||
TodoWriteTool.node,
|
||||
WebFetchTool.node,
|
||||
WebSearchTool.node,
|
||||
WebSearchTool.configNode,
|
||||
WriteTool.node,
|
||||
],
|
||||
})
|
||||
|
|
@ -6,18 +6,16 @@
|
|||
*/
|
||||
export * as EditTool from "./edit"
|
||||
|
||||
import type { PluginContext } from "@opencode-ai/plugin/v2/effect"
|
||||
import { ToolFailure } from "@opencode-ai/llm"
|
||||
import { FileDiff } from "@opencode-ai/schema/file-diff"
|
||||
import { createTwoFilesPatch, diffLines } from "diff"
|
||||
import { Effect, Layer, Schema } from "effect"
|
||||
import { makeLocationNode } from "../effect/app-node"
|
||||
import { Effect, Schema } from "effect"
|
||||
import { FileMutation } from "../file-mutation"
|
||||
import { FSUtil } from "../fs-util"
|
||||
import { LocationMutation } from "../location-mutation"
|
||||
import { PermissionV2 } from "../permission"
|
||||
import { ToolRegistry } from "./registry"
|
||||
import { Tool } from "./tool"
|
||||
import { Tools } from "./tools"
|
||||
|
||||
export const name = "edit"
|
||||
|
||||
|
|
@ -87,15 +85,15 @@ export const toModelOutput = (output: Output, oldString: string, newString: stri
|
|||
// TODO: Add snapshots / undo after design exists.
|
||||
// TODO: Add LSP notification and diagnostics after V2 LSP runtime exists.
|
||||
|
||||
const layer = Layer.effectDiscard(
|
||||
Effect.gen(function* () {
|
||||
const tools = yield* Tools.Service
|
||||
export const Plugin = {
|
||||
id: "core-edit-tool",
|
||||
effect: Effect.fn("EditTool.Plugin")(function* (ctx: PluginContext) {
|
||||
const mutation = yield* LocationMutation.Service
|
||||
const files = yield* FileMutation.Service
|
||||
const fs = yield* FSUtil.Service
|
||||
const permission = yield* PermissionV2.Service
|
||||
|
||||
yield* tools
|
||||
yield* ctx.tool
|
||||
.register({
|
||||
[name]: Tool.withPermission(
|
||||
Tool.make({
|
||||
|
|
@ -214,10 +212,4 @@ const layer = Layer.effectDiscard(
|
|||
})
|
||||
.pipe(Effect.orDie)
|
||||
}),
|
||||
)
|
||||
|
||||
export const node = makeLocationNode({
|
||||
name: "tool/edit",
|
||||
layer,
|
||||
deps: [ToolRegistry.node, LocationMutation.node, FileMutation.node, FSUtil.node, PermissionV2.node],
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,18 +1,16 @@
|
|||
export * as GrepTool from "./grep"
|
||||
|
||||
import type { PluginContext } from "@opencode-ai/plugin/v2/effect"
|
||||
import { ToolFailure } from "@opencode-ai/llm"
|
||||
import { Effect, Layer, Schema } from "effect"
|
||||
import { Effect, Schema } from "effect"
|
||||
import path from "path"
|
||||
import { makeLocationNode } from "../effect/app-node"
|
||||
import { FileSystem } from "../filesystem"
|
||||
import { FSUtil } from "../fs-util"
|
||||
import { Location } from "../location"
|
||||
import { PermissionV2 } from "../permission"
|
||||
import { Ripgrep } from "../ripgrep"
|
||||
import { RelativePath } from "../schema"
|
||||
import { ToolRegistry } from "./registry"
|
||||
import { Tool } from "./tool"
|
||||
import { Tools } from "./tools"
|
||||
|
||||
export const name = "grep"
|
||||
|
||||
|
|
@ -50,15 +48,15 @@ export const toModelOutput = (output: ModelOutput) => {
|
|||
}
|
||||
|
||||
/** Grep leaf that defaults its filesystem root to the active Location. */
|
||||
const layer = Layer.effectDiscard(
|
||||
Effect.gen(function* () {
|
||||
const tools = yield* Tools.Service
|
||||
export const Plugin = {
|
||||
id: "core-grep-tool",
|
||||
effect: Effect.fn("GrepTool.Plugin")(function* (ctx: PluginContext) {
|
||||
const fs = yield* FSUtil.Service
|
||||
const ripgrep = yield* Ripgrep.Service
|
||||
const location = yield* Location.Service
|
||||
const permission = yield* PermissionV2.Service
|
||||
|
||||
yield* tools
|
||||
yield* ctx.tool
|
||||
.register({
|
||||
[name]: Tool.make({
|
||||
description:
|
||||
|
|
@ -128,10 +126,4 @@ const layer = Layer.effectDiscard(
|
|||
})
|
||||
.pipe(Effect.orDie)
|
||||
}),
|
||||
)
|
||||
|
||||
export const node = makeLocationNode({
|
||||
name: "tool/grep",
|
||||
layer,
|
||||
deps: [ToolRegistry.node, FSUtil.node, Ripgrep.node, Location.node, PermissionV2.node],
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,11 @@
|
|||
export * as QuestionTool from "./question"
|
||||
|
||||
import type { PluginContext } from "@opencode-ai/plugin/v2/effect"
|
||||
import { ToolFailure } from "@opencode-ai/llm"
|
||||
import { Effect, Layer, Schema } from "effect"
|
||||
import { makeLocationNode } from "../effect/app-node"
|
||||
import { Effect, Schema } from "effect"
|
||||
import { PermissionV2 } from "../permission"
|
||||
import { QuestionV2 } from "../question"
|
||||
import { ToolRegistry } from "./registry"
|
||||
import { Tool } from "./tool"
|
||||
import { Tools } from "./tools"
|
||||
|
||||
export const name = "question"
|
||||
|
||||
|
|
@ -44,13 +42,13 @@ export const toModelOutput = (
|
|||
return `User has answered your questions: ${formatted}. You can now continue with the user's answers in mind.`
|
||||
}
|
||||
|
||||
const layer = Layer.effectDiscard(
|
||||
Effect.gen(function* () {
|
||||
const tools = yield* Tools.Service
|
||||
export const Plugin = {
|
||||
id: "core-question-tool",
|
||||
effect: Effect.fn("QuestionTool.Plugin")(function* (ctx: PluginContext) {
|
||||
const question = yield* QuestionV2.Service
|
||||
const permission = yield* PermissionV2.Service
|
||||
|
||||
yield* tools
|
||||
yield* ctx.tool
|
||||
.register({
|
||||
[name]: Tool.make({
|
||||
description,
|
||||
|
|
@ -85,10 +83,4 @@ const layer = Layer.effectDiscard(
|
|||
})
|
||||
.pipe(Effect.orDie)
|
||||
}),
|
||||
)
|
||||
|
||||
export const node = makeLocationNode({
|
||||
name: "tool/question",
|
||||
layer,
|
||||
deps: [ToolRegistry.node, PermissionV2.node, QuestionV2.node],
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
export * as ReadTool from "./read"
|
||||
|
||||
import type { PluginContext } from "@opencode-ai/plugin/v2/effect"
|
||||
import { dirname } from "path"
|
||||
import { ToolFailure } from "@opencode-ai/llm"
|
||||
import { Effect, Layer, Schema } from "effect"
|
||||
import { makeLocationNode } from "../effect/app-node"
|
||||
import { Effect, Schema } from "effect"
|
||||
import { FileSystem } from "../filesystem"
|
||||
import { FSUtil } from "../fs-util"
|
||||
import { Image } from "../image"
|
||||
|
|
@ -13,9 +13,7 @@ import { PermissionV2 } from "../permission"
|
|||
import { SessionInstructions } from "../session/instructions"
|
||||
import { AbsolutePath } from "../schema"
|
||||
import { ReadToolFileSystem } from "./read-filesystem"
|
||||
import { ToolRegistry } from "./registry"
|
||||
import { Tool } from "./tool"
|
||||
import { Tools } from "./tools"
|
||||
|
||||
export const name = "read"
|
||||
const FILENAME = "AGENTS.md"
|
||||
|
|
@ -32,9 +30,9 @@ const LocationInput = Schema.Struct({
|
|||
const Input = LocationInput
|
||||
const Output = Schema.Union([FileSystem.Content, ReadToolFileSystem.TextPage, ReadToolFileSystem.ListPage])
|
||||
|
||||
const layer = Layer.effectDiscard(
|
||||
Effect.gen(function* () {
|
||||
const tools = yield* Tools.Service
|
||||
export const Plugin = {
|
||||
id: "core-read-tool",
|
||||
effect: Effect.fn("ReadTool.Plugin")(function* (ctx: PluginContext) {
|
||||
const reader = yield* ReadToolFileSystem.Service
|
||||
const mutation = yield* LocationMutation.Service
|
||||
const image = yield* Image.Service
|
||||
|
|
@ -43,7 +41,7 @@ const layer = Layer.effectDiscard(
|
|||
const fs = yield* FSUtil.Service
|
||||
const location = yield* Location.Service
|
||||
|
||||
yield* tools
|
||||
yield* ctx.tool
|
||||
.register({
|
||||
[name]: Tool.make({
|
||||
description:
|
||||
|
|
@ -111,7 +109,10 @@ const layer = Layer.effectDiscard(
|
|||
const candidates = discovered.map(FSUtil.resolve).filter((file) => dirname(file) !== root)
|
||||
if (candidates.length === 0) return
|
||||
yield* sessionInstructions.load({ sessionID: context.sessionID, paths: candidates })
|
||||
}).pipe(Effect.catch(() => Effect.void), Effect.catchDefect(() => Effect.void))
|
||||
}).pipe(
|
||||
Effect.catch(() => Effect.void),
|
||||
Effect.catchDefect(() => Effect.void),
|
||||
)
|
||||
if ("encoding" in content && content.encoding === "base64" && SUPPORTED_IMAGE_MIMES.has(content.mime)) {
|
||||
return yield* image
|
||||
.normalize(resource, { ...content, encoding: "base64" })
|
||||
|
|
@ -137,19 +138,4 @@ const layer = Layer.effectDiscard(
|
|||
})
|
||||
.pipe(Effect.orDie)
|
||||
}),
|
||||
)
|
||||
|
||||
export const node = makeLocationNode({
|
||||
name: "tool/read",
|
||||
layer,
|
||||
deps: [
|
||||
ToolRegistry.node,
|
||||
ReadToolFileSystem.node,
|
||||
LocationMutation.node,
|
||||
Image.node,
|
||||
PermissionV2.node,
|
||||
SessionInstructions.node,
|
||||
FSUtil.node,
|
||||
Location.node,
|
||||
],
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,15 +1,13 @@
|
|||
export * as SkillTool from "./skill"
|
||||
|
||||
import type { PluginContext } from "@opencode-ai/plugin/v2/effect"
|
||||
import path from "path"
|
||||
import { ToolFailure } from "@opencode-ai/llm"
|
||||
import { Effect, Layer, Schema } from "effect"
|
||||
import { makeLocationNode } from "../effect/app-node"
|
||||
import { Effect, Schema } from "effect"
|
||||
import { FSUtil } from "../fs-util"
|
||||
import { SkillV2 } from "../skill"
|
||||
import { PermissionV2 } from "../permission"
|
||||
import { ToolRegistry } from "./registry"
|
||||
import { Tool } from "./tool"
|
||||
import { Tools } from "./tools"
|
||||
|
||||
export const name = "skill"
|
||||
const FILE_LIMIT = 10
|
||||
|
|
@ -54,13 +52,13 @@ export const toModelOutput = (skill: SkillV2.Info, files: ReadonlyArray<string>)
|
|||
const unableToLoad = (name: string, error?: unknown) =>
|
||||
new ToolFailure({ message: `Unable to load skill ${name}`, error })
|
||||
|
||||
const layer = Layer.effectDiscard(
|
||||
Effect.gen(function* () {
|
||||
const tools = yield* Tools.Service
|
||||
export const Plugin = {
|
||||
id: "core-skill-tool",
|
||||
effect: Effect.fn("SkillTool.Plugin")(function* (ctx: PluginContext) {
|
||||
const fs = yield* FSUtil.Service
|
||||
const skills = yield* SkillV2.Service
|
||||
const permission = yield* PermissionV2.Service
|
||||
yield* tools
|
||||
yield* ctx.tool
|
||||
.register({
|
||||
[name]: Tool.make({
|
||||
description,
|
||||
|
|
@ -100,10 +98,4 @@ const layer = Layer.effectDiscard(
|
|||
})
|
||||
.pipe(Effect.orDie)
|
||||
}),
|
||||
)
|
||||
|
||||
export const node = makeLocationNode({
|
||||
name: "tool/skill",
|
||||
layer,
|
||||
deps: [ToolRegistry.node, FSUtil.node, SkillV2.node, PermissionV2.node],
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,11 @@
|
|||
export * as TodoWriteTool from "./todowrite"
|
||||
|
||||
import type { PluginContext } from "@opencode-ai/plugin/v2/effect"
|
||||
import { ToolFailure } from "@opencode-ai/llm"
|
||||
import { Effect, Layer, Schema } from "effect"
|
||||
import { makeLocationNode } from "../effect/app-node"
|
||||
import { Effect, Schema } from "effect"
|
||||
import { PermissionV2 } from "../permission"
|
||||
import { SessionTodo } from "../session/todo"
|
||||
import { ToolRegistry } from "./registry"
|
||||
import { Tool } from "./tool"
|
||||
import { Tools } from "./tools"
|
||||
|
||||
export const name = "todowrite"
|
||||
|
||||
|
|
@ -22,13 +20,13 @@ export type Output = typeof Output.Type
|
|||
|
||||
export const toModelOutput = (output: Output) => JSON.stringify(output.todos, null, 2)
|
||||
|
||||
const layer = Layer.effectDiscard(
|
||||
Effect.gen(function* () {
|
||||
const tools = yield* Tools.Service
|
||||
export const Plugin = {
|
||||
id: "core-todowrite-tool",
|
||||
effect: Effect.fn("TodoWriteTool.Plugin")(function* (ctx: PluginContext) {
|
||||
const todos = yield* SessionTodo.Service
|
||||
const permission = yield* PermissionV2.Service
|
||||
|
||||
yield* tools
|
||||
yield* ctx.tool
|
||||
.register({
|
||||
[name]: Tool.make({
|
||||
description:
|
||||
|
|
@ -53,10 +51,4 @@ const layer = Layer.effectDiscard(
|
|||
})
|
||||
.pipe(Effect.orDie)
|
||||
}),
|
||||
)
|
||||
|
||||
export const node = makeLocationNode({
|
||||
name: "tool/todowrite",
|
||||
layer,
|
||||
deps: [ToolRegistry.node, PermissionV2.node, SessionTodo.node],
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,17 +1,14 @@
|
|||
export * as WebFetchTool from "./webfetch"
|
||||
|
||||
import type { PluginContext } from "@opencode-ai/plugin/v2/effect"
|
||||
import { ToolFailure } from "@opencode-ai/llm"
|
||||
import { Duration, Effect, Layer, Schema } from "effect"
|
||||
import { Duration, Effect, Schema } from "effect"
|
||||
import { HttpClient, HttpClientRequest, HttpClientResponse } from "effect/unstable/http"
|
||||
import { Parser } from "htmlparser2"
|
||||
import TurndownService from "turndown"
|
||||
import { makeLocationNode } from "../effect/app-node"
|
||||
import { LayerNodePlatform } from "../effect/app-node-platform"
|
||||
import { PermissionV2 } from "../permission"
|
||||
import { collectBoundedResponseBody } from "./http-body"
|
||||
import { ToolRegistry } from "./registry"
|
||||
import { Tool } from "./tool"
|
||||
import { Tools } from "./tools"
|
||||
|
||||
export const name = "webfetch"
|
||||
export const MAX_RESPONSE_BYTES = 5 * 1024 * 1024
|
||||
|
|
@ -115,13 +112,13 @@ const convert = (content: string, contentType: string, format: Format) => {
|
|||
return content
|
||||
}
|
||||
|
||||
const layer = Layer.effectDiscard(
|
||||
Effect.gen(function* () {
|
||||
const tools = yield* Tools.Service
|
||||
export const Plugin = {
|
||||
id: "core-webfetch-tool",
|
||||
effect: Effect.fn("WebFetchTool.Plugin")(function* (ctx: PluginContext) {
|
||||
const http = yield* HttpClient.HttpClient
|
||||
const permission = yield* PermissionV2.Service
|
||||
|
||||
yield* tools
|
||||
yield* ctx.tool
|
||||
.register({
|
||||
[name]: Tool.make({
|
||||
description,
|
||||
|
|
@ -178,13 +175,7 @@ const layer = Layer.effectDiscard(
|
|||
})
|
||||
.pipe(Effect.orDie)
|
||||
}),
|
||||
)
|
||||
|
||||
export const node = makeLocationNode({
|
||||
name: "tool/webfetch",
|
||||
layer,
|
||||
deps: [ToolRegistry.node, PermissionV2.node, LayerNodePlatform.httpClient],
|
||||
})
|
||||
}
|
||||
|
||||
export function extractTextFromHTML(html: string) {
|
||||
let text = ""
|
||||
|
|
|
|||
|
|
@ -1,19 +1,17 @@
|
|||
export * as WebSearchTool from "./websearch"
|
||||
|
||||
import type { PluginContext } from "@opencode-ai/plugin/v2/effect"
|
||||
import { ToolFailure } from "@opencode-ai/llm"
|
||||
import { Context, Duration, Effect, Layer, Schema } from "effect"
|
||||
import { HttpClient, HttpClientRequest } from "effect/unstable/http"
|
||||
import { makeLocationNode } from "../effect/app-node"
|
||||
import { LayerNodePlatform } from "../effect/app-node-platform"
|
||||
import { truthy } from "../flag/flag"
|
||||
import { InstallationVersion } from "../installation/version"
|
||||
import { PositiveInt } from "../schema"
|
||||
import { PermissionV2 } from "../permission"
|
||||
import { Tool } from "./tool"
|
||||
import { Tools } from "./tools"
|
||||
import { collectBoundedResponseBody } from "./http-body"
|
||||
import { checksum } from "../util/encode"
|
||||
import { ToolRegistry } from "./registry"
|
||||
|
||||
export const name = "websearch"
|
||||
export const NO_RESULTS = "No search results found. Please try a different query."
|
||||
|
|
@ -189,14 +187,14 @@ const Output = Schema.Struct({
|
|||
text: Schema.String,
|
||||
})
|
||||
|
||||
const layer = Layer.effectDiscard(
|
||||
Effect.gen(function* () {
|
||||
const tools = yield* Tools.Service
|
||||
export const Plugin = {
|
||||
id: "core-websearch-tool",
|
||||
effect: Effect.fn("WebSearchTool.Plugin")(function* (ctx: PluginContext) {
|
||||
const http = yield* HttpClient.HttpClient
|
||||
const config = yield* ConfigService
|
||||
const permission = yield* PermissionV2.Service
|
||||
|
||||
yield* tools
|
||||
yield* ctx.tool
|
||||
.register({
|
||||
[name]: Tool.make({
|
||||
description,
|
||||
|
|
@ -251,10 +249,4 @@ const layer = Layer.effectDiscard(
|
|||
})
|
||||
.pipe(Effect.orDie)
|
||||
}),
|
||||
)
|
||||
|
||||
export const node = makeLocationNode({
|
||||
name: "tool/websearch",
|
||||
layer,
|
||||
deps: [ToolRegistry.node, PermissionV2.node, LayerNodePlatform.httpClient, configNode],
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,15 +6,13 @@
|
|||
*/
|
||||
export * as WriteTool from "./write"
|
||||
|
||||
import type { PluginContext } from "@opencode-ai/plugin/v2/effect"
|
||||
import { ToolFailure } from "@opencode-ai/llm"
|
||||
import { Effect, Layer, Schema } from "effect"
|
||||
import { makeLocationNode } from "../effect/app-node"
|
||||
import { Effect, Schema } from "effect"
|
||||
import { FileMutation } from "../file-mutation"
|
||||
import { LocationMutation } from "../location-mutation"
|
||||
import { PermissionV2 } from "../permission"
|
||||
import { ToolRegistry } from "./registry"
|
||||
import { Tool } from "./tool"
|
||||
import { Tools } from "./tools"
|
||||
|
||||
export const name = "write"
|
||||
|
||||
|
|
@ -44,14 +42,14 @@ export const toModelOutput = (output: Output) =>
|
|||
// TODO: Add snapshots / undo after design exists.
|
||||
// TODO: Add LSP notification and diagnostics after V2 LSP runtime exists.
|
||||
|
||||
const layer = Layer.effectDiscard(
|
||||
Effect.gen(function* () {
|
||||
const tools = yield* Tools.Service
|
||||
export const Plugin = {
|
||||
id: "core-write-tool",
|
||||
effect: Effect.fn("WriteTool.Plugin")(function* (ctx: PluginContext) {
|
||||
const mutation = yield* LocationMutation.Service
|
||||
const files = yield* FileMutation.Service
|
||||
const permission = yield* PermissionV2.Service
|
||||
|
||||
yield* tools
|
||||
yield* ctx.tool
|
||||
.register({
|
||||
[name]: Tool.withPermission(
|
||||
Tool.make({
|
||||
|
|
@ -92,10 +90,4 @@ const layer = Layer.effectDiscard(
|
|||
})
|
||||
.pipe(Effect.orDie)
|
||||
}),
|
||||
)
|
||||
|
||||
export const node = makeLocationNode({
|
||||
name: "tool/write",
|
||||
layer,
|
||||
deps: [ToolRegistry.node, LocationMutation.node, FileMutation.node, PermissionV2.node],
|
||||
})
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue