@@ -33,8 +33,12 @@ export { Event } from "@opencode/schema/skill"
3333export const available = ( skills : ReadonlyArray < Info > , permissions : Permission . Ruleset ) =>
3434 skills . filter ( ( skill ) => Permission . evaluate ( "skill" , skill . id , permissions ) . effect !== "deny" )
3535
36+ export const uri = ( id : string , file ?: string ) => ( file ? `skill://${ id } /${ file } ` : `skill://${ id } ` )
37+
38+ export const isUri = ( value : string ) => value . startsWith ( "skill://" )
39+
3640export const toModelOutput = ( skill : Info , files : ReadonlyArray < string > ) => {
37- const directory = path . dirname ( skill . path )
41+ const directory = isUri ( skill . path ) ? uri ( skill . id ) : path . dirname ( skill . path )
3842 return [
3943 `<skill_content name="${ skill . name } ">` ,
4044 `# Skill: ${ skill . name } ` ,
@@ -52,7 +56,23 @@ export const toModelOutput = (skill: Info, files: ReadonlyArray<string>) => {
5256 ] . join ( "\n" )
5357}
5458
55- export const prepare = Effect . fn ( "Skill.prepare" ) ( function * ( fs : FSUtil . Interface , skill : Info ) {
59+ export const prepare = Effect . fn ( "Skill.prepare" ) ( function * (
60+ fs : FSUtil . Interface ,
61+ skill : Info ,
62+ virtualFiles ?: ReadonlyMap < string , string > ,
63+ ) {
64+ if ( isUri ( skill . path ) ) {
65+ const directory = uri ( skill . id )
66+ const files = Array . from ( virtualFiles ?. keys ( ) ?? [ ] )
67+ . filter ( ( file ) => file !== "SKILL.md" )
68+ . toSorted ( )
69+ . slice ( 0 , 10 )
70+ . map ( ( file ) => uri ( skill . id , file ) )
71+ return {
72+ directory,
73+ output : toModelOutput ( skill , files ) ,
74+ }
75+ }
5676 const directory = path . dirname ( skill . path )
5777 const files =
5878 path . basename ( skill . path ) === "SKILL.md"
@@ -75,18 +95,20 @@ export const prepare = Effect.fn("Skill.prepare")(function* (fs: FSUtil.Interfac
7595
7696export type Data = {
7797 skills : Map < ID , Types . DeepMutable < Info > >
98+ files : Map < ID , ReadonlyMap < string , string > >
7899}
79100
80101export type Editor = {
81102 list : ( ) => readonly Types . DeepMutable < Info > [ ]
82103 get : ( id : string ) => Types . DeepMutable < Info > | undefined
83- add : ( skill : Info ) => void
104+ add : ( skill : Info , files ?: Readonly < Record < string , string > > ) => void
84105 update : ( id : string , update : ( skill : Types . DeepMutable < Info > ) => void ) => void
85106 remove : ( id : string ) => void
86107}
87108
88109export interface Interface extends State . Transformable < Editor > {
89110 readonly get : ( id : ID ) => Effect . Effect < Info | undefined >
111+ readonly files : ( id : ID ) => Effect . Effect < ReadonlyMap < string , string > | undefined >
90112 readonly list : ( ) => Effect . Effect < Info [ ] >
91113}
92114
@@ -99,12 +121,22 @@ const layer = Layer.effect(
99121
100122 const state = State . create < Data , Editor > ( {
101123 name : "skill" ,
102- initial : ( ) => ( { skills : new Map ( ) } ) ,
124+ initial : ( ) => ( { skills : new Map ( ) , files : new Map ( ) } ) ,
103125 editor : ( editor ) => ( {
104126 list : ( ) => Array . from ( editor . skills . values ( ) ) ,
105127 get : ( id ) => editor . skills . get ( ID . make ( id ) ) ,
106- add : ( skill ) => {
128+ add : ( skill , files ) => {
107129 editor . skills . set ( skill . id , { ...skill } )
130+ const entries = files
131+ ? Object . entries ( files )
132+ . filter ( ( [ file ] ) => file !== "SKILL.md" )
133+ . toSorted ( ( [ a ] , [ b ] ) => a . localeCompare ( b ) )
134+ : [ ]
135+ if ( entries . length === 0 ) {
136+ editor . files . delete ( skill . id )
137+ return
138+ }
139+ editor . files . set ( skill . id , new Map ( entries ) )
108140 } ,
109141 update : ( id , update ) => {
110142 const key = ID . make ( id )
@@ -114,11 +146,16 @@ const layer = Layer.effect(
114146 current . id = key
115147 } ,
116148 remove : ( id ) => {
117- editor . skills . delete ( ID . make ( id ) )
149+ const key = ID . make ( id )
150+ editor . skills . delete ( key )
151+ editor . files . delete ( key )
118152 } ,
119153 } ) ,
120154 notify : State . changes (
121- ( data ) => Array . from ( data . skills . values ( ) ) ,
155+ ( data ) => [
156+ Array . from ( data . skills . values ( ) ) ,
157+ Array . from ( data . files , ( [ id , entries ] ) => [ id , Array . from ( entries ) ] ) ,
158+ ] ,
122159 ( ) => bus . publish ( Skill . Event . Updated , { } ) . pipe ( Effect . asVoid ) ,
123160 ) ,
124161 } )
@@ -129,6 +166,10 @@ const layer = Layer.effect(
129166 get : Effect . fn ( "Skill.get" ) ( function * ( id ) {
130167 return state . get ( ) . skills . get ( id )
131168 } ) ,
169+ files : Effect . fn ( "Skill.files" ) ( function * ( id ) {
170+ const current = state . get ( )
171+ return current . skills . has ( id ) ? current . files . get ( id ) : undefined
172+ } ) ,
132173 list : Effect . fn ( "Skill.list" ) ( function * ( ) {
133174 return Array . from ( state . get ( ) . skills . values ( ) )
134175 } ) ,
0 commit comments