Skip to content

Commit 51eb68a

Browse files
BunsDevclaude
andcommitted
docs: scaffold Vite + Tailwind one-page docs site
Ports BunsDev/comux's docs architecture to coven-code: Vite 6 + Tailwind 4 + @cloudflare/vite-plugin, single-page layout with a floating sidebar and IntersectionObserver scroll-spy, hero with animated logo / install command / GitHub stars badge, content registry of per-section render() modules, and a Cloudflare Worker that proxies /api/stars with edge caching. Accent palette swapped from comux purple (#8b5cf6) to coven pink (#E91E63) family across the theme tokens, hero glow, code-block border, and syntax highlighting. Initial content modules port six of the existing docs/*.md pages (introduction, getting-started, welcome-screen, configuration, providers, familiars); the remaining .md files can be added by dropping new modules into src/content/ and registering them in src/content/index.js. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent c1368c9 commit 51eb68a

21 files changed

Lines changed: 1809 additions & 0 deletions

docs/package.json

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"name": "coven-code-docs",
3+
"version": "1.0.0",
4+
"private": true,
5+
"type": "module",
6+
"scripts": {
7+
"dev": "vite dev",
8+
"build": "vite build && rm -rf client && cp -R src/dist/client client",
9+
"preview": "vite build && vite preview",
10+
"deploy": "vite build && wrangler deploy -c wrangler.json"
11+
},
12+
"devDependencies": {
13+
"@cloudflare/vite-plugin": "1.25.1",
14+
"@tailwindcss/vite": "4.1.18",
15+
"tailwindcss": "4.1.18",
16+
"vite": "6.4.2",
17+
"wrangler": "4.66.0"
18+
}
19+
}

docs/public/coven.svg

Lines changed: 10 additions & 0 deletions
Loading

docs/public/favicon.svg

Lines changed: 11 additions & 0 deletions
Loading

docs/public/og.svg

Lines changed: 17 additions & 0 deletions
Loading

docs/shared/githubStars.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
export const GITHUB_STARS_API = 'https://api.github.com/repos/OpenCoven/coven-code';
2+
export const GITHUB_STARS_ACCEPT = 'application/vnd.github.v3+json';
3+
export const STARS_CACHE_TTL_SECONDS = 60;
4+
export const STARS_STALE_REVALIDATE_SECONDS = STARS_CACHE_TTL_SECONDS * 5;
5+
6+
export function parseGithubStarCount(data) {
7+
const count = data?.stargazers_count;
8+
return typeof count === 'number' ? count : null;
9+
}
10+
11+
export async function fetchGithubStarCount(fetchImpl = fetch, userAgent = 'coven-code-docs') {
12+
const headers = { Accept: GITHUB_STARS_ACCEPT };
13+
if (userAgent) headers['User-Agent'] = userAgent;
14+
15+
const response = await fetchImpl(GITHUB_STARS_API, { headers });
16+
if (!response.ok) return null;
17+
18+
return parseGithubStarCount(await response.json());
19+
}

