Skip to content

Commit e8af137

Browse files
fix(skill): normalize multiline frontmatter descriptions
1 parent 70bb710 commit e8af137

2 files changed

Lines changed: 37 additions & 1 deletion

File tree

packages/opencode/src/skill/index.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,12 @@ function isSkillFrontmatter(data: unknown): data is { name: string; description?
5858
)
5959
}
6060

61+
function normalizeDescription(description: string | undefined) {
62+
if (description === undefined) return undefined
63+
const normalized = description.replace(/\s+/g, " ").trim()
64+
return normalized.length > 0 ? normalized : undefined
65+
}
66+
6167
export class InvalidError extends Schema.TaggedErrorClass<InvalidError>()("SkillInvalidError", {
6268
path: Schema.String,
6369
message: Schema.optional(Schema.String),
@@ -133,7 +139,7 @@ const add = Effect.fnUntraced(function* (state: State, match: string, events: Ev
133139
state.dirs.add(path.dirname(match))
134140
state.skills[md.data.name] = {
135141
name: md.data.name,
136-
description: md.data.description,
142+
description: normalizeDescription(md.data.description),
137143
location: match,
138144
content: md.content,
139145
}

packages/opencode/test/skill/skill.test.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,36 @@ Instructions here.
108108
),
109109
)
110110

111+
it.live("normalizes multiline YAML descriptions in SKILL.md frontmatter", () =>
112+
provideTmpdirInstance(
113+
(dir) =>
114+
Effect.gen(function* () {
115+
yield* Effect.promise(() =>
116+
Bun.write(
117+
path.join(dir, ".opencode", "skill", "multiline-desc", "SKILL.md"),
118+
`---
119+
name: multiline-desc
120+
description: |
121+
This is a multiline description
122+
that spans multiple lines
123+
using YAML pipe syntax
124+
---
125+
126+
# Multiline Skill
127+
`,
128+
),
129+
)
130+
131+
const skill = yield* Skill.Service
132+
const list = (yield* skill.all()).filter((s) => s.location !== "<built-in>")
133+
const item = list.find((x) => x.name === "multiline-desc")
134+
expect(item).toBeDefined()
135+
expect(item!.description).toBe("This is a multiline description that spans multiple lines using YAML pipe syntax")
136+
}),
137+
{ git: true },
138+
),
139+
)
140+
111141
it.live("returns skill directories from Skill.dirs", () =>
112142
provideTmpdirInstance(
113143
(dir) =>

0 commit comments

Comments
 (0)