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 { join } from 'node:path';
|
||||||
import type { AgentCommand } from './provider-types.js';
|
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 {
|
function frontmatterField(content: string, field: string): string | undefined {
|
||||||
const block = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
const block = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
||||||
if (!block?.[1]) return undefined;
|
if (!block?.[1]) return undefined;
|
||||||
const m = block[1].match(new RegExp(`^${field}:\\s*(.+)$`, 'm'));
|
const lines = block[1].split(/\r?\n/);
|
||||||
return m?.[1]?.trim().replace(/^["']|["']$/g, '') || undefined;
|
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[] {
|
function readCommandDir(dir: string): AgentCommand[] {
|
||||||
|
|||||||
Reference in New Issue
Block a user