fix(coder): parse YAML block-scalar descriptions in slash command discovery
Most plugin/han SKILL.md and command files write `description:` as a folded block scalar (`>` / `|`) with the text on the following indented lines. The old single-line frontmatter reader captured the literal `>`, so the slash menu showed garbage/blank descriptions for nearly all of them. frontmatterField now collapses folded blocks (join with spaces) and preserves literal blocks. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -12,12 +12,48 @@ import { homedir } from 'node:os';
|
||||
import { join } from 'node:path';
|
||||
import type { AgentCommand } from './provider-types.js';
|
||||
|
||||
/** Minimal frontmatter reader — single-line `key: value` between `---` fences. */
|
||||
/**
|
||||
* Frontmatter reader between `---` fences. Handles single-line `key: value`
|
||||
* AND YAML block scalars (`key: >` folded / `key: |` literal) whose value
|
||||
* spans the following more-indented lines — the shape most plugin SKILL.md
|
||||
* descriptions use (`description: >`).
|
||||
*/
|
||||
function frontmatterField(content: string, field: string): string | undefined {
|
||||
const block = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
||||
if (!block?.[1]) return undefined;
|
||||
const m = block[1].match(new RegExp(`^${field}:\\s*(.+)$`, 'm'));
|
||||
return m?.[1]?.trim().replace(/^["']|["']$/g, '') || undefined;
|
||||
const lines = block[1].split(/\r?\n/);
|
||||
const keyRe = new RegExp(`^(\\s*)${field}:\\s*(.*)$`);
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
const m = lines[i]?.match(keyRe);
|
||||
if (!m) continue;
|
||||
const keyIndent = (m[1] ?? '').length;
|
||||
const inline = (m[2] ?? '').trim();
|
||||
// Block scalar: `>` (folded) or `|` (literal), optional chomping `+`/`-`.
|
||||
if (/^[>|][+-]?$/.test(inline)) {
|
||||
const folded = inline[0] === '>';
|
||||
const body: string[] = [];
|
||||
for (let j = i + 1; j < lines.length; j++) {
|
||||
const line = lines[j] ?? '';
|
||||
if (line.trim() === '') {
|
||||
body.push('');
|
||||
continue;
|
||||
}
|
||||
const indent = line.length - line.trimStart().length;
|
||||
if (indent <= keyIndent) break; // dedent ends the block
|
||||
body.push(line.slice(keyIndent + 1));
|
||||
}
|
||||
const joined = folded
|
||||
? body
|
||||
.map((l) => l.trim())
|
||||
.join(' ')
|
||||
.replace(/\s+/g, ' ')
|
||||
.trim()
|
||||
: body.join('\n').replace(/\n+$/, '');
|
||||
return joined || undefined;
|
||||
}
|
||||
return inline.replace(/^["']|["']$/g, '').trim() || undefined;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function readCommandDir(dir: string): AgentCommand[] {
|
||||
|
||||
Reference in New Issue
Block a user