Skip to content

Commit 0e10ad9

Browse files
committed
fix(agents): reject implicitly typed YAML rule descriptions
1 parent f354322 commit 0e10ad9

1 file changed

Lines changed: 22 additions & 2 deletions

File tree

‎scripts/sync-skills.ts‎

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ function parseRule(name: string, raw: string): Rule {
134134
if (!description) throw new Error(`${name}: missing 'description' in frontmatter`)
135135
if (!isPlainYamlScalar(description)) {
136136
throw new Error(
137-
`${name}: 'description' must be a plain YAML scalar (no leading quote or indicator, no ': ' or ' #') so both Claude and Cursor parse it`
137+
`${name}: 'description' must be a plain YAML string (no leading quote or indicator, no ': ' or ' #', and not a bare boolean, null, number, or date) so both Claude and Cursor parse it`
138138
)
139139
}
140140
return { name, description, paths, body }
@@ -150,9 +150,29 @@ function isPlainYamlScalar(value: string): boolean {
150150
if (value !== value.trim()) return false
151151
if (/^[-?:,[\]{}#&*!|>'"%@`]/.test(value)) return false
152152
if (value.includes(': ') || value.includes(' #')) return false
153-
return !value.endsWith(':')
153+
if (value.endsWith(':')) return false
154+
return !IMPLICITLY_TYPED_SCALAR.test(value)
154155
}
155156

157+
/**
158+
* Plain scalars a YAML 1.1 or 1.2 reader resolves to something other than a
159+
* string: booleans, null, integers (decimal, hex, octal, binary, underscored),
160+
* floats (including .inf/.nan), and dates or timestamps.
161+
*/
162+
const IMPLICITLY_TYPED_SCALAR = new RegExp(
163+
[
164+
'^(?:',
165+
'~|null|true|false|yes|no|on|off|y|n',
166+
'|[-+]?(?:0b[01_]+|0o?[0-7_]+|0x[0-9a-f_]+|[0-9][0-9_]*(?::[0-5]?[0-9])*)',
167+
'|[-+]?(?:[0-9][0-9_]*)?\\.[0-9_]*(?:e[-+]?[0-9]+)?',
168+
'|[-+]?[0-9][0-9_]*(?:\\.[0-9_]*)?e[-+]?[0-9]+',
169+
'|[-+]?\\.(?:inf|nan)',
170+
'|[0-9]{4}-[0-9]{1,2}-[0-9]{1,2}(?:[tT ].*)?',
171+
')$',
172+
].join(''),
173+
'i'
174+
)
175+
156176
async function loadCanonicalRules(): Promise<Rule[]> {
157177
const entries = await readdir(CANONICAL_RULES_DIR)
158178
const rules: Rule[] = []

0 commit comments

Comments
 (0)