Files
SpacetimeDB/docs/scripts/sync-agent-skills.mjs
clockwork-labs-bot dc20faa611 docs: improve docs and agent discovery metadata (#5243)
## Summary

- Fix TypeScript view examples to use `ctx.sender` as a property,
matching the server SDK `ViewCtx` API.
- Update the architecture overview TypeScript view snippet to use
`players.rowType` and `undefined` for optional view returns.
- Clarify shared `ViewContext` prose so it does not imply every language
uses a callable `ctx.sender()` API.
- Improve docs agent-readiness metadata:
- publish `/docs/robots.txt` with a docs sitemap directive and
Content-Signal policy
- add Markdown alternate links for the existing `/docs/llms.txt` and
`/docs/llms-full.txt` outputs
- generate `/.well-known/agent-skills`-style discovery metadata under
`/docs/.well-known/agent-skills/` from the repo's existing `skills/`
source files during docs builds

## Validation

- `pnpm --dir docs build`
- `pnpm --dir docs typecheck`
- Verified the docs build emits `robots.txt` and
`.well-known/agent-skills/index.json`.
- Verified generated Agent Skills SHA-256 digests match the emitted
`SKILL.md` artifacts.

## Notes

The Cloudflare agent-readiness scan for `spacetimedb.com` still depends
on the root/marketing host exposing root-level files such as
`/robots.txt`, `/sitemap.xml`, and
`/.well-known/agent-skills/index.json`. This PR makes the docs origin
produce the corresponding docs-scoped artifacts at `/docs/...`; the root
host can route or mirror these if we want the exact `spacetimedb.com`
scan to pick them up.

---------

Co-authored-by: clockwork-labs-bot <clockwork-labs-bot@users.noreply.github.com>
Co-authored-by: rain <rain@rain.local>
Co-authored-by: Tyler Cloutier <cloutiertyler@users.noreply.github.com>
2026-06-13 20:37:38 +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(/^---\n([\s\S]*?)\n---\n/);
if (!match) {
throw new Error(`${sourcePath} is missing YAML frontmatter`);
}
const frontmatter = {};
for (const line of match[1].split('\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`
);