From 10d99b17e2629742e70ae3402ec61074f128224c Mon Sep 17 00:00:00 2001 From: forcetrainer Date: Sat, 3 Jan 2026 17:30:56 -0500 Subject: [PATCH] feat(docs): add Getting Started tutorial and simplify build script MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add comprehensive Getting Started tutorial with installation as Step 1 - Simplify build-docs.js to read directly from docs/ (no consolidation) - Remove backup/restore dance that could corrupt docs folder on build failure - Remove ~150 lines of unused consolidation code πŸ€– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- docs/tutorials/getting-started/index.md | 343 ++++++++++++++++++++++++ tools/build-docs.js | 254 ++---------------- 2 files changed, 364 insertions(+), 233 deletions(-) create mode 100644 docs/tutorials/getting-started/index.md diff --git a/docs/tutorials/getting-started/index.md b/docs/tutorials/getting-started/index.md new file mode 100644 index 00000000..c8d850be --- /dev/null +++ b/docs/tutorials/getting-started/index.md @@ -0,0 +1,343 @@ +--- +sidebar_label: Getting Started +description: Install BMAD and create your first planning document +--- + +# Getting Started with BMAD + +Learn how to build software with BMAD's AI-powered workflows. By the end of this tutorial, you'll have installed BMAD, initialized a project, and created your first planning document. + +## What You'll Learn + +- How to install and configure BMAD for your IDE +- How BMAD organizes work into phases and agents +- How to initialize a project and choose a planning track +- How to create your first requirements document + +## Prerequisites + +Before starting, ensure you have: + +- **Node.js 20+** β€” Required for the installer +- **Git** β€” Recommended for version control +- **AI-powered IDE** β€” Claude Code, Cursor, Windsurf, or similar +- **A project idea** β€” Even a simple one works for learning + +--- + +## Step 1: Install BMAD + +Open a terminal in your project directory and run: + +```bash +npx bmad-method install +``` + +The interactive installer guides you through setup: + +### 1.1 Choose Installation Location + +Select where to install BMAD files: + +- **Current directory** β€” Recommended for new projects +- **Subdirectory** β€” If you want BMAD isolated +- **Custom path** β€” For specific project structures + +### 1.2 Select Your AI Tool + +Choose the IDE you'll be using: + +- Claude Code +- Cursor +- Windsurf +- Other + +The installer configures BMAD to work with your selected tool. + +### 1.3 Choose Modules + +For this tutorial, select **BMM** (BMAD Method) β€” the core module for software development. You can add other modules later: + +| Module | Purpose | +| -------- | ----------------------------------------- | +| **BMM** | Core methodology for software development | +| **BMGD** | Game development workflows | +| **CIS** | Creative intelligence and facilitation | +| **BMB** | Building custom agents and workflows | + +### 1.4 Accept Default Configuration + +For your first project, accept the recommended defaults. You can customize settings later in `_bmad/[module]/config.yaml`. + +### 1.5 Verify Installation + +After installation completes, verify by checking your project structure: + +``` +your-project/ +β”œβ”€β”€ _bmad/ +β”‚ β”œβ”€β”€ bmm/ # Method module +β”‚ β”‚ β”œβ”€β”€ agents/ # Agent files +β”‚ β”‚ β”œβ”€β”€ workflows/ # Workflow files +β”‚ β”‚ └── config.yaml # Module config +β”‚ └── core/ # Core utilities +β”œβ”€β”€ _bmad-output/ # Generated artifacts (created later) +└── .claude/ # IDE configuration (if using Claude Code) +``` + +> **Having trouble?** See [Install BMAD](../../how-to/installation/install-bmad.md) for troubleshooting common issues. + +--- + +## Step 2: Understand How BMAD Works + +Before diving in, let's understand BMAD's core concepts. + +### Phases + +BMAD organizes work into four phases: + +``` +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ Phase 1 β”‚ β”‚ Phase 2 β”‚ β”‚ Phase 3 β”‚ β”‚ Phase 4 β”‚ +β”‚ Analysis β”‚ β†’ β”‚ Planning β”‚ β†’ β”‚ Solutioning β”‚ β†’ β”‚Implementationβ”‚ +β”‚ (Optional) β”‚ β”‚ (Required) β”‚ β”‚ (Varies) β”‚ β”‚ (Required) β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + Brainstorm Requirements Architecture Build code + Research PRD or tech-spec Design decisions Story by story +``` + +### Agents + +Agents are specialized AI personas, each expert in their domain: + +- **Analyst** β€” Initializes projects, tracks progress, conducts research +- **PM** β€” Creates requirements (PRD or tech-spec) +- **UX-Designer** β€” Designs user interfaces and experiences +- **Architect** β€” Makes technical decisions, designs system architecture +- **SM (Scrum Master)** β€” Manages sprints, creates stories +- **DEV** β€” Implements code, reviews work + +### Workflows + +Workflows are guided processes that agents run. You tell an agent to run a workflow, and it walks you through the process interactively. + +### Planning Tracks + +Based on your project's complexity, BMAD offers three tracks: + +| Track | Best For | Documents Created | +| --------------- | ------------------------------------------ | ------------------------------ | +| **Quick Flow** | Bug fixes, simple features, clear scope | Tech-spec only | +| **BMAD Method** | Products, platforms, complex features | PRD + Architecture + UX | +| **Enterprise** | Compliance, multi-tenant, enterprise needs | PRD + Architecture + Security + DevOps | + +--- + +## Step 3: Initialize Your Project + +Now let's set up your project with BMAD. + +### 3.1 Load the Analyst Agent + +In your IDE, load the Analyst agent. The method depends on your IDE: + +- **Claude Code**: Type `/analyst` or load the agent file directly +- **Cursor/Windsurf**: Open the agent file from `_bmad/bmm/agents/` + +Wait for the agent's menu to appear. You'll see a list of available workflows. + +### 3.2 Run the Initialization Workflow + +Tell the agent to initialize your project: + +``` +Run workflow-init +``` + +Or use the shorthand: + +``` +*workflow-init +``` + +### 3.3 Describe Your Project + +The workflow asks you to describe: + +- **Your project and goals** β€” What are you building? What problem does it solve? +- **Existing codebase** β€” Is this a new project (greenfield) or existing code (brownfield)? +- **Size and complexity** β€” Roughly how big is this? (You can adjust later) + +### 3.4 Choose Your Track + +Based on your description, the workflow suggests a planning track. You can accept the suggestion or choose a different one: + +- Choose **Quick Flow** if you have a clear, bounded task +- Choose **BMAD Method** for most new products or features +- Choose **Enterprise** if you have compliance or security requirements + +For this tutorial, we'll assume you chose **BMAD Method**. + +### 3.5 Confirm and Create + +Once you confirm, the workflow creates `bmm-workflow-status.yaml` in your project's docs folder. This file tracks your progress through all phases. + +> **Important**: Always start a fresh chat for each workflow. This prevents context limitations from causing issues. + +--- + +## Step 4: Create Your Requirements Document + +With your project initialized, it's time to create your first planning document β€” the PRD (Product Requirements Document). + +### 4.1 Start a Fresh Chat + +Close your current chat and start a new one. This ensures the agent has full context capacity for the workflow. + +### 4.2 Load the PM Agent + +Load the PM (Product Manager) agent in your IDE. + +### 4.3 Run the PRD Workflow + +Tell the PM agent: + +``` +Run prd +``` + +Or use shortcuts: + +- `*prd` +- Select "create-prd" from the menu +- Say "Let's create a new PRD" + +### 4.4 Work Through the PRD + +The PM agent guides you through creating your PRD interactively: + +1. **Project overview** β€” Refine your project description +2. **Goals and success metrics** β€” What does success look like? +3. **User personas** β€” Who uses this product? +4. **Functional requirements** β€” What must the system do? +5. **Non-functional requirements** β€” Performance, security, scalability needs + +Answer the agent's questions thoughtfully. The PRD becomes the foundation for everything that follows. + +### 4.5 Review Your PRD + +When complete, you'll have a `PRD.md` file in your `_bmad-output/` folder. Review it to ensure it captures your vision. + +--- + +## Step 5: Check Your Progress + +At any point, you can check what to do next. + +### 5.1 Load Any Agent + +Start a fresh chat and load any BMAD agent. + +### 5.2 Ask for Status + +Tell the agent: + +``` +workflow-status +``` + +The agent reads your `bmm-workflow-status.yaml` and tells you: + +- Which phase you're in +- What workflows are complete +- What the next recommended or required step is + +Example response: + +``` +Phase 2 (Planning) complete: + βœ“ PRD created + +Next recommended steps: + - UX Design (optional, if your project has a UI) + - Architecture (required for BMAD Method track) + Agent: architect + Command: create-architecture +``` + +--- + +## What You've Accomplished + +You've completed the foundation of a BMAD project: + +- Installed BMAD and configured it for your IDE +- Initialized a project with your chosen planning track +- Created a PRD that defines your product requirements + +Your project now has: + +``` +your-project/ +β”œβ”€β”€ _bmad/ # BMAD configuration +β”œβ”€β”€ _bmad-output/ +β”‚ β”œβ”€β”€ PRD.md # Your requirements document +β”‚ └── bmm-workflow-status.yaml # Progress tracking +└── ... +``` + +--- + +## Next Steps + +Continue building your project by designing your system's technical foundation (required for BMAD Method) and then starting implementation story by story. + +Explore related topics: + +- **[What Are Agents?](../../explanation/core-concepts/what-are-agents.md)** β€” Deep dive into how agents work +- **[What Are Workflows?](../../explanation/core-concepts/what-are-workflows.md)** β€” Understanding BMAD's workflow system +- **[Workflow Reference](../../reference/workflows/index.md)** β€” Complete list of available workflows + +--- + +## Quick Reference + +Commands you learned in this tutorial: + +| Command | Agent | Purpose | +| ---------------- | ------- | ---------------------------------- | +| `*workflow-init` | Analyst | Initialize a new project | +| `*prd` | PM | Create a Product Requirements Document | +| `workflow-status`| Any | Check progress and next steps | + +> **Tip**: Agents are flexible with commands. Menu numbers, shortcuts (`*prd`), or natural language ("Let's create a PRD") all work. + +--- + +## Common Questions + +**Q: Do I need to create a PRD for every project?** + +Only for BMAD Method and Enterprise tracks. Quick Flow projects use a simpler tech-spec instead. + +**Q: Can I skip Phase 1 (Analysis)?** + +Yes, Phase 1 is optional. If you already know what you're building, start with Phase 2 (Planning). + +**Q: What if I want to brainstorm first?** + +Load the Analyst agent and run `*brainstorm-project` before `workflow-init`. + +**Q: Why start fresh chats for each workflow?** + +Workflows are context-intensive. Reusing chats can cause the AI to hallucinate or lose track of details. Fresh chats ensure maximum context capacity. + +--- + +## Getting Help + +- **During workflows**: Agents guide you with questions and explanations +- **Check status**: Run `workflow-status` with any agent +- **Community**: [Discord](https://discord.gg/gk8jAdXWmj) β€” #general-dev, #bugs-issues +- **Video tutorials**: [BMad Code YouTube](https://www.youtube.com/@BMadCode) diff --git a/tools/build-docs.js b/tools/build-docs.js index 86d9c7d7..987de0ac 100644 --- a/tools/build-docs.js +++ b/tools/build-docs.js @@ -28,12 +28,6 @@ const REPO_URL = 'https://github.com/bmad-code-org/BMAD-METHOD'; const LLM_MAX_CHARS = 600_000; const LLM_WARN_CHARS = 500_000; -const MODULES = ['bmm', 'bmb', 'bmgd', 'cis']; - -// No root docs copied - only docs/ folder content goes to site -// README.md, CHANGELOG.md etc. link to GitHub -const ROOT_DOCS = []; - const LLM_EXCLUDE_PATTERNS = ['changelog', 'ide-info/', 'v4-to-v6-upgrade', 'downloads/', 'faq']; // ============================================================================= @@ -50,11 +44,11 @@ async function main() { cleanBuildDirectory(); - const consolidatedDir = consolidateDocs(); - const artifactsDir = await generateArtifacts(consolidatedDir); - const siteDir = buildDocusaurusSite(artifactsDir); + const docsDir = path.join(PROJECT_ROOT, 'docs'); + const artifactsDir = await generateArtifacts(docsDir); + const siteDir = buildDocusaurusSite(); - printBuildSummary(consolidatedDir, artifactsDir, siteDir); + printBuildSummary(docsDir, artifactsDir, siteDir); } main().catch((error) => { @@ -66,31 +60,15 @@ main().catch((error) => { // Pipeline Stages // ============================================================================= -function consolidateDocs() { - printHeader('Consolidating documentation sources'); - - const outputDir = path.join(BUILD_DIR, 'consolidated'); - fs.mkdirSync(outputDir, { recursive: true }); - - copyMainDocs(outputDir); - copyRootDocs(outputDir); - copyModuleDocs(outputDir); - - const mdCount = countMarkdownFiles(outputDir); - console.log(); - console.log(` \u001B[32mβœ“\u001B[0m Consolidation complete: ${mdCount} markdown files`); - - return outputDir; -} - -async function generateArtifacts(consolidatedDir) { +async function generateArtifacts(docsDir) { printHeader('Generating LLM files and download bundles'); const outputDir = path.join(BUILD_DIR, 'artifacts'); - copyDirectory(consolidatedDir, outputDir); + fs.mkdirSync(outputDir, { recursive: true }); + // Generate LLM files reading from docs/, output to artifacts/ generateLlmsTxt(outputDir); - generateLlmsFullTxt(outputDir); + generateLlmsFullTxt(docsDir, outputDir); await generateDownloadBundles(outputDir); console.log(); @@ -99,21 +77,14 @@ async function generateArtifacts(consolidatedDir) { return outputDir; } -function buildDocusaurusSite(artifactsDir) { +function buildDocusaurusSite() { printHeader('Building Docusaurus site'); const siteDir = path.join(BUILD_DIR, 'site'); - const mainDocs = path.join(PROJECT_ROOT, 'docs'); - const docsBackup = path.join(BUILD_DIR, 'docs-backup'); - - backupAndReplaceDocs(mainDocs, docsBackup, artifactsDir); - - try { - runDocusaurusBuild(siteDir); - } finally { - restoreDocs(mainDocs, docsBackup); - } + const artifactsDir = path.join(BUILD_DIR, 'artifacts'); + // Build directly from docs/ - no backup/restore needed + runDocusaurusBuild(siteDir); copyArtifactsToSite(artifactsDir, siteDir); console.log(); @@ -122,56 +93,6 @@ function buildDocusaurusSite(artifactsDir) { return siteDir; } -// ============================================================================= -// Documentation Consolidation -// ============================================================================= - -function copyMainDocs(destDir) { - console.log(' β†’ Copying main docs...'); - const docsDir = path.join(PROJECT_ROOT, 'docs'); - // Include modules folder - docs now live in docs/modules/ instead of src/modules/*/docs/ - copyDirectory(docsDir, destDir, ['llms.txt', 'llms-full.txt'], true); -} - -function copyRootDocs(destDir) { - console.log(' β†’ Copying root documentation files...'); - - for (const doc of ROOT_DOCS) { - const srcPath = path.join(PROJECT_ROOT, doc.src); - const destPath = path.join(destDir, doc.dest); - - if (fs.existsSync(srcPath)) { - let content = fs.readFileSync(srcPath, 'utf-8'); - - if (!content.startsWith('---')) { - content = `---\ntitle: "${doc.title}"\n---\n\n${content}`; - } - - content = transformMarkdownLinks(content); - fs.writeFileSync(destPath, content); - console.log(` ${doc.src} β†’ ${doc.dest}`); - } - } -} - -function copyModuleDocs(destDir) { - fs.mkdirSync(path.join(destDir, 'modules'), { recursive: true }); - - for (const moduleName of MODULES) { - const srcPath = path.join(PROJECT_ROOT, 'src', 'modules', moduleName, 'docs'); - const moduleDest = path.join(destDir, 'modules', moduleName); - - if (fs.existsSync(srcPath)) { - console.log(` β†’ Copying ${moduleName} docs...`); - copyDirectory(srcPath, moduleDest, [], false, moduleName); - const count = countMarkdownFiles(moduleDest); - console.log(` ${count} markdown files`); - } else { - console.log(` ⚠ WARNING: ${moduleName} docs not found`); - } - } -} - // ============================================================================= // LLM File Generation // ============================================================================= @@ -220,11 +141,11 @@ function generateLlmsTxt(outputDir) { console.log(` Generated llms.txt (${content.length.toLocaleString()} chars)`); } -function generateLlmsFullTxt(outputDir) { +function generateLlmsFullTxt(docsDir, outputDir) { console.log(' β†’ Generating llms-full.txt...'); const date = new Date().toISOString().split('T')[0]; - const files = getAllMarkdownFiles(outputDir); + const files = getAllMarkdownFiles(docsDir); const output = [ '# BMAD Method Documentation (Full)', @@ -244,7 +165,7 @@ function generateLlmsFullTxt(outputDir) { continue; } - const fullPath = path.join(outputDir, mdPath); + const fullPath = path.join(docsDir, mdPath); try { const content = readMarkdownContent(fullPath); output.push(``, content, '', ''); @@ -352,18 +273,6 @@ async function generatePromptsBundle(downloadsDir) { // Docusaurus Build // ============================================================================= -function backupAndReplaceDocs(mainDocs, backupDir, artifactsDir) { - console.log(' β†’ Preparing docs for Docusaurus...'); - - if (fs.existsSync(mainDocs)) { - copyDirectory(mainDocs, backupDir); - fs.rmSync(mainDocs, { recursive: true }); - } - - copyDirectory(artifactsDir, mainDocs, ['llms.txt', 'llms-full.txt']); - removeZipFiles(path.join(mainDocs, 'downloads')); -} - function runDocusaurusBuild(siteDir) { console.log(' β†’ Running docusaurus build...'); execSync('npx docusaurus build --config website/docusaurus.config.js --out-dir ' + siteDir, { @@ -372,16 +281,6 @@ function runDocusaurusBuild(siteDir) { }); } -function restoreDocs(mainDocs, backupDir) { - console.log(' β†’ Restoring original docs...'); - fs.rmSync(mainDocs, { recursive: true }); - - if (fs.existsSync(backupDir)) { - copyDirectory(backupDir, mainDocs); - fs.rmSync(backupDir, { recursive: true }); - } -} - function copyArtifactsToSite(artifactsDir, siteDir) { console.log(' β†’ Copying artifacts to site...'); @@ -394,28 +293,18 @@ function copyArtifactsToSite(artifactsDir, siteDir) { } } -function removeZipFiles(dir) { - if (!fs.existsSync(dir)) return; - - for (const file of fs.readdirSync(dir)) { - if (file.endsWith('.zip')) { - fs.unlinkSync(path.join(dir, file)); - } - } -} - // ============================================================================= // Build Summary // ============================================================================= -function printBuildSummary(consolidatedDir, artifactsDir, siteDir) { +function printBuildSummary(docsDir, artifactsDir, siteDir) { console.log(); printBanner('Build Complete!'); console.log(); console.log('Build artifacts:'); - console.log(` Consolidated docs: ${consolidatedDir}`); - console.log(` Generated files: ${artifactsDir}`); - console.log(` Final site: ${siteDir}`); + console.log(` Source docs: ${docsDir}`); + console.log(` Generated files: ${artifactsDir}`); + console.log(` Final site: ${siteDir}`); console.log(); console.log(`Deployable output: ${siteDir}/`); console.log(); @@ -461,7 +350,7 @@ function cleanBuildDirectory() { fs.mkdirSync(BUILD_DIR, { recursive: true }); } -function copyDirectory(src, dest, exclude = [], transformMd = false, moduleName = null) { +function copyDirectory(src, dest, exclude = []) { if (!fs.existsSync(src)) return false; fs.mkdirSync(dest, { recursive: true }); @@ -472,12 +361,7 @@ function copyDirectory(src, dest, exclude = [], transformMd = false, moduleName const destPath = path.join(dest, entry.name); if (entry.isDirectory()) { - copyDirectory(srcPath, destPath, exclude, transformMd, moduleName); - } else if (entry.name.endsWith('.md')) { - // Always transform markdown links, use module context if provided - let content = fs.readFileSync(srcPath, 'utf-8'); - content = transformMarkdownLinks(content, moduleName); - fs.writeFileSync(destPath, content); + copyDirectory(srcPath, destPath, exclude); } else { fs.copyFileSync(srcPath, destPath); } @@ -485,102 +369,6 @@ function copyDirectory(src, dest, exclude = [], transformMd = false, moduleName return true; } -function transformMarkdownLinks(content, moduleName = null) { - // Transform HTML img src attributes for module docs images - content = content.replaceAll(/src="\.\/src\/modules\/([^/]+)\/docs\/images\/([^"]+)"/g, (match, mod, file) => { - return `src="./modules/${mod}/images/${file}"`; - }); - - return content.replaceAll(/\]\(([^)]+)\)/g, (match, url) => { - // src/modules/{mod}/docs/{path}.md β†’ ./modules/{mod}/{path}.md - // Keeps .md - Docusaurus handles .md β†’ page conversion - const docsMatch = url.match(/^\.\.?\/src\/modules\/([^/]+)\/docs\/(.+\.md)$/); - if (docsMatch) return `](./modules/${docsMatch[1]}/${docsMatch[2]})`; - - // src/modules/{mod}/docs/ β†’ ./modules/{mod}/ - const docsDirMatch = url.match(/^\.\.?\/src\/modules\/([^/]+)\/docs\/$/); - if (docsDirMatch) return `](./modules/${docsDirMatch[1]}/)`; - - // src/modules/{mod}/docs/images/{file} β†’ ./modules/{mod}/images/{file} - const docsImageMatch = url.match(/^\.\.?\/src\/modules\/([^/]+)\/docs\/images\/(.+)$/); - if (docsImageMatch) return `](./modules/${docsImageMatch[1]}/images/${docsImageMatch[2]})`; - - // src/modules/{mod}/README.md β†’ GitHub (not in docs folder) - const readmeMatch = url.match(/^\.\.?\/src\/modules\/([^/]+)\/README\.md$/i); - if (readmeMatch) return `](${REPO_URL}/blob/main/src/modules/${readmeMatch[1]}/README.md)`; - - // src/modules/* (non-docs) β†’ GitHub - const srcMatch = url.match(/^\.\.?\/src\/modules\/(.+)$/); - if (srcMatch) return `](${REPO_URL}/tree/main/src/modules/${srcMatch[1]})`; - - // Relative paths escaping docs/ folder β†’ GitHub (when module context is known) - // e.g., ../workflows/foo/bar.md from within docs/ β†’ src/modules/{mod}/workflows/foo/bar.md - if (moduleName) { - const relativeEscapeMatch = url.match(/^\.\.\/([^.][^)]+)$/); - if (relativeEscapeMatch && !relativeEscapeMatch[1].startsWith('src/')) { - const relativePath = relativeEscapeMatch[1]; - return `](${REPO_URL}/blob/main/src/modules/${moduleName}/${relativePath})`; - } - } - - // ./docs/{path}.md β†’ ./{path}.md (docs folder contents are at root in build) - // Keeps .md - Docusaurus handles .md β†’ page conversion - const rootDocsMatch = url.match(/^\.\/docs\/(.+\.md)$/); - if (rootDocsMatch) return `](./${rootDocsMatch[1]})`; - - // Root docs β†’ GitHub (not part of docs site) - if (url === '../README.md' || url === './README.md' || url === './project-readme') { - return `](${REPO_URL}/blob/main/README.md)`; - } - if (url === '../CHANGELOG.md' || url === './CHANGELOG.md' || url === './changelog') { - return `](${REPO_URL}/blob/main/CHANGELOG.md)`; - } - - // Root files β†’ GitHub (CONTRIBUTING, LICENSE, CODE_OF_CONDUCT, etc.) - const contributingMatch = url.match(/^(\.\.\/)?CONTRIBUTING\.md(#.*)?$/); - if (contributingMatch) { - const anchor = contributingMatch[2] || ''; - return `](${REPO_URL}/blob/main/CONTRIBUTING.md${anchor})`; - } - if (url === 'LICENSE' || url === '../LICENSE') { - return `](${REPO_URL}/blob/main/LICENSE)`; - } - if (url === '.github/CODE_OF_CONDUCT.md' || url === '../.github/CODE_OF_CONDUCT.md') { - return `](${REPO_URL}/blob/main/.github/CODE_OF_CONDUCT.md)`; - } - - // Other root .md files β†’ GitHub - const rootFileMatch = url.match(/^\.\.\/([A-Z][^/]+\.md)$/); - if (rootFileMatch) return `](${REPO_URL}/blob/main/${rootFileMatch[1]})`; - - // Cross-module doc links: ../../{mod}/docs/{path}.md β†’ ../{mod}/{path}.md - // Fixes path structure but keeps .md (Docusaurus handles .md β†’ page conversion) - const crossModuleDocsMatch = url.match(/^\.\.\/\.\.\/([^/]+)\/docs\/(.+\.md)$/); - if (crossModuleDocsMatch) return `](../${crossModuleDocsMatch[1]}/${crossModuleDocsMatch[2]})`; - - // Root-level folders (samples/) β†’ GitHub - const rootFolderMatch = url.match(/^\.\.\/((samples)\/.*)/); - if (rootFolderMatch) return `](${REPO_URL}/blob/main/${rootFolderMatch[1]})`; - - return match; - }); -} - -function countMarkdownFiles(dir) { - let count = 0; - if (!fs.existsSync(dir)) return 0; - - for (const entry of fs.readdirSync(dir, { withFileTypes: true })) { - const fullPath = path.join(dir, entry.name); - if (entry.isDirectory()) { - count += countMarkdownFiles(fullPath); - } else if (entry.name.endsWith('.md')) { - count++; - } - } - return count; -} - function createZipArchive(sourceDir, outputPath, exclude = []) { return new Promise((resolve, reject) => { const output = fs.createWriteStream(outputPath);