Something went wrong. Try again.
opencode v2 mirror
Something went wrong. Try again.
TypeScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134export * as Skill from "./skill.js"
import { makeLocationNode } from "@opencode/util/effect/app-node"import type { FSUtil } from "@opencode/util/fs-util"import path from "path"import { Context, Effect, Layer, Types } from "effect"import { Skill } from "@opencode/schema/skill"import { Bus } from "./bus.js"import { Permission } from "./permission.js"import { State } from "./state.js"
export const DirectorySource = Skill.DirectorySourceexport type DirectorySource = Skill.DirectorySource
export const UrlSource = Skill.UrlSourceexport type UrlSource = Skill.UrlSource
export const EmbeddedSource = Skill.EmbeddedSourceexport type EmbeddedSource = Skill.EmbeddedSource
export const Source = Skill.Sourceexport type Source = Skill.Source
export const Info = Skill.Infoexport type Info = Skill.Infoexport const ID = Skill.IDexport type ID = Skill.IDexport const Name = Skill.Nameexport type Name = Skill.Name
export { Event } from "@opencode/schema/skill"
export const available = (skills: ReadonlyArray<Info>, permissions: Permission.Ruleset) => skills.filter((skill) => Permission.evaluate("skill", skill.id, permissions).effect !== "deny")
export const toModelOutput = (skill: Info, files: ReadonlyArray<string>) => { const directory = path.dirname(skill.path) return [ `<skill_content name="${skill.name}">`, `# Skill: ${skill.name}`, "", skill.content.trim(), "", `Base directory for this skill: ${directory}`, "Relative paths in this skill (e.g., scripts/, reference/) are relative to this base directory.", "Note: file list is sampled.", "", "<skill_files>", ...files.map((file) => `<file>${file}</file>`), "</skill_files>", "</skill_content>", ].join("\n")}
export const prepare = Effect.fn("Skill.prepare")(function* (fs: FSUtil.Interface, skill: Info) { const directory = path.dirname(skill.path) const files = path.basename(skill.path) === "SKILL.md" ? (yield* fs.scan("**/*", { cwd: directory, absolute: true, include: "file", dot: true })) .filter((file) => path.basename(file) !== "SKILL.md") .toSorted() .slice(0, 10) : [] return { directory, output: toModelOutput(skill, files), }})
export type Data = { skills: Map<ID, Types.DeepMutable<Info>>}
export type Editor = { list: () => readonly Types.DeepMutable<Info>[] get: (id: string) => Types.DeepMutable<Info> | undefined add: (skill: Info) => void update: (id: string, update: (skill: Types.DeepMutable<Info>) => void) => void remove: (id: string) => void}
export interface Interface extends State.Transformable<Editor> { readonly get: (id: ID) => Effect.Effect<Info | undefined> readonly list: () => Effect.Effect<Info[]>}
export class Service extends Context.Service<Service, Interface>()("@opencode/Skill") {}
const layer = Layer.effect( Service, Effect.gen(function* () { const bus = yield* Bus.Service
const state = State.create<Data, Editor>({ name: "skill", initial: () => ({ skills: new Map() }), editor: (editor) => ({ list: () => Array.from(editor.skills.values()), get: (id) => editor.skills.get(ID.make(id)), add: (skill) => { editor.skills.set(skill.id, { ...skill } as Types.DeepMutable<Info>) }, update: (id, update) => { const current = editor.skills.get(ID.make(id)) if (!current) return update(current) current.id = ID.make(id) }, remove: (id) => { editor.skills.delete(ID.make(id)) }, }), notify: () => bus.publish(Skill.Event.Updated, {}).pipe(Effect.asVoid), })
return Service.of({ transform: state.transform, reload: state.reload, get: Effect.fn("Skill.get")(function* (id) { return state.get().skills.get(id) }), list: Effect.fn("Skill.list")(function* () { return Array.from(state.get().skills.values()) }), }) }),)
export const node = makeLocationNode({ service: Service, layer, deps: [Bus.node],})