← Back to DevBytes

Building a Documentation Generator with Claude Code: Complete Guide

Introduction to Building a Documentation Generator with Claude Code

Documentation is often the most neglected part of the software development lifecycle. Developers write code, ship features, and move on—leaving READMEs outdated and API references stale. With Claude Code, Anthropic's command-line AI coding assistant, you can build a documentation generator that reads your codebase, understands its structure, and produces accurate, maintainable documentation automatically.

This tutorial walks you through building a complete documentation generator powered by Claude Code. You'll learn how to leverage Claude's ability to analyze source files, extract meaningful insights, and generate documentation in formats like Markdown, HTML, or JSON. By the end, you'll have a working tool you can integrate into your CI/CD pipeline.

What Is Claude Code?

Claude Code is a terminal-based AI assistant that can read, write, and understand code directly from your command line. Unlike browser-based chat interfaces, Claude Code operates within your project directory, giving it direct access to your files, git history, and project structure. This makes it uniquely suited for tasks like documentation generation, where context awareness is critical.

Key capabilities relevant to documentation generation include:

Why Build a Documentation Generator?

Manual documentation has several well-known problems. It drifts from the code as features evolve. It requires context-switching that developers resist. And it rarely captures the "why" behind implementation decisions—only the "what." An AI-powered documentation generator addresses these issues by deriving documentation directly from source code, ensuring it stays synchronized with the implementation.

Specific benefits include:

Prerequisites and Setup

Before building the generator, ensure you have the following:

Install Claude Code globally if you haven't already:

npm install -g @anthropic-ai/claude-code

Verify the installation and authenticate:

claude --version
claude auth login

Once authenticated, you can invoke Claude Code from any directory. The tool reads files in your current working directory and can execute commands, create files, and modify existing ones based on your instructions.

Architecture of the Documentation Generator

Our documentation generator will follow a pipeline architecture with four stages:

We'll implement this as a Node.js script that orchestrates Claude Code in non-interactive mode. The script will be configurable, allowing you to specify input directories, output format, and documentation style.

Step 1: Project Structure

Create a new directory for your documentation generator and initialize the project:

mkdir doc-generator
cd doc-generator
npm init -y

Create the following directory structure:

doc-generator/
├── package.json
├── src/
│   ├── index.js
│   ├── discover.js
│   ├── analyze.js
│   ├── generate.js
│   └── output.js
├── templates/
│   ├── markdown.md
│   └── html.html
└── output/

The src/ directory contains our generator's modules. The templates/ directory holds output templates. The output/ directory is where generated documentation will be written.

Step 2: File Discovery Module

The discovery module scans a target directory and returns a list of source files to document. We'll use Node's built-in fs and path modules to keep dependencies minimal.

Create src/discover.js:

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

const SUPPORTED_EXTENSIONS = [
  '.js', '.ts', '.jsx', '.tsx',
  '.py', '.java', '.go', '.rb',
  '.php', '.cs', '.rs', '.c', '.cpp'
];

const IGNORE_DIRS = [
  'node_modules', '.git', 'dist', 'build',
  'coverage', '__pycache__', '.next', 'vendor'
];

function discoverFiles(rootDir) {
  const results = [];

  function walk(dir) {
    const entries = fs.readdirSync(dir, { withFileTypes: true });

    for (const entry of entries) {
      const fullPath = path.join(dir, entry.name);

      if (entry.isDirectory()) {
        if (!IGNORE_DIRS.includes(entry.name)) {
          walk(fullPath);
        }
      } else if (entry.isFile()) {
        const ext = path.extname(entry.name);
        if (SUPPORTED_EXTENSIONS.includes(ext)) {
          results.push(fullPath);
        }
      }
    }
  }

  walk(rootDir);
  return results;
}

module.exports = { discoverFiles };

This module recursively walks the target directory, skipping common dependency and build folders, and collects all source files with recognized extensions. You can extend SUPPORTED_EXTENSIONS and IGNORE_DIRS to match your project's needs.

Step 3: File Analysis Module

The analysis module reads each discovered file and extracts structural metadata. Rather than building a full parser for every language, we'll use lightweight regular expressions to identify functions, classes, and exports. This metadata will be passed to Claude Code along with the file contents so it can generate accurate documentation.

Create src/analyze.js:

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

