diff --git a/scripts/prerender-routes.js b/scripts/prerender-routes.js index 8b73270..1f1e884 100644 --- a/scripts/prerender-routes.js +++ b/scripts/prerender-routes.js @@ -58,6 +58,13 @@ const ROUTES = [ description: 'Applying semantic anchors to brownfield codebases using a bounded-context approach.', }, + { + path: '/contracts', + fragment: 'docs/contracts.html', + title: 'Semantic Contracts — Semantic Anchors', + description: + 'Semantic Contracts define what terms mean in your project — composing established anchors or custom definitions for your AGENTS.md or CLAUDE.md.', + }, { path: '/changelog', fragment: 'docs/changelog.html', @@ -168,7 +175,7 @@ function prerenderRoute(shell, route) { fs.mkdirSync(outDir, { recursive: true }) fs.writeFileSync( outFile, - ``, + ``, 'utf-8' ) return diff --git a/scripts/render-contracts.js b/scripts/render-contracts.js new file mode 100644 index 0000000..94a4d82 --- /dev/null +++ b/scripts/render-contracts.js @@ -0,0 +1,89 @@ +#!/usr/bin/env node +/** + * Render a static HTML fragment from contracts.json for pre-rendering. + * + * The contracts page is JS-interactive (checkboxes, download), but crawlers + * and LLMs need to see the content without executing JavaScript. + * This script generates a plain HTML summary of all contracts that + * prerender-routes.js injects into the static shell. + * + * Output: website/public/docs/contracts.html + * + * Usage: node scripts/render-contracts.js + */ + +const fs = require('fs') +const path = require('path') + +const ROOT = path.join(__dirname, '..') +const CONTRACTS_JSON = path.join(ROOT, 'website/public/data/contracts.json') +const OUTPUT = path.join(ROOT, 'website/public/docs/contracts.html') + +function escapeHtml(str) { + return String(str) + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') +} + +function renderTemplate(template) { + return template + .split('\n') + .map((line) => { + if (line.startsWith('- ')) { + return `
${escapeHtml(line)}
` + }) + .join('\n') +} + +function renderContract(contract) { + const anchors = contract.anchors + .map( + (id) => + `${escapeHtml(id)}` + ) + .join(' ') + + return ` +${escapeHtml(contract.description)}
++ Semantic Anchors reference public knowledge that LLMs already understand. + But your team's conventions, templates, and definitions need Semantic Contracts. + A contract defines what a term means in your project — either by composing + established anchors or by providing custom definitions that only exist within your team. +
+