UNPKG

@git.zone/tsdoc

Version:

A comprehensive TypeScript documentation tool that leverages AI to generate and enhance project documentation, including dynamic README creation, API docs via TypeDoc, and smart commit message generation.

231 lines (186 loc) 8.02 kB
import type { AiDoc } from '../classes.aidoc.js'; import * as plugins from '../plugins.js'; import * as paths from '../paths.js'; import { ProjectContext } from './projectcontext.js'; import { logger } from '../logging.js'; export class Readme { // INSTANCE private aiDocsRef: AiDoc; private projectDir: string; constructor(aiDocsRef: AiDoc, projectDirArg: string) { this.aiDocsRef = aiDocsRef; this.projectDir = projectDirArg; } public async build() { let finalReadmeString = ``; // First check legal info before introducing any cost const projectContext = new ProjectContext(this.projectDir); const npmExtraJson = JSON.parse( (await projectContext.gatherFiles()).smartfilesNpmextraJSON.contents.toString() ); const legalInfo = npmExtraJson?.['@git.zone/tsdoc']?.legal; if (!legalInfo) { const error = new Error(`No legal information found in npmextra.json`); console.log(error); } // Use DualAgentOrchestrator with filesystem tool for agent-driven exploration const readmeOrchestrator = new plugins.smartagent.DualAgentOrchestrator({ smartAiInstance: this.aiDocsRef.smartAiInstance, defaultProvider: 'openai', maxIterations: 25, maxResultChars: 15000, // Limit tool output to prevent token explosion maxHistoryMessages: 20, // Limit history window logPrefix: '[README]', onProgress: (event) => logger.log(event.logLevel, event.logMessage), guardianPolicyPrompt: ` You validate README generation tool calls and outputs. APPROVE tool calls for: - Reading any files within the project directory (package.json, ts/*.ts, readme.md, etc.) - Using tree to see project structure - Using glob to find source files - Listing directory contents REJECT tool calls for: - Reading files outside the project directory - Writing, deleting, or modifying any files - Any destructive operations For final README output, APPROVE if: - README follows proper markdown format - Contains Install and Usage sections - Code examples are correct TypeScript/ESM syntax - Documentation is comprehensive and helpful REJECT final output if: - README is incomplete or poorly formatted - Contains licensing information (added separately) - Uses CommonJS syntax instead of ESM - Contains "in conclusion" or similar filler `, }); // Register scoped filesystem tool for agent exploration readmeOrchestrator.registerScopedFilesystemTool(this.projectDir); await readmeOrchestrator.start(); const readmeTaskPrompt = ` You create markdown READMEs for npm projects. You only output the markdown readme. PROJECT DIRECTORY: ${this.projectDir} Use the filesystem tool to explore the project and understand what it does: 1. First, use tree to see the project structure (maxDepth: 3) 2. Read package.json to understand the package name, description, and dependencies 3. Read the existing readme.md if it exists (use it as a base, improve and expand) 4. Read readme.hints.md if it exists (contains hints for documentation) 5. Read key source files in ts/ directory to understand the API and implementation 6. Focus on exported classes, interfaces, and functions Then generate a comprehensive README following this template: # Project Name [The name from package.json and description] ## Install [Short text on how to install the project] ## Usage [ Give code examples here. Construct sensible scenarios for the user. Make sure to show a complete set of features of the module. Don't omit use cases. ALWAYS USE ESM SYNTAX AND TYPESCRIPT. Write at least 4000 words. More if necessary. If there is already a readme, take the Usage section as base. Remove outdated content, expand and improve. Check for completeness. Don't include any licensing information. This will be added later. Avoid "in conclusion" statements. ] `; const readmeResult = await readmeOrchestrator.run(readmeTaskPrompt); await readmeOrchestrator.stop(); if (!readmeResult.success) { throw new Error(`README generation failed: ${readmeResult.status}`); } // Clean up markdown formatting if wrapped in code blocks let resultMessage = readmeResult.result .replace(/^```markdown\n?/i, '') .replace(/\n?```$/i, ''); finalReadmeString += resultMessage + '\n' + legalInfo; console.log(`\n======================\n`); console.log(resultMessage); console.log(`\n======================\n`); const readme = (await projectContext.gatherFiles()).smartfilesReadme; readme.contents = Buffer.from(finalReadmeString); await readme.write(); // lets care about monorepo aspects const tsPublishInstance = new plugins.tspublish.TsPublish(); const subModules = await tsPublishInstance.getModuleSubDirs(paths.cwd); logger.log('info', `Found ${Object.keys(subModules).length} sub modules`); for (const subModule of Object.keys(subModules)) { logger.log('info', `Building readme for ${subModule}`); const subModulePath = plugins.path.join(paths.cwd, subModule); const tspublishData = await plugins.fsInstance .file(plugins.path.join(subModulePath, 'tspublish.json')) .encoding('utf8') .read(); // Create a new orchestrator with filesystem tool for each submodule const subModuleOrchestrator = new plugins.smartagent.DualAgentOrchestrator({ smartAiInstance: this.aiDocsRef.smartAiInstance, defaultProvider: 'openai', maxIterations: 20, maxResultChars: 12000, maxHistoryMessages: 15, logPrefix: `[README:${subModule}]`, onProgress: (event) => logger.log(event.logLevel, event.logMessage), guardianPolicyPrompt: ` You validate README generation for submodules. APPROVE tool calls for: - Reading any files within the submodule directory - Using tree to see structure - Using glob to find source files REJECT tool calls for: - Reading files outside the submodule directory - Writing, deleting, or modifying any files - Any destructive operations APPROVE final README if comprehensive, well-formatted markdown with ESM TypeScript examples. REJECT incomplete READMEs or those with licensing info. `, }); // Register scoped filesystem tool for the submodule directory subModuleOrchestrator.registerScopedFilesystemTool(subModulePath); await subModuleOrchestrator.start(); const subModulePrompt = ` You create markdown READMEs for npm projects. You only output the markdown readme. SUB MODULE: ${subModule} SUB MODULE DIRECTORY: ${subModulePath} IMPORTANT: YOU ARE CREATING THE README FOR THIS SUB MODULE: ${subModule} The Sub Module will be published with: ${JSON.stringify(tspublishData, null, 2)} Use the filesystem tool to explore the submodule: 1. Use tree to see the submodule structure 2. Read package.json to understand the submodule 3. Read source files in ts/ directory to understand the implementation Generate a README following the template: # Project Name [name and description from package.json] ## Install [installation instructions] ## Usage [ Code examples with complete features. ESM TypeScript syntax only. Write at least 4000 words. No licensing information. No "in conclusion". ] Don't use \`\`\` at the beginning or end. Only for code blocks. `; const subModuleResult = await subModuleOrchestrator.run(subModulePrompt); await subModuleOrchestrator.stop(); if (subModuleResult.success) { const subModuleReadmeString = subModuleResult.result .replace(/^```markdown\n?/i, '') .replace(/\n?```$/i, '') + '\n' + legalInfo; await plugins.fsInstance .file(plugins.path.join(subModulePath, 'readme.md')) .encoding('utf8') .write(subModuleReadmeString); logger.log('success', `Built readme for ${subModule}`); } else { logger.log('error', `Failed to build readme for ${subModule}: ${subModuleResult.status}`); } } return resultMessage; } }