Skip to content

Commit f137782

Browse files
authored
perf: speed up skill directory discovery (#22990)
1 parent 326471a commit f137782

1 file changed

Lines changed: 42 additions & 18 deletions

File tree

packages/opencode/src/skill/index.ts

Lines changed: 42 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,16 @@ type State = {
5454
dirs: Set<string>
5555
}
5656

57+
type DiscoveryState = {
58+
matches: string[]
59+
dirs: string[]
60+
}
61+
62+
type ScanState = {
63+
matches: Set<string>
64+
dirs: Set<string>
65+
}
66+
5767
export interface Interface {
5868
readonly get: (name: string) => Effect.Effect<Info | undefined>
5969
readonly all: () => Effect.Effect<Info[]>
@@ -102,8 +112,7 @@ const add = Effect.fnUntraced(function* (state: State, match: string, bus: Bus.I
102112
})
103113

104114
const scan = Effect.fnUntraced(function* (
105-
state: State,
106-
bus: Bus.Interface,
115+
state: ScanState,
107116
root: string,
108117
pattern: string,
109118
opts?: { dot?: boolean; scope?: string },
@@ -126,40 +135,40 @@ const scan = Effect.fnUntraced(function* (
126135
}),
127136
)
128137

129-
yield* Effect.forEach(matches, (match) => add(state, match, bus), {
130-
concurrency: "unbounded",
131-
discard: true,
132-
})
138+
for (const match of matches) {
139+
state.matches.add(match)
140+
state.dirs.add(path.dirname(match))
141+
}
133142
})
134143

135-
const loadSkills = Effect.fnUntraced(function* (
136-
state: State,
144+
const discoverSkills = Effect.fnUntraced(function* (
137145
config: Config.Interface,
138146
discovery: Discovery.Interface,
139-
bus: Bus.Interface,
140147
fsys: AppFileSystem.Interface,
141148
directory: string,
142149
worktree: string,
143150
) {
151+
const state: ScanState = { matches: new Set(), dirs: new Set() }
152+
144153
if (!Flag.OPENCODE_DISABLE_EXTERNAL_SKILLS) {
145154
for (const dir of EXTERNAL_DIRS) {
146155
const root = path.join(Global.Path.home, dir)
147156
if (!(yield* fsys.isDir(root))) continue
148-
yield* scan(state, bus, root, EXTERNAL_SKILL_PATTERN, { dot: true, scope: "global" })
157+
yield* scan(state, root, EXTERNAL_SKILL_PATTERN, { dot: true, scope: "global" })
149158
}
150159

151160
const upDirs = yield* fsys
152161
.up({ targets: EXTERNAL_DIRS, start: directory, stop: worktree })
153162
.pipe(Effect.catch(() => Effect.succeed([] as string[])))
154163

155164
for (const root of upDirs) {
156-
yield* scan(state, bus, root, EXTERNAL_SKILL_PATTERN, { dot: true, scope: "project" })
165+
yield* scan(state, root, EXTERNAL_SKILL_PATTERN, { dot: true, scope: "project" })
157166
}
158167
}
159168

160169
const configDirs = yield* config.directories()
161170
for (const dir of configDirs) {
162-
yield* scan(state, bus, dir, OPENCODE_SKILL_PATTERN)
171+
yield* scan(state, dir, OPENCODE_SKILL_PATTERN)
163172
}
164173

165174
const cfg = yield* config.get()
@@ -171,17 +180,28 @@ const loadSkills = Effect.fnUntraced(function* (
171180
continue
172181
}
173182

174-
yield* scan(state, bus, dir, SKILL_PATTERN)
183+
yield* scan(state, dir, SKILL_PATTERN)
175184
}
176185

177186
for (const url of cfg.skills?.urls ?? []) {
178187
const pulledDirs = yield* discovery.pull(url)
179188
for (const dir of pulledDirs) {
180-
state.dirs.add(dir)
181-
yield* scan(state, bus, dir, SKILL_PATTERN)
189+
yield* scan(state, dir, SKILL_PATTERN)
182190
}
183191
}
184192

193+
return {
194+
matches: Array.from(state.matches),
195+
dirs: Array.from(state.dirs),
196+
}
197+
})
198+
199+
const loadSkills = Effect.fnUntraced(function* (state: State, discovered: DiscoveryState, bus: Bus.Interface) {
200+
yield* Effect.forEach(discovered.matches, (match) => add(state, match, bus), {
201+
concurrency: "unbounded",
202+
discard: true,
203+
})
204+
185205
log.info("init", { count: Object.keys(state.skills).length })
186206
})
187207

@@ -194,10 +214,15 @@ export const layer = Layer.effect(
194214
const config = yield* Config.Service
195215
const bus = yield* Bus.Service
196216
const fsys = yield* AppFileSystem.Service
217+
const discovered = yield* InstanceState.make(
218+
Effect.fn("Skill.discovery")(function* (ctx) {
219+
return yield* discoverSkills(config, discovery, fsys, ctx.directory, ctx.worktree)
220+
}),
221+
)
197222
const state = yield* InstanceState.make(
198223
Effect.fn("Skill.state")(function* (ctx) {
199224
const s: State = { skills: {}, dirs: new Set() }
200-
yield* loadSkills(s, config, discovery, bus, fsys, ctx.directory, ctx.worktree)
225+
yield* loadSkills(s, yield* InstanceState.get(discovered), bus)
201226
return s
202227
}),
203228
)
@@ -213,8 +238,7 @@ export const layer = Layer.effect(
213238
})
214239

215240
const dirs = Effect.fn("Skill.dirs")(function* () {
216-
const s = yield* InstanceState.get(state)
217-
return Array.from(s.dirs)
241+
return (yield* InstanceState.get(discovered)).dirs
218242
})
219243

220244
const available = Effect.fn("Skill.available")(function* (agent?: Agent.Info) {

0 commit comments

Comments
 (0)