192 lines
6.5 KiB
TypeScript
192 lines
6.5 KiB
TypeScript
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 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;
|
|
}
|
|
}
|