Skip to content

Commit 45ec310

Browse files
feat: support config skill registration (#9640)
Co-authored-by: Aiden Cline <[email protected]>
1 parent 5a56e81 commit 45ec310

4 files changed

Lines changed: 38 additions & 3 deletions

File tree

packages/opencode/src/config/config.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -560,6 +560,11 @@ export namespace Config {
560560
})
561561
export type Command = z.infer<typeof Command>
562562

563+
export const Skills = z.object({
564+
paths: z.array(z.string()).optional().describe("Additional paths to skill folders"),
565+
})
566+
export type Skills = z.infer<typeof Skills>
567+
563568
export const Agent = z
564569
.object({
565570
model: z.string().optional(),
@@ -895,6 +900,7 @@ export namespace Config {
895900
.record(z.string(), Command)
896901
.optional()
897902
.describe("Command configuration, see https://opencode.ai/docs/commands"),
903+
skills: Skills.optional().describe("Additional skill folder paths"),
898904
watcher: z
899905
.object({
900906
ignore: z.array(z.string()).optional(),

packages/opencode/src/skill/skill.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import z from "zod"
22
import path from "path"
3+
import os from "os"
34
import { Config } from "../config/config"
45
import { Instance } from "../project/instance"
56
import { NamedError } from "@opencode-ai/util/error"
@@ -40,6 +41,7 @@ export namespace Skill {
4041

4142
const OPENCODE_SKILL_GLOB = new Bun.Glob("{skill,skills}/**/SKILL.md")
4243
const CLAUDE_SKILL_GLOB = new Bun.Glob("skills/**/SKILL.md")
44+
const SKILL_GLOB = new Bun.Glob("**/SKILL.md")
4345

4446
export const state = Instance.state(async () => {
4547
const skills: Record<string, Info> = {}
@@ -122,6 +124,25 @@ export namespace Skill {
122124
}
123125
}
124126

127+
// Scan additional skill paths from config
128+
const config = await Config.get()
129+
for (const skillPath of config.skills?.paths ?? []) {
130+
const expanded = skillPath.startsWith("~/") ? path.join(os.homedir(), skillPath.slice(2)) : skillPath
131+
const resolved = path.isAbsolute(expanded) ? expanded : path.join(Instance.directory, expanded)
132+
if (!(await Filesystem.isDir(resolved))) {
133+
log.warn("skill path not found", { path: resolved })
134+
continue
135+
}
136+
for await (const match of SKILL_GLOB.scan({
137+
cwd: resolved,
138+
absolute: true,
139+
onlyFiles: true,
140+
followSymlinks: true,
141+
})) {
142+
await addSkill(match)
143+
}
144+
}
145+
125146
return skills
126147
})
127148

packages/opencode/src/tool/skill.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,12 +62,11 @@ export const SkillTool = Tool.define("skill", async (ctx) => {
6262
always: [params.name],
6363
metadata: {},
6464
})
65-
// Load and parse skill content
66-
const parsed = await ConfigMarkdown.parse(skill.location)
65+
const content = (await ConfigMarkdown.parse(skill.location)).content
6766
const dir = path.dirname(skill.location)
6867

6968
// Format output similar to plugin pattern
70-
const output = [`## Skill: ${skill.name}`, "", `**Base directory**: ${dir}`, "", parsed.content.trim()].join("\n")
69+
const output = [`## Skill: ${skill.name}`, "", `**Base directory**: ${dir}`, "", content.trim()].join("\n")
7170

7271
return {
7372
title: `Loaded skill: ${skill.name}`,

packages/sdk/js/src/v2/gen/types.gen.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1633,6 +1633,15 @@ export type Config = {
16331633
subtask?: boolean
16341634
}
16351635
}
1636+
/**
1637+
* Additional skill folder paths to scan
1638+
*/
1639+
skills?: {
1640+
/**
1641+
* Additional paths to skill folders to scan
1642+
*/
1643+
paths?: Array<string>
1644+
}
16361645
watcher?: {
16371646
ignore?: Array<string>
16381647
}

0 commit comments

Comments
 (0)