Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
908 changes: 908 additions & 0 deletions docs/SEO_MARKETING_PLAN.md

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions docs/docs/intro.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ keywords: [ralph wiggum, autonomous coding, AI coding loops, claude code]

> **Ralph Wiggum made easy.** One command to run autonomous AI coding loops.

import DownloadDocs from '@site/src/components/DownloadDocs'

<DownloadDocs />

## What is Ralph Wiggum?

Ralph Wiggum is a technique for running AI coding agents in autonomous loops until tasks are completed. Instead of prompting back and forth, you give the AI a task and let it iterate until done.
Expand Down
3 changes: 2 additions & 1 deletion docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
"scripts": {
"docusaurus": "docusaurus",
"start": "docusaurus start",
"build": "docusaurus build",
"build": "node scripts/generate-full-docs.cjs && docusaurus build",
"docs:full": "node scripts/generate-full-docs.cjs",
"seo:check": "node scripts/check-seo-aeo.cjs",
"seo:sync": "node scripts/sync-seo-aeo-assets.cjs",
"swizzle": "docusaurus swizzle",
Expand Down
162 changes: 162 additions & 0 deletions docs/scripts/generate-full-docs.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
/**
* Generates a single markdown file containing all ralph-starter documentation.
* Output: docs/static/docs.md
*
* Usage: node scripts/generate-full-docs.cjs
*/

const fs = require('fs');
const path = require('path');

const DOCS_DIR = path.join(__dirname, '..', 'docs');
const OUTPUT_FILE = path.join(__dirname, '..', 'static', 'docs.md');

// Ordered sections matching the sidebar structure
const SECTIONS = [
{ heading: 'Getting Started', files: ['intro.md', 'installation.md'] },
{
heading: 'CLI Commands',
dir: 'cli',
files: [
'run.md',
'auto.md',
'init.md',
'plan.md',
'setup.md',
'check.md',
'config.md',
'auth.md',
'integrations.md',
'source.md',
'presets.md',
'skill.md',
'template.md',
],
},
{
heading: 'Sources & Integrations',
dir: 'sources',
files: ['overview.md', 'github.md', 'linear.md', 'notion.md', 'figma.md'],
},
{
heading: 'Guides',
dir: 'guides',
files: [
'workflow-presets.md',
'prd-workflow.md',
'cost-tracking.md',
'skills-system.md',
'testing-integrations.md',
'extending-ralph-starter.md',
],
},
{
heading: 'Wizard',
dir: 'wizard',
files: ['overview.md', 'idea-mode.md'],
},
{
heading: 'Advanced',
dir: 'advanced',
files: [
'validation.md',
'git-automation.md',
'circuit-breaker.md',
'rate-limiting.md',
'ralph-playbook.md',
],
},
{
heading: 'MCP Server',
dir: 'mcp',
files: ['setup.md', 'claude-desktop.md'],
},
{
heading: 'Community',
dir: 'community',
files: ['contributing.md', 'changelog.md', 'ideas.md'],
},
{ heading: 'FAQ', files: ['faq.md'] },
];

function stripFrontmatter(content) {
if (content.startsWith('---')) {
const end = content.indexOf('---', 3);
if (end !== -1) {
return content.slice(end + 3).trimStart();
}
}
return content;
}

function bumpHeadings(content) {
// Bump all headings down by one level (# -> ##, ## -> ###, etc.)
// so section headings stay as the top-level within each section.
// Skip lines inside fenced code blocks to avoid mutating snippets
// (e.g. shell comments like "# Install" should not become "## Install").
const lines = content.split('\n');
let inFence = false;

for (let i = 0; i < lines.length; i++) {
if (/^```/.test(lines[i])) {
inFence = !inFence;
continue;
}
if (!inFence) {
lines[i] = lines[i].replace(/^(#{1,5}) /, (_, hashes) => hashes + '# ');
}
}

return lines.join('\n');
}

function generate() {
const parts = [];

parts.push('# ralph-starter Documentation\n');
parts.push(
'> AI-powered autonomous coding tool. Connect GitHub, Linear, Notion, Figma and run AI coding loops from specs to production.\n'
);
parts.push(
`> Generated on ${new Date().toISOString().split('T')[0]} | [ralphstarter.ai](https://ralphstarter.ai)\n`
);

// Table of contents
parts.push('## Table of Contents\n');
for (const section of SECTIONS) {
parts.push(`- [${section.heading}](#${section.heading.toLowerCase().replace(/[^a-z0-9]+/g, '-')})`);
}
parts.push('');

parts.push('---\n');

for (const section of SECTIONS) {
parts.push(`## ${section.heading}\n`);

for (const file of section.files) {
const filePath = section.dir
? path.join(DOCS_DIR, section.dir, file)
: path.join(DOCS_DIR, file);

if (!fs.existsSync(filePath)) {
console.warn(`Warning: ${filePath} not found, skipping`);
continue;
}

const raw = fs.readFileSync(filePath, 'utf-8');
const content = bumpHeadings(stripFrontmatter(raw));
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Strip MDX-only directives from generated docs.md

The concatenation path currently applies only stripFrontmatter and bumpHeadings, then writes content directly, so MDX-only syntax from source pages (for example docs/docs/intro.md now has import DownloadDocs... and <DownloadDocs />) is copied into docs/static/docs.md. This makes the “single markdown file” artifact include non-Markdown implementation details that can confuse downstream markdown parsers and LLM/document ingestion pipelines expecting plain docs content.

Useful? React with 👍 / 👎.

parts.push(content);
parts.push('\n---\n');
}
}

const output = parts.join('\n');
fs.writeFileSync(OUTPUT_FILE, output, 'utf-8');

const lines = output.split('\n').length;
const sizeKb = (Buffer.byteLength(output, 'utf-8') / 1024).toFixed(1);
console.log(`Generated ${OUTPUT_FILE}`);
console.log(` ${lines} lines, ${sizeKb} KB`);
}

generate();
38 changes: 38 additions & 0 deletions docs/src/components/DownloadDocs/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import type { ReactNode } from 'react';
import styles from './styles.module.css';

export default function DownloadDocs(): ReactNode {
return (
<div className={styles.wrapper}>
<div className={styles.card}>
<div className={styles.icon}>
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" />
<polyline points="7 10 12 15 17 10" />
<line x1="12" y1="15" x2="12" y2="3" />
</svg>
</div>
<div className={styles.content}>
<span className={styles.title}>Download full docs</span>
<span className={styles.subtitle}>All 39 pages in a single file</span>
</div>
<div className={styles.buttons}>
<a
href="/docs.md"
download="ralph-starter-docs.md"
className={styles.button}
>
.md
</a>
<a
href="/llms-full.txt"
download="ralph-starter-docs.txt"
className={styles.button}
>
.txt
</a>
</div>
</div>
</div>
);
}
86 changes: 86 additions & 0 deletions docs/src/components/DownloadDocs/styles.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
.wrapper {
margin: 1.5rem 0;
}

.card {
display: flex;
align-items: center;
gap: 0.75rem;
padding: 0.75rem 1rem;
background: var(--quantum-surface);
border: 1px solid var(--quantum-tungsten);
border-radius: var(--radius-md);
transition: border-color 0.2s ease;
}

.card:hover {
border-color: var(--quantum-steel);
}

.icon {
display: flex;
align-items: center;
justify-content: center;
color: var(--quantum-silver);
flex-shrink: 0;
}

.content {
display: flex;
flex-direction: column;
flex: 1;
min-width: 0;
}

.title {
font-family: var(--font-body);
font-size: 0.875rem;
font-weight: 600;
color: var(--quantum-white);
line-height: 1.3;
}

.subtitle {
font-family: var(--font-body);
font-size: 0.75rem;
color: var(--quantum-silver);
line-height: 1.3;
}

.buttons {
display: flex;
gap: 0.5rem;
flex-shrink: 0;
}

.button {
font-family: var(--font-mono);
font-size: 0.75rem;
font-weight: 600;
padding: 0.375rem 0.75rem;
border: 1px solid var(--quantum-tungsten);
border-radius: var(--radius-sm);
background: transparent;
color: var(--quantum-light);
text-decoration: none;
transition: all 0.2s ease;
white-space: nowrap;
}

.button:hover {
border-color: var(--quantum-mist);
background: rgba(152, 152, 159, 0.08);
color: var(--quantum-white);
text-decoration: none;
}

@media screen and (max-width: 500px) {
.card {
flex-wrap: wrap;
}

.buttons {
width: 100%;
justify-content: flex-end;
}
}
Loading