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 smartconfigJson = JSON.parse(
(await projectContext.gatherFiles()).smartfilesNpmextraJSON.contents.toString()
);
const legalInfo = smartconfigJson?.['@git.zone/tsdoc']?.legal;
if (!legalInfo) {
const error = new Error(`No legal information found in smartconfig.json`);
console.log(error);
}
// Use runAgent with filesystem tool for agent-driven exploration
const fsTools = plugins.smartagentTools.filesystemTool({ rootDir: this.projectDir });
const readmeSystemPrompt = `
You create markdown READMEs for npm projects. You only output the markdown readme.
You have access to filesystem tools to explore the project. Use them to understand the codebase.
IMPORTANT RULES:
- Only READ files within the project directory
- Do NOT write, delete, or modify any files
- README must follow proper markdown format
- Must contain Install and Usage sections
- Code examples must use correct TypeScript/ESM syntax
- Documentation must be comprehensive and helpful
- Do NOT include licensing information (added separately)
- Do NOT use CommonJS syntax - only ESM
- Do NOT include "in conclusion" or similar filler
`;
const readmeTaskPrompt = `
PROJECT DIRECTORY: ${this.projectDir}
Use the filesystem tools to explore the project and understand what it does:
1. First, use list_directory to see the project structure
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.
]
`;
logger.log('info', 'Starting README generation with agent...');
const readmeResult = await plugins.smartagent.runAgent({
model: this.aiDocsRef.model,
prompt: readmeTaskPrompt,
system: readmeSystemPrompt,
tools: fsTools,
maxSteps: 25,
onToolCall: (toolName) => logger.log('info', `[README] Tool call: ${toolName}`),
});
// Clean up markdown formatting if wrapped in code blocks
let resultMessage = readmeResult.text
.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();
const subModuleFsTools = plugins.smartagentTools.filesystemTool({ rootDir: subModulePath });
const subModuleSystemPrompt = `
You create markdown READMEs for npm projects. You only output the markdown readme.
IMPORTANT RULES:
- Only READ files within the submodule directory
- Do NOT write, delete, or modify any files
- README must be comprehensive, well-formatted markdown with ESM TypeScript examples
- Do NOT include licensing information (added separately)
`;
const subModulePrompt = `
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 tools to explore the submodule:
1. Use list_directory 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 plugins.smartagent.runAgent({
model: this.aiDocsRef.model,
prompt: subModulePrompt,
system: subModuleSystemPrompt,
tools: subModuleFsTools,
maxSteps: 20,
onToolCall: (toolName) => logger.log('info', `[README:${subModule}] Tool call: ${toolName}`),
});
const subModuleReadmeString = subModuleResult.text
.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}`);
}
return resultMessage;
}
}