Skip to content

Commit ed3b983

Browse files
montfortclaude
andauthored
feat(website): publish llms.txt + generated llms-full.txt (#295)
Add the llmstxt.org convention to straymark.dev: a hand-curated /llms.txt index (the docs map: adopters + contributors) plus a build-generated /llms-full.txt that concatenates the full doc text for LLM consumption. - website/static/llms.txt: curated index (H1 + blockquote + section links), managed like robots.txt / sitemap-index.xml. - website/scripts/gen-llms-full.ts: tsx generator mirroring sync-docs-i18n.ts; reads repo-root docs/ (intro + adopters/** + contributors/**), strips frontmatter via gray-matter, emits static/llms-full.txt with canonical URLs. - Wired into prestart/prebuild; output gitignored (regenerated each build). Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 8f43343 commit ed3b983

4 files changed

Lines changed: 113 additions & 2 deletions

File tree

website/.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@
1111
# Output of scripts/sync-docs-i18n.ts (regenerated at prestart/prebuild)
1212
/i18n/*/docusaurus-plugin-content-docs/
1313

14+
# Output of scripts/gen-llms-full.ts (regenerated at prestart/prebuild)
15+
/static/llms-full.txt
16+
1417
# Misc
1518
.DS_Store
1619
.env.local

website/package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,11 @@
55
"scripts": {
66
"docusaurus": "docusaurus",
77
"sync:docs": "tsx scripts/sync-docs-i18n.ts",
8+
"generate:llms": "tsx scripts/gen-llms-full.ts",
89
"migrate:blog": "tsx scripts/migrate-blog.ts",
9-
"prestart": "npm run sync:docs",
10+
"prestart": "npm run sync:docs && npm run generate:llms",
1011
"start": "docusaurus start",
11-
"prebuild": "npm run sync:docs",
12+
"prebuild": "npm run sync:docs && npm run generate:llms",
1213
"build": "docusaurus build",
1314
"swizzle": "docusaurus swizzle",
1415
"deploy": "docusaurus deploy",

website/scripts/gen-llms-full.ts

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
// Generates website/static/llms-full.txt — the full text of the canonical docs
2+
// concatenated into a single file, served verbatim at /llms-full.txt (the
3+
// "give me everything" companion to the hand-curated /llms.txt index).
4+
//
5+
// Source of truth is repo-root docs/, mirroring the docs plugin's include rule
6+
// in docusaurus.config.ts (intro.md, adopters/**, contributors/**). The output
7+
// is a generated artifact (gitignored) and is regenerated by prestart/prebuild,
8+
// exactly like scripts/sync-docs-i18n.ts.
9+
import {readdirSync, readFileSync, writeFileSync, statSync} from 'node:fs';
10+
import {join, resolve} from 'node:path';
11+
import matter from 'gray-matter';
12+
13+
const ROOT = resolve(__dirname, '..');
14+
const DOCS = resolve(ROOT, '../docs');
15+
const OUT = join(ROOT, 'static', 'llms-full.txt');
16+
const SITE_URL = 'https://straymark.dev';
17+
18+
// Directories under docs/ to walk, in output order. intro.md is handled first
19+
// and separately (it carries `slug: /`). decisions/ and proposals/ are excluded
20+
// by omission — they live outside these two dirs.
21+
const DIRS = ['adopters', 'contributors'];
22+
23+
/** Recursively collect *.md files under `dir`, returned sorted for stable output. */
24+
function collectMarkdown(dir: string): string[] {
25+
const out: string[] = [];
26+
for (const entry of readdirSync(dir)) {
27+
const full = join(dir, entry);
28+
if (statSync(full).isDirectory()) {
29+
out.push(...collectMarkdown(full));
30+
} else if (entry.endsWith('.md')) {
31+
out.push(full);
32+
}
33+
}
34+
return out.sort();
35+
}
36+
37+
/** Map an absolute doc path to its public Docusaurus route (trailingSlash: false). */
38+
function routeFor(absPath: string): string {
39+
const rel = absPath.slice(DOCS.length + 1).replace(/\.md$/, '');
40+
if (rel === 'intro') return `${SITE_URL}/docs`; // slug: / → docs root
41+
// README.md / index.md become the directory index route.
42+
const asIndex = rel.replace(/\/(README|index)$/, '');
43+
if (asIndex !== rel) return `${SITE_URL}/docs/${asIndex}`;
44+
return `${SITE_URL}/docs/${rel}`;
45+
}
46+
47+
/** Title from frontmatter `title`, else first `# H1`, else the filename. */
48+
function titleFor(absPath: string, data: Record<string, unknown>, body: string): string {
49+
if (typeof data.title === 'string' && data.title.trim()) return data.title.trim();
50+
const h1 = body.match(/^#\s+(.+)$/m);
51+
if (h1) return h1[1].trim();
52+
return absPath.slice(DOCS.length + 1).replace(/\.md$/, '');
53+
}
54+
55+
// Ordered file list: intro first, then each configured dir.
56+
const files = [join(DOCS, 'intro.md'), ...DIRS.flatMap((d) => collectMarkdown(join(DOCS, d)))];
57+
58+
const sections: string[] = [
59+
'# StrayMark — Full Documentation',
60+
'',
61+
'> The complete text of the StrayMark adopter and contributor documentation,',
62+
'> concatenated for LLM consumption. The curated index lives at /llms.txt.',
63+
'> Canonical source: https://github.com/StrangeDaysTech/straymark/tree/main/docs',
64+
'',
65+
];
66+
67+
for (const file of files) {
68+
const raw = readFileSync(file, 'utf8');
69+
const {content, data} = matter(raw);
70+
const title = titleFor(file, data, content);
71+
const url = routeFor(file);
72+
sections.push('---', '', `## ${title}`, '', `Source: ${url}`, '', content.trim(), '');
73+
}
74+
75+
writeFileSync(OUT, sections.join('\n') + '\n');
76+
console.log(`[gen-llms-full] wrote ${OUT} (${files.length} docs)`);

website/static/llms.txt

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# StrayMark
2+
3+
> StrayMark is a governance framework for AI-assisted software development:
4+
> documentation templates, governance policies, and agent directives, managed by
5+
> the `straymark` Rust CLI that installs and maintains the framework in a project.
6+
> It keeps human- and agent-authored docs compliant, traceable, and current.
7+
8+
The full text of the documentation is concatenated at /llms-full.txt. The site is
9+
also available in Spanish (/es/) and Simplified Chinese (/zh-CN/); the links below
10+
point at the canonical English pages.
11+
12+
## Docs
13+
14+
- [Introduction](https://straymark.dev/docs): what StrayMark is and how to get started.
15+
- [Adoption Guide](https://straymark.dev/docs/adopters/ADOPTION-GUIDE): install StrayMark and adopt it in a new or existing project.
16+
- [CLI Reference](https://straymark.dev/docs/adopters/CLI-REFERENCE): every command of the `straymark` binary.
17+
- [Workflows](https://straymark.dev/docs/adopters/WORKFLOWS): recommended day-to-day workflows.
18+
- [Loom](https://straymark.dev/docs/adopters/LOOM): the experimental knowledge-graph / architecture visualization component.
19+
- [Adopter Feedback](https://straymark.dev/docs/adopters/ADOPTER-FEEDBACK): how to send feedback as an adopter.
20+
21+
## Contributors
22+
23+
- [Contributors Overview](https://straymark.dev/docs/contributors): resources for people contributing to StrayMark.
24+
- [Design Principles](https://straymark.dev/docs/contributors/DESIGN-PRINCIPLES): the principles behind the framework's shape.
25+
- [What is a Charter](https://straymark.dev/docs/contributors/WHAT-IS-A-CHARTER): the charter concept and how charters work.
26+
- [Translation Guide](https://straymark.dev/docs/contributors/TRANSLATION-GUIDE): how to translate the docs.
27+
28+
## Optional
29+
30+
- [Full documentation](https://straymark.dev/llms-full.txt): every doc page concatenated into one file.
31+
- [Blog](https://straymark.dev/blog): the StrayMark chronicle — how the framework emerged, decision by decision.

0 commit comments

Comments
 (0)