feat/wiki-astro #214
7 changed files with 298 additions and 0 deletions
3
astro/wiki/.gitignore
vendored
Normal file
3
astro/wiki/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
node_modules/
|
||||
dist/
|
||||
.astro/
|
||||
12
astro/wiki/astro.config.mjs
Normal file
12
astro/wiki/astro.config.mjs
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
import { defineConfig } from "astro/config";
|
||||
|
||||
const site = process.env.ASTRO_SITE_URL || "https://wiki.clawdie.si";
|
||||
const outDir = process.env.ASTRO_OUT_DIR || "./dist";
|
||||
|
||||
// https://astro.build/config
|
||||
export default defineConfig({
|
||||
site,
|
||||
outDir,
|
||||
output: "static",
|
||||
trailingSlash: "always",
|
||||
});
|
||||
14
astro/wiki/package.json
Normal file
14
astro/wiki/package.json
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"name": "clawdie-wiki",
|
||||
"private": true,
|
||||
"version": "0.12.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "astro dev --host 0.0.0.0",
|
||||
"build": "astro build",
|
||||
"preview": "astro preview --host 0.0.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"astro": "^5.16.11"
|
||||
}
|
||||
}
|
||||
131
astro/wiki/src/pages/[...slug].astro
Normal file
131
astro/wiki/src/pages/[...slug].astro
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
---
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
|
||||
const WIKI_DIR = path.resolve("../../docs/wiki");
|
||||
const EXCLUDE = [".git", "sl", "index.md"];
|
||||
|
||||
export function getStaticPaths() {
|
||||
function walk(dir, prefix = "") {
|
||||
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
||||
const slugs = [];
|
||||
for (const e of entries) {
|
||||
if (e.name.startsWith(".") || EXCLUDE.includes(e.name)) continue;
|
||||
const full = path.join(dir, e.name);
|
||||
if (e.isDirectory()) {
|
||||
slugs.push(...walk(full, prefix ? `${prefix}/${e.name}` : e.name));
|
||||
} else if (e.name.endsWith(".md")) {
|
||||
const rel = prefix ? `${prefix}/${e.name}` : e.name;
|
||||
slugs.push({ params: { slug: rel.replace(/\.md$/, "") } });
|
||||
}
|
||||
}
|
||||
return slugs;
|
||||
}
|
||||
return walk(WIKI_DIR);
|
||||
}
|
||||
|
||||
const { slug } = Astro.params;
|
||||
const filePath = path.join(WIKI_DIR, `${slug}.md`);
|
||||
|
||||
if (!fs.existsSync(filePath)) {
|
||||
return new Response("Not found", { status: 404 });
|
||||
}
|
||||
|
||||
const raw = fs.readFileSync(filePath, "utf-8");
|
||||
|
||||
// Parse frontmatter
|
||||
let content = raw;
|
||||
let frontmatter = {};
|
||||
if (raw.startsWith("---")) {
|
||||
const end = raw.indexOf("---", 3);
|
||||
if (end !== -1) {
|
||||
const fm = raw.slice(3, end);
|
||||
for (const line of fm.split("\n")) {
|
||||
const m = line.match(/^(\w+):\s*(.+)$/);
|
||||
if (m) frontmatter[m[1]] = m[2].replace(/^["']|["']$/g, "");
|
||||
}
|
||||
content = raw.slice(end + 3).trim();
|
||||
}
|
||||
}
|
||||
|
||||
// Resolve relative wiki links [label](./page.md) → [label](/page/)
|
||||
const resolveLinks = (md) =>
|
||||
md.replace(/\]\(\.\/([^)]+)\.md\)/g, "](/$1/)")
|
||||
.replace(/\]\(\.\.\/([^)]+)\.md\)/g, "](/$1/)");
|
||||
|
||||
content = resolveLinks(content);
|
||||
|
||||
// Render fenced code blocks
|
||||
const renderCode = (md) =>
|
||||
md.replace(/```(\w*)\n([\s\S]*?)```/g, (_, lang, code) => {
|
||||
return `<pre><code${lang ? ` class="language-${lang}"` : ""}>${code.trim()}</code></pre>`;
|
||||
});
|
||||
|
||||
content = renderCode(content);
|
||||
|
||||
// Render tables
|
||||
const renderTables = (md) => {
|
||||
return md.replace(/\|(.+)\|\n\|[-| ]+\|\n((?:\|.+\|\n?)*)/gm, (_, header, rows) => {
|
||||
const hcells = header.split("|").map(c => c.trim()).filter(Boolean);
|
||||
const thead = `<tr>${hcells.map(c => `<th>${c}</th>`).join("")}</tr>`;
|
||||
const tbody = rows.trim().split("\n").map(row => {
|
||||
const cells = row.split("|").map(c => c.trim()).filter(Boolean);
|
||||
return `<tr>${cells.map(c => `<td>${c}</td>`).join("")}</tr>`;
|
||||
}).join("");
|
||||
return `<table><thead>${thead}</thead><tbody>${tbody}</tbody></table>`;
|
||||
});
|
||||
};
|
||||
content = renderTables(content);
|
||||
|
||||
// Render inline code, bold, italic, links, headings, lists
|
||||
content = content
|
||||
.replace(/`([^`]+)`/g, "<code>$1</code>")
|
||||
.replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>")
|
||||
.replace(/\*([^*]+)\*/g, "<em>$1</em>")
|
||||
.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2">$1</a>')
|
||||
.replace(/^### (.+)$/gm, "<h3>$1</h3>")
|
||||
.replace(/^## (.+)$/gm, "<h2>$1</h2>")
|
||||
.replace(/^# (.+)$/gm, "<h1>$1</h1>")
|
||||
.replace(/^- (.+)$/gm, "<li>$1</li>")
|
||||
.replace(/((?:<li>.*<\/li>\n?)+)/g, "<ul>$1</ul>")
|
||||
.replace(/\n\n/g, "</p><p>")
|
||||
.replace(/^(.+)$/gm, (line) => {
|
||||
if (line.startsWith("<")) return line;
|
||||
return line;
|
||||
});
|
||||
|
||||
const title = frontmatter.title || slug;
|
||||
---
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>{(title)} — Colibri Wiki</title>
|
||||
<style>
|
||||
:root { --bg: #fff; --fg: #1a1a1a; --link: #0366d6; --muted: #666; --border: #e0e0e0; }
|
||||
@media (prefers-color-scheme: dark) { :root { --bg: #1a1a1a; --fg: #e6e6e6; --link: #58a6ff; --muted: #999; --border: #333; } }
|
||||
body { max-width: 720px; margin: 2rem auto; padding: 0 1rem; font: 16px/1.6 system-ui; background: var(--bg); color: var(--fg); }
|
||||
nav { margin-bottom: 1.5rem; }
|
||||
nav a { color: var(--muted); font-size: .9rem; }
|
||||
h1 { font-size: 1.8rem; }
|
||||
h2 { font-size: 1.4rem; margin-top: 2rem; border-bottom: 1px solid var(--border); padding-bottom: .3rem; }
|
||||
h3 { font-size: 1.1rem; margin-top: 1.5rem; }
|
||||
a { color: var(--link); }
|
||||
pre { background: var(--border); padding: .8rem 1rem; border-radius: 4px; overflow-x: auto; font-size: .9rem; }
|
||||
code { font-size: .9em; background: var(--border); padding: .1em .3em; border-radius: 3px; }
|
||||
pre code { background: none; padding: 0; }
|
||||
table { border-collapse: collapse; width: 100%; margin: 1rem 0; }
|
||||
th, td { border: 1px solid var(--border); padding: .4rem .6rem; text-align: left; font-size: .9rem; }
|
||||
th { background: var(--border); }
|
||||
hr { border: none; border-top: 1px solid var(--border); margin: 2rem 0; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<nav><a href="/">← wiki index</a></nav>
|
||||
<article>
|
||||
<h1>{title}</h1>
|
||||
<p set:html={content} />
|
||||
</article>
|
||||
</body>
|
||||
</html>
|
||||
60
astro/wiki/src/pages/index.astro
Normal file
60
astro/wiki/src/pages/index.astro
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
---
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
|
||||
const WIKI_DIR = path.resolve("../../docs/wiki");
|
||||
const EXCLUDE = [".git", "sl", "index.md"];
|
||||
|
||||
function walkMarkdown(dir, prefix = "") {
|
||||
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
||||
const files = [];
|
||||
for (const e of entries) {
|
||||
if (e.name.startsWith(".") || EXCLUDE.includes(e.name)) continue;
|
||||
const full = path.join(dir, e.name);
|
||||
if (e.isDirectory()) {
|
||||
files.push(...walkMarkdown(full, prefix ? `${prefix}/${e.name}` : e.name));
|
||||
} else if (e.name.endsWith(".md")) {
|
||||
const rel = prefix ? `${prefix}/${e.name}` : e.name;
|
||||
const slug = rel.replace(/\.md$/, "");
|
||||
// Skip frontmatter, grab first H1 as title
|
||||
const raw = fs.readFileSync(full, "utf-8");
|
||||
const title = raw.match(/^#\s+(.+)$/m)?.[1] || slug;
|
||||
files.push({ slug, title, file: rel });
|
||||
}
|
||||
}
|
||||
return files.sort((a, b) => a.title.localeCompare(b.title));
|
||||
}
|
||||
|
||||
const pages = walkMarkdown(WIKI_DIR);
|
||||
---
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Colibri Wiki</title>
|
||||
<style>
|
||||
:root { --bg: #fff; --fg: #1a1a1a; --link: #0366d6; --muted: #666; }
|
||||
@media (prefers-color-scheme: dark) { :root { --bg: #1a1a1a; --fg: #e6e6e6; --link: #58a6ff; --muted: #999; } }
|
||||
body { max-width: 720px; margin: 2rem auto; padding: 0 1rem; font: 16px/1.6 system-ui; background: var(--bg); color: var(--fg); }
|
||||
h1 { margin-bottom: .25rem; }
|
||||
p.lede { color: var(--muted); margin-bottom: 1.5rem; }
|
||||
ul { list-style: none; padding: 0; }
|
||||
li { margin: .35rem 0; }
|
||||
a { color: var(--link); text-decoration: none; }
|
||||
a:hover { text-decoration: underline; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Colibri Wiki</h1>
|
||||
<p class="lede">
|
||||
Decision pages — the <em>why</em> behind the architecture.
|
||||
<a href="https://github.com/karpathy/llm-wiki">LLM Wiki pattern</a>.
|
||||
</p>
|
||||
<ul>
|
||||
{pages.map((p) => (
|
||||
<li><a href={`/${p.slug}/`}>{p.title}</a></li>
|
||||
))}
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
||||
49
docs/PLAN-WIKI-CLAWDIE-SI.md
Normal file
49
docs/PLAN-WIKI-CLAWDIE-SI.md
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
# wiki.clawdie.si — separate domain for decision pages
|
||||
|
||||
**Status:** planned · **Created:** 26.jun.2026 · **Blocks:** nothing in 0.12
|
||||
|
||||
## Goal
|
||||
|
||||
Split the current docs.clawdie.si (single site) into two domains during the
|
||||
Astro migration from clawdie-ai → colibri:
|
||||
|
||||
```
|
||||
docs.clawdie.si → guide (procedural: install, operate, reference)
|
||||
wiki.clawdie.si → wiki (decisions: architecture rationale, LLM-wiki)
|
||||
clawdie.si → landing (unchanged)
|
||||
```
|
||||
|
||||
## Why
|
||||
|
||||
- Wiki stays pure Karpathy LLM-wiki pattern — one decision per page, flat list
|
||||
- Guide stays procedural — structured sidebar with install/operate/architecture
|
||||
- Different audiences: wiki for agents/architects, guide for operators
|
||||
- ISO can toggle each surface independently (FEATURE_DOCS, FEATURE_WIKI)
|
||||
|
||||
## What needs building
|
||||
|
||||
| Layer | Task |
|
||||
|---|---|
|
||||
| DNS | `wiki.clawdie.si` A/AAAA → same host |
|
||||
| TLS | New Let's Encrypt cert (acme.sh auto-renew) |
|
||||
| Nginx | New vhost for wiki.clawdie.si |
|
||||
| Astro | Two Starlight configs from one colibri source tree |
|
||||
| Build | `build-docs.sh` → dist-guide/ + dist-wiki/ |
|
||||
| ISO | `FEATURE_DOCS` / `FEATURE_WIKI` toggle knobs |
|
||||
|
||||
## Two Starlight configs
|
||||
|
||||
```
|
||||
colibri/astro/
|
||||
guide.config.mjs → full sidebar: Install, Operate, Architecture...
|
||||
wiki.config.mjs → minimal sidebar: autogenerate flat article list
|
||||
```
|
||||
|
||||
Same toolchain, two configs, two output dirs. Wiki uses autogenerate — no
|
||||
manual sidebar to maintain as pages are added.
|
||||
|
||||
## Prerequisite
|
||||
|
||||
The Astro build pipeline must be migrated from clawdie-ai to colibri first.
|
||||
The content already lives in colibri (docs/guide/ + docs/wiki/). The build
|
||||
scripts and Astro config don't yet.
|
||||
29
scripts/build-wiki.sh
Executable file
29
scripts/build-wiki.sh
Executable file
|
|
@ -0,0 +1,29 @@
|
|||
#!/bin/sh
|
||||
# Build the Colibri wiki site — plain Astro, no Starlight.
|
||||
#
|
||||
# Prerequisites: Node.js + npm (node24 npm-node24 on FreeBSD).
|
||||
# cd astro/wiki && npm ci
|
||||
#
|
||||
# Usage:
|
||||
# ./scripts/build-wiki.sh # build to astro/wiki/dist/
|
||||
# ./scripts/build-wiki.sh --preview # dev server at localhost:4321
|
||||
#
|
||||
# Site URL override:
|
||||
# ASTRO_SITE_URL=https://wiki.clawdie.si ./scripts/build-wiki.sh
|
||||
|
||||
set -eu
|
||||
|
||||
SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
|
||||
REPO_ROOT=$(cd "$SCRIPT_DIR/.." && pwd)
|
||||
WIKI_DIR="$REPO_ROOT/astro/wiki"
|
||||
|
||||
cd "$WIKI_DIR"
|
||||
|
||||
if [ "${1:-}" = "--preview" ]; then
|
||||
echo "==> wiki dev server (http://localhost:4321)"
|
||||
npx astro dev --host 0.0.0.0
|
||||
else
|
||||
echo "==> building wiki ($WIKI_DIR)"
|
||||
npx astro build
|
||||
echo "==> wiki built → $WIKI_DIR/dist/"
|
||||
fi
|
||||
Loading…
Add table
Reference in a new issue