import * as plugins from '../plugins.js'; export class ProjectContext { public static async fromDir(dirArg: string) { const projectContext = new ProjectContext(dirArg); await projectContext.update(); return projectContext; } // INSTANCE public projectDir: string; private tokenCount: number = 0; private contextString: string = ''; constructor(projectDirArg: string) { this.projectDir = projectDirArg; } private async getSmartFile(fileName: string): Promise { const filePath = plugins.path.join(this.projectDir, fileName); if (await plugins.fsInstance.file(filePath).exists()) { return await plugins.smartfileFactory.fromFilePath(filePath, this.projectDir); } return plugins.smartfileFactory.fromString(filePath, '', 'utf8'); } public async gatherFiles() { const smartfilePackageJSON = await this.getSmartFile('package.json'); const smartfilesReadme = await this.getSmartFile('readme.md'); const smartfilesReadmeHints = await this.getSmartFile('readme.hints.md'); const smartfilesSmartconfigJSON = await this.getSmartFile('.smartconfig.json'); const virtualDirectory = await plugins.smartfileFactory.virtualDirectoryFromPath(this.projectDir); const smartfilesMod = await virtualDirectory .filter(f => (f.relative.startsWith('ts/') || f.relative.startsWith('ts_web/')) && f.relative.endsWith('.ts')) .listFiles(); const smartfilesTest = await virtualDirectory .filter(f => f.relative.startsWith('test/') && f.relative.endsWith('.ts')) .listFiles(); return { smartfilePackageJSON, smartfilesReadme, smartfilesReadmeHints, smartfilesSmartconfigJSON, smartfilesMod, smartfilesTest, }; } public async convertFilesToContext(filesArg: plugins.smartfile.SmartFile[]) { filesArg.map((fileArg) => { // console.log(` -> ${fileArg.relative}`); }); return filesArg .map((smartfile) => { return ` ====== START OF FILE ${smartfile.relative} ====== ${smartfile.contents.toString()} ====== END OF FILE ${smartfile.relative} ====== `; }) .join('\n'); } /** * Estimate token count for a string * Uses a rough estimate of 4 characters per token * @param text The text to estimate tokens for * @returns Estimated number of tokens */ public countTokens(text: string): number { // Rough estimate: ~4 characters per token for English text return Math.ceil(text.length / 4); } private async buildContext(dirArg: string) { const files = await this.gatherFiles(); let context = await this.convertFilesToContext([ files.smartfilePackageJSON, files.smartfilesReadme, files.smartfilesReadmeHints, files.smartfilesSmartconfigJSON, ...files.smartfilesMod, ...files.smartfilesTest, ]); // Count tokens in the context this.contextString = context; this.tokenCount = this.countTokens(context); // console.log(context); return context; } /** * Get the token count for the current context * @returns The number of tokens in the context */ public getTokenCount(): number { return this.tokenCount; } /** * Get both the context string and its token count * @returns An object containing the context string and token count */ public getContextWithTokenCount(): { context: string; tokenCount: number } { return { context: this.contextString, tokenCount: this.tokenCount }; } public async update() { const result = await this.buildContext(this.projectDir); return result; } }