Files
2025-12-15 15:54:25 +00:00
..
2025-12-15 15:54:25 +00:00
2025-12-15 15:14:16 +00:00
2024-06-22 21:21:52 +02:00
2025-12-15 14:34:02 +00:00
2025-12-15 15:14:16 +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 = ``;

    // 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;
  }
}