|
|
@@ -5,12 +5,12 @@ import { EditTool } from "./edit"
|
|
|
import { GlobTool } from "./glob"
|
|
|
import { GrepTool } from "./grep"
|
|
|
import { ReadTool } from "./read"
|
|
|
-import { TaskDescription, TaskTool } from "./task"
|
|
|
+import { TaskTool } from "./task"
|
|
|
import { TodoWriteTool } from "./todo"
|
|
|
import { WebFetchTool } from "./webfetch"
|
|
|
import { WriteTool } from "./write"
|
|
|
import { InvalidTool } from "./invalid"
|
|
|
-import { SkillDescription, SkillTool } from "./skill"
|
|
|
+import { SkillTool } from "./skill"
|
|
|
import { Tool } from "./tool"
|
|
|
import { Config } from "../config/config"
|
|
|
import { type ToolContext as PluginToolContext, type ToolDefinition } from "@opencode-ai/plugin"
|
|
|
@@ -38,6 +38,8 @@ import { FileTime } from "../file/time"
|
|
|
import { Instruction } from "../session/instruction"
|
|
|
import { AppFileSystem } from "../filesystem"
|
|
|
import { Agent } from "../agent/agent"
|
|
|
+import { Skill } from "../skill"
|
|
|
+import { Permission } from "@/permission"
|
|
|
|
|
|
export namespace ToolRegistry {
|
|
|
const log = Log.create({ service: "tool.registry" })
|
|
|
@@ -73,6 +75,7 @@ export namespace ToolRegistry {
|
|
|
| Question.Service
|
|
|
| Todo.Service
|
|
|
| Agent.Service
|
|
|
+ | Skill.Service
|
|
|
| LSP.Service
|
|
|
| FileTime.Service
|
|
|
| Instruction.Service
|
|
|
@@ -82,6 +85,8 @@ export namespace ToolRegistry {
|
|
|
Effect.gen(function* () {
|
|
|
const config = yield* Config.Service
|
|
|
const plugin = yield* Plugin.Service
|
|
|
+ const agents = yield* Agent.Service
|
|
|
+ const skill = yield* Skill.Service
|
|
|
|
|
|
const task = yield* TaskTool
|
|
|
const read = yield* ReadTool
|
|
|
@@ -199,6 +204,40 @@ export namespace ToolRegistry {
|
|
|
return (yield* all()).map((tool) => tool.id)
|
|
|
})
|
|
|
|
|
|
+ const describeSkill = Effect.fn("ToolRegistry.describeSkill")(function* (agent: Agent.Info) {
|
|
|
+ const list = yield* skill.available(agent)
|
|
|
+ if (list.length === 0) return "No skills are currently available."
|
|
|
+ return [
|
|
|
+ "Load a specialized skill that provides domain-specific instructions and workflows.",
|
|
|
+ "",
|
|
|
+ "When you recognize that a task matches one of the available skills listed below, use this tool to load the full skill instructions.",
|
|
|
+ "",
|
|
|
+ "The skill will inject detailed instructions, workflows, and access to bundled resources (scripts, references, templates) into the conversation context.",
|
|
|
+ "",
|
|
|
+ 'Tool output includes a `<skill_content name="...">` block with the loaded content.',
|
|
|
+ "",
|
|
|
+ "The following skills provide specialized sets of instructions for particular tasks",
|
|
|
+ "Invoke this tool to load a skill when a task matches one of the available skills listed below:",
|
|
|
+ "",
|
|
|
+ Skill.fmt(list, { verbose: false }),
|
|
|
+ ].join("\n")
|
|
|
+ })
|
|
|
+
|
|
|
+ const describeTask = Effect.fn("ToolRegistry.describeTask")(function* (agent: Agent.Info) {
|
|
|
+ const items = (yield* agents.list()).filter((item) => item.mode !== "primary")
|
|
|
+ const filtered = items.filter(
|
|
|
+ (item) => Permission.evaluate("task", item.name, agent.permission).action !== "deny",
|
|
|
+ )
|
|
|
+ const list = filtered.toSorted((a, b) => a.name.localeCompare(b.name))
|
|
|
+ const description = list
|
|
|
+ .map(
|
|
|
+ (item) =>
|
|
|
+ `- ${item.name}: ${item.description ?? "This subagent should only be called manually by the user."}`,
|
|
|
+ )
|
|
|
+ .join("\n")
|
|
|
+ return ["Available agent types and the tools they have access to:", description].join("\n")
|
|
|
+ })
|
|
|
+
|
|
|
const tools: Interface["tools"] = Effect.fn("ToolRegistry.tools")(function* (input) {
|
|
|
const filtered = (yield* all()).filter((tool) => {
|
|
|
if (tool.id === CodeSearchTool.id || tool.id === WebSearchTool.id) {
|
|
|
@@ -227,8 +266,8 @@ export namespace ToolRegistry {
|
|
|
id: tool.id,
|
|
|
description: [
|
|
|
output.description,
|
|
|
- tool.id === TaskTool.id ? yield* TaskDescription(input.agent) : undefined,
|
|
|
- tool.id === SkillTool.id ? yield* SkillDescription(input.agent) : undefined,
|
|
|
+ tool.id === TaskTool.id ? yield* describeTask(input.agent) : undefined,
|
|
|
+ tool.id === SkillTool.id ? yield* describeSkill(input.agent) : undefined,
|
|
|
]
|
|
|
.filter(Boolean)
|
|
|
.join("\n"),
|
|
|
@@ -257,7 +296,9 @@ export namespace ToolRegistry {
|
|
|
Layer.provide(Plugin.defaultLayer),
|
|
|
Layer.provide(Question.defaultLayer),
|
|
|
Layer.provide(Todo.defaultLayer),
|
|
|
+ Layer.provide(Skill.defaultLayer),
|
|
|
Layer.provide(Agent.defaultLayer),
|
|
|
+ Layer.provide(Skill.defaultLayer),
|
|
|
Layer.provide(LSP.defaultLayer),
|
|
|
Layer.provide(FileTime.defaultLayer),
|
|
|
Layer.provide(Instruction.defaultLayer),
|