Files
SpacetimeDB/docs/scripts/sync-agent-skills.mjs
Ryan 16aadbf0bf Allow sync-agent-skills.mjs to match Windows line ends (#5477)
# Description of Changes

The readFrontmatter regex in `docs/scripts/sync-agent-skills.mjs` uses
`\n` to match line endings, which fails on Windows where files have CRLF
(`\r\n`) endings. This causes `pnpm run build` to fail with:
```
Error: skills\cli\SKILL.md is missing YAML frontmatter
```
Fix: change `\n` → `\r?\n` in the frontmatter regex and use /\r?\n/ for
line splitting so the script works on both Unix and Windows.

# API and ABI breaking changes

No API or ABI changes

# Expected complexity level and risk

1 - Trivial

# Testing

- [X] When used along side `bump-versions`, this prevents Windows from
having the outlined issue.
2026-07-03 13:01:22 +00:00

74 lines
2.1 KiB
JavaScript

import { createHash } from 'node:crypto';
import { mkdir, readdir, readFile, rm, writeFile } from 'node:fs/promises';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const repoRoot = path.resolve(__dirname, '../..');
const sourceDir = path.join(repoRoot, 'skills');
const outputDir = path.join(
repoRoot,
'docs/static/.well-known/agent-skills'
);
function readFrontmatter(markdown, sourcePath) {
const match = markdown.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n/);
if (!match) {
throw new Error(`${sourcePath} is missing YAML frontmatter`);
}
const frontmatter = {};
for (const line of match[1].split(/\r?\n/)) {
const field = line.match(/^([a-zA-Z0-9_-]+):\s*(.*)$/);
if (field) {
frontmatter[field[1]] = field[2].replace(/^"(.*)"$/, '$1');
}
}
if (!frontmatter.name || !frontmatter.description) {
throw new Error(`${sourcePath} must declare name and description`);
}
return frontmatter;
}
await rm(outputDir, { recursive: true, force: true });
await mkdir(outputDir, { recursive: true });
const skills = [];
for (const entry of (await readdir(sourceDir, { withFileTypes: true })).sort(
(a, b) => a.name.localeCompare(b.name)
)) {
if (!entry.isDirectory()) {
continue;
}
const skillPath = path.join(sourceDir, entry.name, 'SKILL.md');
const markdown = await readFile(skillPath, 'utf8');
const metadata = readFrontmatter(markdown, skillPath);
const skillOutputDir = path.join(outputDir, metadata.name);
await mkdir(skillOutputDir, { recursive: true });
await writeFile(path.join(skillOutputDir, 'SKILL.md'), markdown);
skills.push({
name: metadata.name,
type: 'skill-md',
description: metadata.description,
url: `/docs/.well-known/agent-skills/${metadata.name}/SKILL.md`,
digest: `sha256:${createHash('sha256').update(markdown).digest('hex')}`,
});
}
await writeFile(
path.join(outputDir, 'index.json'),
`${JSON.stringify(
{
$schema: 'https://schemas.agentskills.io/discovery/0.2.0/schema.json',
skills,
},
null,
2
)}\n`
);