function analyzeFile(filePath) {
  const content = fs.readFileSync(filePath, 'utf-8');
  const ext = path.extname(filePath);
  const relativePath = path.relative(process.cwd(), filePath);

  const analysis = {
    path: relativePath,
    extension: ext,
    lineCount: content.split('\n').length,
    functions: [],
    classes: [],
    exports: [],
    content: content
  };

  // Detect function declarations (JS/TS)
  const funcRegex = /(?:export\s+)?(?:async\s+)?function\s+(\w+)/g;
  let match;
  while ((match = funcRegex.exec(content)) !== null) {
    analysis.functions.push(match[1]);
  }

  // Detect arrow functions assigned to variables
  const arrowRegex = /(?:const|let|var)\s+(\w+)\s*=\s*(?:async\s*)?\([^)]*\)\s*=>/g;
  while ((match = arrowRegex.exec(content)) !== null) {
    analysis.functions.push(match[1]);
  }

  // Detect class declarations
  const classRegex = /(?:export\s+)?class\s+(\w+)/g;
  while ((match = classRegex.exec(content)) !== null) {
    analysis.classes.push(match[1]);
  }

  // Detect exports
  const exportRegex = /export\s+(?:default\s+)?(?:const|let|var|function|class)\s+(\w+)/g;
  while ((match = exportRegex.exec(content)) !== null) {
    analysis.exports.push(match[1]);
  }

  return analysis;
}

function analyzeFiles(filePaths) {
  return filePaths.map(analyzeFile);
}

module.exports = { analyzeFile, analyzeFiles };

This module produces a structured object for each file containing its path, detected functions, classes, exports, and full content. The content is included because Claude Code needs to read the actual implementation to generate meaningful documentation—not just a list of names.

Step 4: Documentation Generation with Claude Code

This is the core module. It sends each file's analysis to Claude Code in non-interactive mode and receives generated documentation. We'll use Claude Code's --print flag, which runs a prompt and returns the response without entering an interactive session.

Create src/generate.js:

const { execSync } = require('child_process');
const path = require('path');

function buildPrompt(analysis, options = {}) {
  const style = options.style || 'concise';
  const format = options.format || 'markdown';

  const structureInfo = [
    `File: ${analysis.path}`,
    `Language: ${analysis.extension}`,
    `Lines: ${analysis.lineCount}`,
    analysis.functions.length > 0
      ? `Functions: ${analysis.functions.join(', ')}`
      : 'Functions: none detected',
    analysis.classes.length > 0
      ? `Classes: ${analysis.classes.join(', ')}`
      : 'Classes: none detected',
    analysis.exports.length > 0
      ? `Exports: ${analysis.exports.join(', ')}`
      : 'Exports: none detected'
  ].join('\n');

  return `You are a technical documentation generator. Analyze the following source file and generate comprehensive documentation in ${format} format.

Use a ${style} writing style. Include:
1. A brief module overview explaining the file's purpose
2. Documentation for each function, class, and export
3. Parameter descriptions, return types, and usage examples where applicable
4. Notes on important implementation details or edge cases

Do NOT include the original source code in your output. Only produce documentation.

--- FILE STRUCTURE ---
${structureInfo}

--- SOURCE CODE ---
${analysis.content}

--- END ---

Generate the documentation now:`;
}

function generateDocs(analysis, options = {}) {
  const prompt = buildPrompt(analysis, options);

  // Write prompt to a temp file to avoid shell escaping issues
  const tempFile = path.join(__dirname, '..', '.temp-prompt.txt');
  const fs = require('fs');
  fs.writeFileSync(tempFile, prompt);

  try {
    const command = `claude --print "$(cat ${tempFile})" --max-turns 3`;
    const result = execSync(command, {
      encoding: 'utf-8',
      maxBuffer: 10 * 1024 * 1024,
      timeout: 120000
    });
    return result.trim();
  } finally {
    fs.unlinkSync(tempFile);
  }
}

function generateAllDocs(analyses, options = {}) {
  const results = [];
  for (const analysis of analyses) {
    console.log(`Generating docs for ${analysis.path}...`);
    try {
      const docs = generateDocs(analysis, options);
      results.push({ path: analysis.path, docs, error: null });
    } catch (err) {
      console.error(`Failed to generate docs for ${analysis.path}: ${err.message}`);
      results.push({ path: analysis.path, docs: null, error: err.message });
    }
  }
  return results;
}

module.exports = { generateDocs, generateAllDocs, buildPrompt };

