import * as plugins from './plugins.js'; import * as paths from './paths.js'; import { logger } from './logging.js'; import { TypeDoc } from './classes.typedoc.js'; import { AiDoc } from './classes.aidoc.js'; import * as context from './context/index.js'; export const run = async () => { const tsdocCli = new plugins.smartcli.Smartcli(); tsdocCli.standardCommand().subscribe(async (argvArg) => { logger.log('warn', `Auto detecting environment!`); switch (true) { case await TypeDoc.isTypeDocDir(paths.cwd): logger.log('ok', `Detected TypeDoc compliant directory at ${paths.cwd}`); tsdocCli.triggerCommand('typedoc', argvArg); break; default: logger.log('error', `Cannot determine docs format at ${paths.cwd}`); } }); tsdocCli.addCommand('typedoc').subscribe(async (argvArg) => { const typeDocInstance = new TypeDoc(paths.cwd); await typeDocInstance.compile({ publicSubdir: argvArg.publicSubdir, }); }); tsdocCli.addCommand('aidoc').subscribe(async (argvArg) => { const aidocInstance = new AiDoc(); await aidocInstance.start(); // Get context token count if requested if (argvArg.tokens || argvArg.showTokens) { logger.log('info', `Calculating context token count...`); const tokenCount = await aidocInstance.getProjectContextTokenCount(paths.cwd); logger.log('ok', `Total context token count: ${tokenCount}`); if (argvArg.tokensOnly) { return; // Exit early if we only want token count } } logger.log('info', `Generating new readme...`); logger.log('info', `This may take some time...`); await aidocInstance.buildReadme(paths.cwd); logger.log('info', `Generating new keywords...`); logger.log('info', `This may take some time...`); await aidocInstance.buildDescription(paths.cwd); }); tsdocCli.addCommand('tokens').subscribe(async (argvArg) => { const aidocInstance = new AiDoc(); await aidocInstance.start(); logger.log('info', `Calculating context token count...`); // Determine context mode based on args let contextMode: context.ContextMode = 'full'; if (argvArg.trim || argvArg.trimmed) { contextMode = 'trimmed'; } else if (argvArg.summarize || argvArg.summarized) { contextMode = 'summarized'; } // Get task type if specified let taskType: context.TaskType | undefined = undefined; if (argvArg.task) { if (['readme', 'commit', 'description'].includes(argvArg.task)) { taskType = argvArg.task as context.TaskType; } else { logger.log('warn', `Unknown task type: ${argvArg.task}. Using default context.`); } } // Use enhanced context const taskFactory = new context.TaskContextFactory(paths.cwd); await taskFactory.initialize(); let contextResult: context.IContextResult; if (argvArg.all) { // Show stats for all task types const stats = await taskFactory.getTokenStats(); logger.log('ok', 'Token statistics by task:'); for (const [task, data] of Object.entries(stats)) { logger.log('info', `\n${task.toUpperCase()}:`); logger.log('info', ` Tokens: ${data.tokenCount}`); logger.log('info', ` Token savings: ${data.savings}`); logger.log('info', ` Files: ${data.includedFiles} included, ${data.trimmedFiles} trimmed, ${data.excludedFiles} excluded`); // Calculate percentage of model context const o4MiniPercentage = (data.tokenCount / 200000 * 100).toFixed(2); logger.log('info', ` Context usage: ${o4MiniPercentage}% of o4-mini (200K tokens)`); } return; } if (taskType) { // Get context for specific task contextResult = await taskFactory.createContextForTask(taskType); } else { // Get generic context with specified mode const enhancedContext = new context.EnhancedContext(paths.cwd); await enhancedContext.initialize(); enhancedContext.setContextMode(contextMode); if (argvArg.maxTokens) { enhancedContext.setTokenBudget(parseInt(argvArg.maxTokens, 10)); } contextResult = await enhancedContext.buildContext(); } // Display results logger.log('ok', `Total context token count: ${contextResult.tokenCount}`); logger.log('info', `Files included: ${contextResult.includedFiles.length}`); logger.log('info', `Files trimmed: ${contextResult.trimmedFiles.length}`); logger.log('info', `Files excluded: ${contextResult.excludedFiles.length}`); logger.log('info', `Token savings: ${contextResult.tokenSavings}`); if (argvArg.detailed) { // Show more detailed info about the context and token usage const o4MiniPercentage = (contextResult.tokenCount / 200000 * 100).toFixed(2); logger.log('info', `Token usage: ${o4MiniPercentage}% of o4-mini 200K token context window`); if (argvArg.model) { // Show percentages for different models if (argvArg.model === 'gpt4') { const gpt4Percentage = (contextResult.tokenCount / 8192 * 100).toFixed(2); logger.log('info', `Token usage (GPT-4): ${gpt4Percentage}% of 8192 token context window`); } else if (argvArg.model === 'gpt35') { const gpt35Percentage = (contextResult.tokenCount / 4096 * 100).toFixed(2); logger.log('info', `Token usage (GPT-3.5): ${gpt35Percentage}% of 4096 token context window`); } } // Estimate cost (approximate values) const o4MiniInputCost = 0.00005; // per 1K tokens for o4-mini const estimatedCost = (contextResult.tokenCount / 1000 * o4MiniInputCost).toFixed(6); logger.log('info', `Estimated input cost: $${estimatedCost} (o4-mini)`); if (argvArg.listFiles) { // List files included in context logger.log('info', '\nIncluded files:'); contextResult.includedFiles.forEach(file => { logger.log('info', ` ${file.relativePath} (${file.tokenCount} tokens)`); }); logger.log('info', '\nTrimmed files:'); contextResult.trimmedFiles.forEach(file => { logger.log('info', ` ${file.relativePath} (${file.tokenCount} tokens)`); }); if (contextResult.excludedFiles.length > 0) { logger.log('info', '\nExcluded files:'); contextResult.excludedFiles.forEach(file => { logger.log('info', ` ${file.relativePath} (${file.tokenCount} tokens)`); }); } } } }); tsdocCli.addCommand('test').subscribe((argvArg) => { tsdocCli.triggerCommand('typedoc', argvArg); process.on('exit', async () => { await plugins.smartfile.fs.remove(paths.publicDir); }); }); tsdocCli.startParse(); };