fix(skill): allow missing descriptions (#26391)

This commit is contained in:
Dax
2026-05-08 18:50:06 +00:00
committed by GitHub
parent 75308ea47d
commit fed221e0b0
4 changed files with 45 additions and 7 deletions
+7 -6
View File
@@ -27,7 +27,7 @@ const SKILL_PATTERN = "**/SKILL.md"
export const Info = Schema.Struct({ export const Info = Schema.Struct({
name: Schema.String, name: Schema.String,
description: Schema.String, description: Schema.optional(Schema.String),
location: Schema.String, location: Schema.String,
content: Schema.String, content: Schema.String,
}).pipe(withStatics((s) => ({ zod: zod(s) }))) }).pipe(withStatics((s) => ({ zod: zod(s) })))
@@ -93,7 +93,7 @@ const add = Effect.fnUntraced(function* (state: State, match: string, bus: Bus.I
if (!md) return if (!md) return
const parsed = z.object({ name: z.string(), description: z.string() }).safeParse(md.data) const parsed = z.object({ name: z.string(), description: z.string().optional() }).safeParse(md.data)
if (!parsed.success) return if (!parsed.success) return
if (state.skills[parsed.data.name]) { if (state.skills[parsed.data.name]) {
@@ -269,12 +269,13 @@ export const defaultLayer = layer.pipe(
) )
export function fmt(list: Info[], opts: { verbose: boolean }) { export function fmt(list: Info[], opts: { verbose: boolean }) {
if (list.length === 0) return "No skills are currently available." const described = list.filter((skill) => skill.description !== undefined)
if (described.length === 0) return "No skills are currently available."
if (opts.verbose) { if (opts.verbose) {
return [ return [
"<available_skills>", "<available_skills>",
...list ...described
.sort((a, b) => a.name.localeCompare(b.name)) .toSorted((a, b) => a.name.localeCompare(b.name))
.flatMap((skill) => [ .flatMap((skill) => [
" <skill>", " <skill>",
` <name>${skill.name}</name>`, ` <name>${skill.name}</name>`,
@@ -288,7 +289,7 @@ export function fmt(list: Info[], opts: { verbose: boolean }) {
return [ return [
"## Available Skills", "## Available Skills",
...list ...described
.toSorted((a, b) => a.name.localeCompare(b.name)) .toSorted((a, b) => a.name.localeCompare(b.name))
.map((skill) => `- **${skill.name}**: ${skill.description}`), .map((skill) => `- **${skill.name}**: ${skill.description}`),
].join("\n") ].join("\n")
@@ -26,6 +26,11 @@ const skills: Skill.Info[] = [
location: "/tmp/middle-skill/SKILL.md", location: "/tmp/middle-skill/SKILL.md",
content: "# middle-skill", content: "# middle-skill",
}, },
{
name: "manual-skill",
location: "/tmp/manual-skill/SKILL.md",
content: "# manual-skill",
},
] ]
const build: Agent.Info = { const build: Agent.Info = {
@@ -68,6 +73,7 @@ describe("session.system", () => {
expect(alpha).toBeGreaterThan(-1) expect(alpha).toBeGreaterThan(-1)
expect(middle).toBeGreaterThan(alpha) expect(middle).toBeGreaterThan(alpha)
expect(zeta).toBeGreaterThan(middle) expect(zeta).toBeGreaterThan(middle)
expect(output).not.toContain("manual-skill")
}), }),
) )
}) })
@@ -163,6 +163,37 @@ Just some content without YAML frontmatter.
), ),
) )
it.live("discovers skills without descriptions", () =>
provideTmpdirInstance(
(dir) =>
Effect.gen(function* () {
yield* Effect.promise(() =>
Bun.write(
path.join(dir, ".opencode", "skill", "manual-skill", "SKILL.md"),
`---
name: manual-skill
---
# Manual Skill
Instructions here.
`,
),
)
const skill = yield* Skill.Service
const list = yield* skill.all()
expect(list.length).toBe(1)
const item = list.find((x) => x.name === "manual-skill")
expect(item).toBeDefined()
expect(item!.description).toBeUndefined()
expect(Skill.fmt(list, { verbose: false })).toBe("No skills are currently available.")
expect(Skill.fmt(list, { verbose: true })).toBe("No skills are currently available.")
}),
{ git: true },
),
)
it.live("discovers skills from .claude/skills/ directory", () => it.live("discovers skills from .claude/skills/ directory", () =>
provideTmpdirInstance( provideTmpdirInstance(
(dir) => (dir) =>
+1 -1
View File
@@ -4195,7 +4195,7 @@ export type AppSkillsResponses = {
*/ */
200: Array<{ 200: Array<{
name: string name: string
description: string description?: string
location: string location: string
content: string content: string
}> }>