docs/src/code-highlight.js

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
/**
2+
* Lightweight syntax highlighting and copy-to-clipboard for code blocks.
3+
* Uses token placeholders to prevent regex passes from corrupting each other.
4+
*/
5+
6+
const KEYWORDS = /\b(const|let|var|function|return|if|else|for|while|class|import|export|from|default|async|await|new|this|try|catch|throw|typeof|interface|type|extends|implements|enum|readonly|true|false|null|undefined|void|string|number|boolean|fn|pub|mut|impl|trait|struct|enum|match|use|mod|crate|self|Self|where|move|ref|box|as)\b/g;
7+
const STRINGS = /(["'`])(?:(?=(\\?))\2.)*?\1/g;
8+
const COMMENTS = /(\/\/.*$|\/\*[\s\S]*?\*\/)/gm;
9+
const NUMBERS = /\b(\d+\.?\d*)\b/g;
10+
const FUNCTIONS = /\b([a-zA-Z_]\w*)\s*(?=\()/g;
11+
const SHELL_COMMENT = /^(\s*#.*)$/gm;
12+
const SHELL_CMD = /^(\s*)(npm|npx|pnpm|yarn|curl|git|cd|mkdir|cat|echo|tmux|comux|coven-code|coven|cargo|rustup|ls|rm|cp|mv|sudo|brew|apt|pip|node|deno|bun|chmod|export)\b/gm;
13+
const SHELL_FLAG = /\s(--?[\w-]+)/g;
14+
const JSON_KEY = /("[\w-]+")\s*:/g;
15+
16+
function escapeHtml(str) {
17+
return str.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
18+
}
19+
20+
function tok(text, regex, cls, tokens) {
21+
return text.replace(regex, (match) => {
22+
const i = tokens.length;
23+
tokens.push(`<span class="${cls}">${match}</span>`);
24+
return `\x00${i}\x00`;
25+
});
26+
}
27+
28+
function restore(text, tokens) {
29+
return text.replace(/\x00(\d+)\x00/g, (_, i) => tokens[i]);
30+
}
31+
32+
function highlightJS(code) {
33+
const tokens = [];
34+
let r = escapeHtml(code);
35+
r = tok(r, COMMENTS, 'hl-comment', tokens);
36+
r = tok(r, STRINGS, 'hl-string', tokens);
37+
r = tok(r, KEYWORDS, 'hl-keyword', tokens);
38+
r = tok(r, NUMBERS, 'hl-number', tokens);
39+
r = tok(r, FUNCTIONS, 'hl-fn', tokens);
40+
return restore(r, tokens);
41+
}
42+
43+
function highlightShell(code) {
44+
const tokens = [];
45+
let r = escapeHtml(code);
46+
r = tok(r, SHELL_COMMENT, 'hl-comment', tokens);
47+
r = tok(r, STRINGS, 'hl-string', tokens);
48+
r = r.replace(SHELL_CMD, (match, ws, cmd) => {
49+
const i = tokens.length;
50+
tokens.push(`<span class="hl-keyword">${cmd}</span>`);
51+
return `${ws}\x00${i}\x00`;
52+
});
53+
r = r.replace(SHELL_FLAG, (match, flag) => {
54+
const i = tokens.length;
55+
tokens.push(`<span class="hl-fn">${flag}</span>`);
56+
return ` \x00${i}\x00`;
57+
});
58+
return restore(r, tokens);
59+
}
60+
61+
function highlightJSON(code) {
62+
const tokens = [];
63+
let r = escapeHtml(code);
64+
r = tok(r, STRINGS, 'hl-string', tokens);
65+
r = r.replace(JSON_KEY, (match, key) => {
66+
const i = tokens.length;
67+
tokens.push(`<span class="hl-fn">${key}</span>`);
68+
return `\x00${i}\x00:`;
69+
});
70+
r = tok(r, NUMBERS, 'hl-number', tokens);
71+
r = tok(r, /\b(true|false|null)\b/g, 'hl-keyword', tokens);
72+
return restore(r, tokens);
73+
}
74+
75+
export function highlight(code, lang) {
76+
if (!lang) lang = '';
77+
lang = lang.toLowerCase().trim();
78+
if (['bash', 'sh', 'shell', 'zsh'].includes(lang)) return highlightShell(code);
79+
if (['json', 'jsonc'].includes(lang)) return highlightJSON(code);
80+
if (['js', 'javascript', 'ts', 'typescript', 'jsx', 'tsx', 'rs', 'rust'].includes(lang)) return highlightJS(code);
81+
return escapeHtml(code);
82+
}
83+
84+
export function processCodeBlocks(container) {
85+
container.querySelectorAll('pre code').forEach((block) => {
86+
const lang = block.dataset.lang || '';
87+
const raw = block.textContent;
88+
block.innerHTML = highlight(raw, lang);
89+
90+
const pre = block.parentElement;
91+
if (pre && !pre.querySelector('.code-copy')) {
92+
const btn = document.createElement('button');
93+
btn.className = 'code-copy';
94+
btn.title = 'Copy to clipboard';
95+
btn.innerHTML = `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>`;
96+
btn.addEventListener('click', () => {
97+
navigator.clipboard.writeText(raw).then(() => {
98+
btn.innerHTML = `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="20 6 9 17 4 12"/></svg>`;
99+
setTimeout(() => {
100+
btn.innerHTML = `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>`;
101+
}, 2000);
102+
});
103+
});
104+
pre.style.position = 'relative';
105+
pre.appendChild(btn);
106+
}
107+
108+
if (lang && !pre.querySelector('.code-lang')) {
109+
const label = document.createElement('span');
110+
label.className = 'code-lang';
111+
label.textContent = lang;
112+
pre.appendChild(label);
113+
}
114+
});
115+
}

docs/src/content/configuration.js

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
export const meta = { title: 'Configuration' };
2+
3+
export function render() {
4+
return `
5+
<h1>Configuration</h1>
6+
<p class="lead">Coven Code is configured through a layered system of JSON files, environment variables, and command-line flags. Project settings override global settings; CLI flags override both.</p>
7+
8+
<h2>File locations</h2>
9+
10+
<p>The global settings file lives at:</p>
11+
12+
<pre><code data-lang="bash">~/.coven-code/settings.json</code></pre>
13+
14+
<p>The directory is created automatically on first run. Files are standard JSON (or JSONC — comments are stripped before parsing).</p>
15+
16+
<h3>Per-project settings</h3>
17+
18+
<p>Coven Code walks up from the current working directory looking for a project-level settings file. The first file found wins:</p>
19+
20+
<pre><code data-lang="bash">&lt;project-root&gt;/.coven-code/settings.json
21+
&lt;project-root&gt;/.coven-code/settings.jsonc</code></pre>
22+
23+
<p>Keys present in the project file override the global value; keys absent fall back to global.</p>
24+
25+
<h2>Top-level structure</h2>
26+
27+
<pre><code data-lang="json">{
28+
"version": 1,
29+
"provider": "anthropic",
30+
"config": { },
31+
"providers": { },
32+
"projects": { },
33+
"commands": { },
34+
"formatter": { },
35+
"agents": { },
36+
"skills": { },
37+
"permissionRules": [],
38+
"enabledPlugins": [],
39+
"disabledPlugins": [],
40+
"hasCompletedOnboarding": false
41+
}</code></pre>
42+
43+
<p>Most day-to-day options live inside the <code>config</code> object. Provider credentials live in the <code>providers</code> map.</p>
44+
45+
<h2>Common <code>config</code> options</h2>
46+
47+
<h3>Model and tokens</h3>
48+
<table>
49+
<thead><tr><th>Key</th><th>Default</th><th>Description</th></tr></thead>
50+
<tbody>
51+
<tr><td><code>api_key</code></td><td><code>null</code></td><td>Anthropic API key. Overrides <code>ANTHROPIC_API_KEY</code>. Prefer the env var in shared environments.</td></tr>
52+
<tr><td><code>model</code></td><td>provider default</td><td>Model ID. Falls back to the provider's default (e.g. <code>claude-sonnet-4-6</code>).</td></tr>
53+
<tr><td><code>max_tokens</code></td><td><code>8192</code></td><td>Maximum tokens per model response.</td></tr>
54+
<tr><td><code>provider</code></td><td><code>"anthropic"</code></td><td>Active provider.</td></tr>
55+
</tbody>
56+
</table>
57+
58+
<h3>Permission mode</h3>
59+
<table>
60+
<thead><tr><th>Mode</th><th>Behavior</th></tr></thead>
61+
<tbody>
62+
<tr><td><code>default</code></td><td>Prompt for any tool that touches your filesystem or shell.</td></tr>
63+
<tr><td><code>acceptEdits</code></td><td>Auto-approve file edits; still prompt for shell.</td></tr>
64+
<tr><td><code>bypassPermissions</code></td><td>No prompts. Use only in trusted, sandboxed contexts.</td></tr>
65+
<tr><td><code>plan</code></td><td>Read-only mode. Model can read and search but cannot write or run commands.</td></tr>
66+
</tbody>
67+
</table>
68+
69+
<h3>Familiar</h3>
70+
<p>Set <code>"familiar"</code> to the id of your active familiar (e.g. <code>"kitty"</code>, <code>"raven"</code>). This drives the welcome-screen portrait and the <code>/agents</code> overlay when the daemon is online.</p>
71+
72+
<pre><code data-lang="json">{
73+
"familiar": "raven",
74+
"config": {
75+
"model": "claude-opus-4-7",
76+
"permission_mode": "default",
77+
"auto_compact": true,
78+
"compact_threshold": 0.8
79+
}
80+
}</code></pre>
81+
82+
<h2>Environment variables</h2>
83+
84+
<table>
85+
<thead><tr><th>Variable</th><th>Purpose</th></tr></thead>
86+
<tbody>
87+
<tr><td><code>ANTHROPIC_API_KEY</code></td><td>API key for Anthropic provider.</td></tr>
88+
<tr><td><code>OPENAI_API_KEY</code></td><td>API key for OpenAI provider.</td></tr>
89+
<tr><td><code>COVEN_CODE_HOME</code></td><td>Override the default <code>~/.coven-code/</code> directory.</td></tr>
90+
</tbody>
91+
</table>
92+
93+
<p>See <a href="https://github.com/OpenCoven/coven-code/blob/main/docs/configuration.md" target="_blank" rel="noopener">the full configuration reference</a> for every option, including <code>agents</code>, <code>commands</code>, <code>permissionRules</code>, and per-provider keys.</p>
94+
`;
95+
}

docs/src/content/familiars.js

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
export const meta = { title: 'Familiars' };
2+
3+
export function render() {
4+
return `
5+
<h1>Coven familiars</h1>
6+
<p class="lead">Coven Code integrates natively with the Coven daemon's familiar roster. When the daemon is installed and running, every familiar you have configured under <code>~/.coven/</code> is automatically available inside Coven Code as a selectable agent persona — no extra setup required.</p>
7+
8+
<h2>What is a familiar?</h2>
9+
10+
<p>A familiar is a named AI persona defined in the Coven ecosystem. Each familiar has an identity (display name, emoji, pronouns), a role description, and optional metadata used to shape how the model presents itself and reasons about tasks. Familiars are user-defined and live in <code>~/.coven/familiars.toml</code>, managed by the Coven daemon.</p>
11+
12+
<p>For example, a minimal Coven setup might have:</p>
13+
14+
<table>
15+
<thead><tr><th>ID</th><th>Name</th><th>Role</th></tr></thead>
16+
<tbody>
17+
<tr><td><code>dev</code></td><td>Dev 🤖</td><td>Code-first implementation agent</td></tr>
18+
<tr><td><code>research</code></td><td>Research 🧙</td><td>Research and reasoning</td></tr>
19+
<tr><td><code>writer</code></td><td>Writer ✍️</td><td>Writing and communication</td></tr>
20+
</tbody>
21+
</table>
22+
23+
<p>You define your own familiars — the names, roles, and roster are entirely yours.</p>
24+
25+
<h2>How familiars appear</h2>
26+
27+
<p>When the daemon is present, <code>load_agent_definitions()</code> reads <code>~/.coven/familiars.toml</code> and converts each familiar into an <code>AgentDefinition</code> with:</p>
28+
29+
<ul>
30+
<li><strong>source:</strong> <code>coven:familiar:&lt;id&gt;</code> — distinguishes them from user-defined agents</li>
31+
<li><strong>instructions:</strong> a synthesised system-prompt body that captures the familiar's name, role, and description</li>
32+
<li><strong>memory_scope:</strong> <code>workspace</code> — familiars have full workspace context by default</li>
33+
<li><strong>model:</strong> inherits the session default (no override unless the user sets one)</li>
34+
</ul>
35+
36+
<p>Familiars are appended <strong>after</strong> workspace agents in the list. If a user-defined agent shares the same display name as a familiar, the user definition wins.</p>
37+
38+
<h2>Where familiars show up</h2>
39+
40+
<ol>
41+
<li>The <strong>welcome panel</strong> (top-left of the home screen): glyph, name, access tier dot, and on wider terminals the role and an accent rule. See <a href="#welcome-screen">Welcome Screen</a>.</li>
42+
<li>The <strong>F2 switcher popup</strong>: one row per familiar, each painted in that familiar's accent palette with a coloured tier dot.</li>
43+
<li>The <strong><code>/agents</code> detail view</strong>: the card appears above the persona preview when you select a familiar-sourced agent.</li>
44+
</ol>
45+
46+
<h2>Switching familiars</h2>
47+
48+
<p>From the TUI, press <kbd>F2</kbd> to open the switcher, or use the slash command:</p>
49+
50+
<pre><code data-lang="bash">/familiar raven
51+
/familiar list</code></pre>
52+
53+
<p>From the CLI:</p>
54+
55+
<pre><code data-lang="bash">coven-code agents list
56+
coven-code agents use raven</code></pre>
57+
58+
<p>Or set it persistently in <code>~/.coven-code/settings.json</code>:</p>
59+
60+
<pre><code data-lang="json">{
61+
"familiar": "raven"
62+
}</code></pre>
63+
64+
<h2>Without the daemon</h2>
65+
66+
<p>Coven Code works fully standalone. Without the daemon, familiars degrade gracefully:</p>
67+
68+
<ul>
69+
<li>The welcome panel shows a built-in glyph (default: <code>kitty</code>).</li>
70+
<li>The <code>/agents</code> overlay shows only workspace agents.</li>
71+
<li><kbd>F2</kbd> opens a switcher with the bundled built-in familiars.</li>
72+
</ul>
73+
74+
<p>Install the daemon to unlock the full roster:</p>
75+
76+
<pre><code data-lang="bash">npm install -g @opencoven/coven</code></pre>
77+
78+
<p>See <a href="https://github.com/OpenCoven/coven-code/blob/main/docs/familiars.md" target="_blank" rel="noopener">the full familiars reference</a> for access tiers, persona authoring, and CLI integration.</p>
79+
`;
80+
}

0 commit comments

Comments
 (0)