215 lines
6.9 KiB
TypeScript
215 lines
6.9 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 = ``;
|
|
|
|
// 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;
|
|
}
|
|
}
|