ctx-gen
Version:
AI-Enhanced Documentation Generator for Code Understanding
203 lines • 8.12 kB
JavaScript
import fs from 'fs-extra';
import path from 'path';
import { glob } from 'glob';
import { writeFileWithDir } from '../utils/fileUtils.js';
/**
* Generate machine-readable documentation for AI consumption
* @param options - CtxGen options
*/
export async function generateMachineReadable(options) {
// Get all markdown files
const moduleDocs = await glob('**/*.md', {
cwd: path.join(options.docsDir, 'modules'),
ignore: ['index.md']
});
// Convert to machine-readable formats
await generateAnalysisJson(options, moduleDocs);
}
/**
* Generate an analysis JSON file with structured information about the codebase
* @param options - CtxGen options
* @param moduleDocs - Array of module documentation file paths
*/
async function generateAnalysisJson(options, moduleDocs) {
// Create analysis object with basic structure
const analysis = {
projectInfo: {},
modules: {},
functions: {},
classes: {},
dependencies: {}
};
// Get project metadata if it exists
try {
const metadataPath = path.join(options.docsDir, 'metadata.json');
if (await fs.pathExists(metadataPath)) {
analysis.projectInfo = await fs.readJSON(metadataPath);
}
}
catch (error) {
console.error('Error reading metadata:', error);
}
// Process each module document
for (const doc of moduleDocs) {
try {
// Read the markdown content
const content = await fs.readFile(path.join(options.docsDir, 'modules', doc), 'utf-8');
// Extract module information
const moduleName = path.basename(doc, '.md');
const moduleInfo = extractModuleInfo(content, moduleName);
// Add to analysis
analysis.modules[moduleName] = moduleInfo.module;
// Add functions
Object.entries(moduleInfo.functions).forEach(([funcName, funcInfo]) => {
analysis.functions[`${moduleName}.${funcName}`] = {
module: moduleName,
name: funcInfo.name,
description: funcInfo.description,
parameters: funcInfo.parameters,
returnType: funcInfo.returnType
};
});
// Add classes
Object.entries(moduleInfo.classes).forEach(([className, classInfo]) => {
analysis.classes[`${moduleName}.${className}`] = {
module: moduleName,
name: classInfo.name,
description: classInfo.description,
methods: classInfo.methods,
properties: classInfo.properties
};
});
// Add dependencies
if (moduleInfo.dependencies.length > 0) {
analysis.dependencies[moduleName] = moduleInfo.dependencies;
}
}
catch (error) {
console.error(`Error processing ${doc} for analysis:`, error);
}
}
// Write analysis to file
const analysisPath = path.join(options.docsDir, 'analysis.json');
await writeFileWithDir(analysisPath, JSON.stringify(analysis, null, 2));
}
/**
* Extract module information from markdown content
* @param content - Markdown content
* @param moduleName - Module name
* @returns Extracted module information
*/
function extractModuleInfo(content, moduleName) {
// Initialize result object
const result = {
module: {
name: moduleName,
description: '',
filePath: '',
language: ''
},
functions: {},
classes: {},
dependencies: []
};
// Extract file path and language
const filePathMatch = content.match(/\*\*File Path:\*\* `([^`]+)`/);
if (filePathMatch) {
result.module.filePath = filePathMatch[1];
}
const languageMatch = content.match(/\*\*Language:\*\* ([^\n]+)/);
if (languageMatch) {
result.module.language = languageMatch[1];
}
// Extract module description
const overviewMatch = content.match(/## Overview\s*\n\n([^\n]+)/);
if (overviewMatch) {
result.module.description = overviewMatch[1];
}
// Extract enhanced description if available
const aiMatch = content.match(/## AI-Enhanced Analysis\s*\n\n([\s\S]+?)(?=\n##|$)/);
if (aiMatch) {
// Extract the first paragraph of AI analysis
const firstParagraph = aiMatch[1].split('\n\n')[0];
if (firstParagraph && firstParagraph.length > result.module.description.length) {
result.module.description = firstParagraph;
}
// Try to extract functions
const functionMatch = aiMatch[1].match(/(?:## |###) Functions\/Methods\s*\n\n([\s\S]+?)(?=\n##|$)/);
if (functionMatch) {
// Simple function extraction using bullet points/headers
const functionSection = functionMatch[1];
// Extract function names with descriptions
const functionRegex = /[*-] \*\*`([^`]+)`\*\*: ([^\n]+)/g;
let match;
while ((match = functionRegex.exec(functionSection)) !== null) {
const functionName = match[1];
const description = match[2];
result.functions[functionName] = {
name: functionName,
description: description,
parameters: [],
returnType: ''
};
}
// Also try alternative function format with headers
const headerFunctionRegex = /### `([^`]+)`\s*\n\n([^\n]+)/g;
while ((match = headerFunctionRegex.exec(functionSection)) !== null) {
const functionName = match[1];
const description = match[2];
result.functions[functionName] = {
name: functionName,
description: description,
parameters: [],
returnType: ''
};
}
}
// Try to extract classes
const classMatch = aiMatch[1].match(/(?:## |###) Classes\/Objects\s*\n\n([\s\S]+?)(?=\n##|$)/);
if (classMatch) {
// Simple class extraction using bullet points/headers
const classSection = classMatch[1];
// Extract class names with descriptions
const classRegex = /[*-] \*\*`([^`]+)`\*\*: ([^\n]+)/g;
let match;
while ((match = classRegex.exec(classSection)) !== null) {
const className = match[1];
const description = match[2];
result.classes[className] = {
name: className,
description: description,
methods: [],
properties: []
};
}
// Also try alternative class format with headers
const headerClassRegex = /### `([^`]+)`\s*\n\n([^\n]+)/g;
while ((match = headerClassRegex.exec(classSection)) !== null) {
const className = match[1];
const description = match[2];
result.classes[className] = {
name: className,
description: description,
methods: [],
properties: []
};
}
}
// Try to extract dependencies
const dependencyMatch = aiMatch[1].match(/(?:## |###) Dependencies\s*\n\n([\s\S]+?)(?=\n##|$)/);
if (dependencyMatch) {
// Simple dependency extraction using bullet points
const dependencySection = dependencyMatch[1];
// Extract dependency names
const dependencyRegex = /[*-] \*\*`([^`]+)`\*\*:/g;
let match;
while ((match = dependencyRegex.exec(dependencySection)) !== null) {
result.dependencies.push(match[1]);
}
}
}
return result;
}
//# sourceMappingURL=machineReadableGenerator.js.map