import path from "path" import { pathToFileURL } from "url" import { Effect, Schema } from "effect" import { Ripgrep } from "@opencode-ai/core/ripgrep" import { Skill } from "../skill" import * as Tool from "./tool" import DESCRIPTION from "./skill.txt" export const Parameters = Schema.Struct({ name: Schema.String.annotate({ description: "The name of the skill from available_skills" }), }) export const SkillTool = Tool.define( "skill", Effect.gen(function* () { const skill = yield* Skill.Service const ripgrep = yield* Ripgrep.Service return { description: DESCRIPTION, parameters: Parameters, execute: (params: Schema.Schema.Type, ctx: Tool.Context) => Effect.gen(function* () { const info = yield* skill .require(params.name) .pipe(Effect.catchTag("Skill.NotFoundError", (error) => Effect.die(new Error(error.message)))) yield* ctx.ask({ permission: "skill", patterns: [params.name], always: [params.name], metadata: {}, }) const dir = path.dirname(info.location) const base = pathToFileURL(dir).href const files = yield* ripgrep.find({ cwd: dir, pattern: "!**/SKILL.md", hidden: true, follow: false, signal: ctx.abort, limit: 10, }) return { title: `Loaded skill: ${info.name}`, output: [ ``, `# Skill: ${info.name}`, "", info.content.trim(), "", `Base directory for this skill: ${base}`, "Relative paths in this skill (e.g., scripts/, reference/) are relative to this base directory.", "Note: file list is sampled.", "", "", files.map((file) => `${path.resolve(dir, file.path)}`).join("\n"), "", "", ].join("\n"), metadata: { name: info.name, dir, }, } }).pipe(Effect.orDie), } }), )