Files
tsdoc/ts/aidocs_classes
2025-12-15 14:34:02 +00:00
..
2025-12-15 14:34:02 +00:00
2025-12-15 14:34:02 +00:00
2024-06-22 21:21:52 +02:00
2025-12-15 14:34:02 +00:00
2025-12-15 14:34:02 +00:00

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 = ``;

    // lets first check legal before introducung 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);
    }

    // Gather project context upfront to avoid token explosion from filesystem tool
    const contextString = await projectContext.convertFilesToContext([
      (await projectContext.gatherFiles()).smartfilePackageJSON,
      (await projectContext.gatherFiles()).smartfilesReadme,
      (await projectContext.gatherFiles()).smartfilesReadmeHints,
      (await projectContext.gatherFiles()).smartfilesNpmextraJSON,
      ...(await projectContext.gatherFiles()).smartfilesMod,
    ]);

    // Use DualAgentOrchestrator for readme generation
    const readmeOrchestrator = new plugins.smartagent.DualAgentOrchestrator({
      smartAiInstance: this.aiDocsRef.smartAiInstance,
      defaultProvider: 'openai',
      guardianPolicyPrompt: `
You validate README generation.

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 if:
- README is incomplete or poorly formatted
- Contains licensing information (added separately)
- Uses CommonJS syntax instead of ESM
- Contains "in conclusion" or similar filler
`,
    });

    await readmeOrchestrator.start();

    const readmeTaskPrompt = `
You create markdown READMEs for npm projects. You only output the markdown readme.

Analyze the project files provided below to understand the codebase, then generate a comprehensive README.

The README should follow 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.
]

Here are the project files:

${contextString}

Generate the README based on these files.
`;

    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 tspublishData = await plugins.fsInstance
        .file(plugins.path.join(paths.cwd, subModule, 'tspublish.json'))
        .encoding('utf8')
        .read();

      // Gather submodule context
      const subModuleContext = new ProjectContext(plugins.path.join(paths.cwd, subModule));
      let subModuleContextString = '';
      try {
        const subModuleFiles = await subModuleContext.gatherFiles();
        subModuleContextString = await subModuleContext.convertFilesToContext([
          subModuleFiles.smartfilePackageJSON,
          subModuleFiles.smartfilesNpmextraJSON,
          ...subModuleFiles.smartfilesMod,
        ]);
      } catch (e) {
        // Submodule may not have all files, continue with what we have
        logger.log('warn', `Could not gather full context for ${subModule}`);
      }

      // Create a new orchestrator for each submodule
      const subModuleOrchestrator = new plugins.smartagent.DualAgentOrchestrator({
        smartAiInstance: this.aiDocsRef.smartAiInstance,
        defaultProvider: 'openai',
        guardianPolicyPrompt: `
You validate README generation for submodules.

APPROVE comprehensive, well-formatted markdown with ESM TypeScript examples.
REJECT incomplete READMEs or those with licensing info.
`,
      });

      await subModuleOrchestrator.start();

      const subModulePrompt = `
You create markdown READMEs for npm projects. You only output the markdown readme.
SUB MODULE: ${subModule}

IMPORTANT: YOU ARE CREATING THE README FOR THIS SUB MODULE: ${subModule}
The Sub Module will be published with:
${JSON.stringify(tspublishData, null, 2)}

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.

Here are the submodule files:

${subModuleContextString}

Generate the README based on these files.
`;

      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(paths.cwd, subModule, '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;
  }
}