The buildPrompt function constructs a detailed instruction for Claude Code, including the file's structural metadata and full source content. The generateDocs function executes Claude Code in print mode and captures the output. We write the prompt to a temporary file to avoid shell escaping problems with large code blocks.

Understanding the Claude Code Command

The --print flag tells Claude Code to run non-interactively: it processes the prompt, returns the result as stdout, and exits. The --max-turns flag limits how many internal reasoning steps Claude takes, which helps control execution time and cost. For documentation generation, 3 turns is usually sufficient—Claude reads the code, reasons about it, and produces the output.

Step 5: Output Assembly Module

The output module takes generated documentation and writes it to disk in the requested format. We'll support Markdown (one file per source file) and a combined HTML format.

Create src/output.js:

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

function ensureDir(dir) {
  if (!fs.existsSync(dir)) {
    fs.mkdirSync(dir, { recursive: true });
  }
}

function writeMarkdown(results, outputDir) {
  ensureDir(outputDir);

  for (const result of results) {
    if (result.error || !result.docs) continue;

    const baseName = result.path
      .replace(/\//g, '_')
      .replace(/\.[^.]+$/, '');

    const outputFile = path.join(outputDir, `${baseName}.md`);
    const header = `\n\n\n`;
    
    fs.writeFileSync(outputFile, header + result.docs, 'utf-8');
    console.log(`Written: ${outputFile}`);
  }

  // Generate an index file
  const indexPath = path.join(outputDir, 'INDEX.md');
  let indexContent = '# Documentation Index\n\n';
  indexContent += 'This documentation was auto-generated from source code.\n\n';
  indexContent += '## Files\n\n';

  for (const result of results) {
    if (result.error) {
      indexContent += `- ~~${result.path}~~ (generation failed)\n`;
    } else {
      const link = result.path.replace(/\//g, '_').replace(/\.[^.]+$/, '') + '.md';
      indexContent += `- [${result.path}](${link})\n`;
    }
  }

  fs.writeFileSync(indexPath, indexContent, 'utf-8');
  console.log(`Written: ${indexPath}`);
}

function writeHtml(results, outputDir) {
  ensureDir(outputDir);

  let html = `


  
  Project Documentation
  

  

Project Documentation

Auto-generated from source code.

`; for (const result of results) { html += `
\n`; html += `

${result.path}

\n`; if (result.error) { html += `

Documentation generation failed: ${result.error}

\n`; } else { // Convert basic markdown to HTML (simplified) const htmlContent = result.docs .replace(/^### (.*$)/gm, '

$1

') .replace(/^## (.*$)/gm, '

$1

') .replace(/^# (.*$)/gm, '

$1

') .replace(/\*\*(.*?)\*\*/g, '$1') .replace(/`([^`]+)`/g, '$1') .replace(/\n\n/g, '

') .replace(/^- (.*$)/gm, '

  • $1
  • '); html += `

    ${htmlContent}

    \n`; } html += `
    \n`; } html += `

    The Markdown writer creates one file per source file plus an index page that links them together. The HTML writer produces a single-page document with all files combined. Both include auto-generation notices so developers know not to edit the output manually.

    Step 6: Main Entry Point

    Now we tie everything together in the main entry point. This script accepts command-line arguments for the target directory, output format, and documentation style.

    Create src/index.js:

    #!/usr/bin/env node
    
    const path = require('path');
    const { discoverFiles } = require('./discover');
    const { analyzeFiles } = require('./analyze');
    const { generateAllDocs } = require('./generate');
    const { writeMarkdown, writeHtml } = require('./output');
    
    function parseArgs() {
      const args = process.argv.slice(2);
      const config = {
        targetDir: '.',
        outputDir: './output',
        format: 'markdown',
        style: 'concise'
      };
    
      for (let i = 0; i < args.length; i++) {
        switch (args[i]) {
          case '--target':
          case '-t':
            config.targetDir = args[++i];
            break;
          case '--output':
          case '-o':
            config.outputDir = args[++i];
            break;
          case '--format':
          case '-f':
            config.format = args[++i];
            break;
          case '--style':
          case '-s':
            config.style = args[++i];
            break;
          case '--help':
          case '-h':
            console.log(`
    Documentation Generator powered by Claude Code
    
    Usage: node src/index.js [options]
    
    Options:
      -t, --target        Source directory to document (default: current dir)
      -o, --output        Output directory (default: ./output)
      -f, --format     Output format: markdown or html (default: markdown)
      -s, --style