@@ -54,11 +54,6 @@ export namespace Skill {
5454 type State = {
5555 skills : Record < string , Info >
5656 dirs : Set < string >
57- task ?: Promise < void >
58- }
59-
60- type Cache = State & {
61- ensure : ( ) => Promise < void >
6257 }
6358
6459 export interface Interface {
@@ -116,66 +111,47 @@ export namespace Skill {
116111 } )
117112 }
118113
119- // TODO: Migrate to Effect
120- const create = ( discovery : Discovery . Interface , directory : string , worktree : string ) : Cache => {
121- const state : State = {
122- skills : { } ,
123- dirs : new Set < string > ( ) ,
124- }
125-
126- const load = async ( ) => {
127- if ( ! Flag . OPENCODE_DISABLE_EXTERNAL_SKILLS ) {
128- for ( const dir of EXTERNAL_DIRS ) {
129- const root = path . join ( Global . Path . home , dir )
130- if ( ! ( await Filesystem . isDir ( root ) ) ) continue
131- await scan ( state , root , EXTERNAL_SKILL_PATTERN , { dot : true , scope : "global" } )
132- }
133-
134- for await ( const root of Filesystem . up ( {
135- targets : EXTERNAL_DIRS ,
136- start : directory ,
137- stop : worktree ,
138- } ) ) {
139- await scan ( state , root , EXTERNAL_SKILL_PATTERN , { dot : true , scope : "project" } )
140- }
114+ async function loadSkills ( state : State , discovery : Discovery . Interface , directory : string , worktree : string ) {
115+ if ( ! Flag . OPENCODE_DISABLE_EXTERNAL_SKILLS ) {
116+ for ( const dir of EXTERNAL_DIRS ) {
117+ const root = path . join ( Global . Path . home , dir )
118+ if ( ! ( await Filesystem . isDir ( root ) ) ) continue
119+ await scan ( state , root , EXTERNAL_SKILL_PATTERN , { dot : true , scope : "global" } )
141120 }
142121
143- for ( const dir of await Config . directories ( ) ) {
144- await scan ( state , dir , OPENCODE_SKILL_PATTERN )
122+ for await ( const root of Filesystem . up ( {
123+ targets : EXTERNAL_DIRS ,
124+ start : directory ,
125+ stop : worktree ,
126+ } ) ) {
127+ await scan ( state , root , EXTERNAL_SKILL_PATTERN , { dot : true , scope : "project" } )
145128 }
129+ }
146130
147- const cfg = await Config . get ( )
148- for ( const item of cfg . skills ?. paths ?? [ ] ) {
149- const expanded = item . startsWith ( "~/" ) ? path . join ( os . homedir ( ) , item . slice ( 2 ) ) : item
150- const dir = path . isAbsolute ( expanded ) ? expanded : path . join ( directory , expanded )
151- if ( ! ( await Filesystem . isDir ( dir ) ) ) {
152- log . warn ( "skill path not found" , { path : dir } )
153- continue
154- }
155-
156- await scan ( state , dir , SKILL_PATTERN )
157- }
131+ for ( const dir of await Config . directories ( ) ) {
132+ await scan ( state , dir , OPENCODE_SKILL_PATTERN )
133+ }
158134
159- for ( const url of cfg . skills ?. urls ?? [ ] ) {
160- for ( const dir of await Effect . runPromise ( discovery . pull ( url ) ) ) {
161- state . dirs . add ( dir )
162- await scan ( state , dir , SKILL_PATTERN )
163- }
135+ const cfg = await Config . get ( )
136+ for ( const item of cfg . skills ?. paths ?? [ ] ) {
137+ const expanded = item . startsWith ( "~/" ) ? path . join ( os . homedir ( ) , item . slice ( 2 ) ) : item
138+ const dir = path . isAbsolute ( expanded ) ? expanded : path . join ( directory , expanded )
139+ if ( ! ( await Filesystem . isDir ( dir ) ) ) {
140+ log . warn ( "skill path not found" , { path : dir } )
141+ continue
164142 }
165143
166- log . info ( "init" , { count : Object . keys ( state . skills ) . length } )
144+ await scan ( state , dir , SKILL_PATTERN )
167145 }
168146
169- const ensure = ( ) => {
170- if ( state . task ) return state . task
171- state . task = load ( ) . catch ( ( err ) => {
172- state . task = undefined
173- throw err
174- } )
175- return state . task
147+ for ( const url of cfg . skills ?. urls ?? [ ] ) {
148+ for ( const dir of await Effect . runPromise ( discovery . pull ( url ) ) ) {
149+ state . dirs . add ( dir )
150+ await scan ( state , dir , SKILL_PATTERN )
151+ }
176152 }
177153
178- return { ... state , ensure }
154+ log . info ( "init" , { count : Object . keys ( state . skills ) . length } )
179155 }
180156
181157 export class Service extends ServiceMap . Service < Service , Interface > ( ) ( "@opencode/Skill" ) { }
@@ -185,33 +161,33 @@ export namespace Skill {
185161 Effect . gen ( function * ( ) {
186162 const discovery = yield * Discovery . Service
187163 const state = yield * InstanceState . make (
188- Effect . fn ( "Skill.state" ) ( ( ctx ) => Effect . sync ( ( ) => create ( discovery , ctx . directory , ctx . worktree ) ) ) ,
164+ Effect . fn ( "Skill.state" ) ( ( ctx ) =>
165+ Effect . gen ( function * ( ) {
166+ const s : State = { skills : { } , dirs : new Set ( ) }
167+ yield * Effect . promise ( ( ) => loadSkills ( s , discovery , ctx . directory , ctx . worktree ) )
168+ return s
169+ } ) ,
170+ ) ,
189171 )
190172
191- const ensure = Effect . fn ( "Skill.ensure" ) ( function * ( ) {
192- const cache = yield * InstanceState . get ( state )
193- yield * Effect . promise ( ( ) => cache . ensure ( ) )
194- return cache
195- } )
196-
197173 const get = Effect . fn ( "Skill.get" ) ( function * ( name : string ) {
198- const cache = yield * ensure ( )
199- return cache . skills [ name ]
174+ const s = yield * InstanceState . get ( state )
175+ return s . skills [ name ]
200176 } )
201177
202178 const all = Effect . fn ( "Skill.all" ) ( function * ( ) {
203- const cache = yield * ensure ( )
204- return Object . values ( cache . skills )
179+ const s = yield * InstanceState . get ( state )
180+ return Object . values ( s . skills )
205181 } )
206182
207183 const dirs = Effect . fn ( "Skill.dirs" ) ( function * ( ) {
208- const cache = yield * ensure ( )
209- return Array . from ( cache . dirs )
184+ const s = yield * InstanceState . get ( state )
185+ return Array . from ( s . dirs )
210186 } )
211187
212188 const available = Effect . fn ( "Skill.available" ) ( function * ( agent ?: Agent . Info ) {
213- const cache = yield * ensure ( )
214- const list = Object . values ( cache . skills ) . toSorted ( ( a , b ) => a . name . localeCompare ( b . name ) )
189+ const s = yield * InstanceState . get ( state )
190+ const list = Object . values ( s . skills ) . toSorted ( ( a , b ) => a . name . localeCompare ( b . name ) )
215191 if ( ! agent ) return list
216192 return list . filter ( ( skill ) => Permission . evaluate ( "skill" , skill . name , agent . permission ) . action !== "deny" )
217193 } )
0 commit comments