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:
- Reading and parsing multiple source files across languages
- Understanding function signatures, class hierarchies, and module relationships
- Generating natural language explanations of complex logic
- Producing structured output in Markdown, HTML, or other formats
- Running in non-interactive mode for automation and scripting
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:
- Consistency: Every function, class, and module gets documented using the same structure and tone.
- Timeliness: Running the generator as part of CI ensures docs reflect the latest code.
- Depth: Claude can infer intent and explain behavior that isn't obvious from signatures alone.
- Multi-format output: Generate Markdown for GitHub, HTML for static sites, or JSON for programmatic consumption.
- Reduced cognitive load: Developers focus on code; the generator handles the prose.
Prerequisites and Setup
Before building the generator, ensure you have the following:
- Node.js 18 or later installed on your system
- The Claude Code CLI installed and authenticated
- A project directory with source code you want to document
- Basic familiarity with shell scripting and JavaScript
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:
- Discovery: Scan the project directory and identify source files to document.
- Analysis: Parse each file and extract structural information (functions, classes, exports).
- Generation: Send extracted information to Claude Code to produce documentation text.
- Output: Assemble generated documentation into the target format and write to disk.
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