UNPKG

@git.zone/tsdoc

Version:

A comprehensive TypeScript documentation tool that leverages AI to generate and enhance project documentation, including dynamic README creation, API docs via TypeDoc, and smart commit message generation.

181 lines (168 loc) 15.4 kB
import * as plugins from '../plugins.js'; import { AiDoc } from '../classes.aidoc.js'; import { ProjectContext } from './projectcontext.js'; import { DiffProcessor } from '../context/diff-processor.js'; export class Commit { constructor(aiDocsRef, projectDirArg) { this.aiDocsRef = aiDocsRef; this.projectDir = projectDirArg; } async buildNextCommitObject() { const smartgitInstance = new plugins.smartgit.Smartgit(); await smartgitInstance.init(); const gitRepo = await plugins.smartgit.GitRepo.fromOpeningRepoDir(smartgitInstance, this.projectDir); // Define comprehensive exclusion patterns // smartgit@3.3.0+ supports glob patterns natively const excludePatterns = [ // Lock files 'pnpm-lock.yaml', 'package-lock.json', 'npm-shrinkwrap.json', 'yarn.lock', 'deno.lock', 'bun.lockb', // Build artifacts (main culprit for large diffs!) 'dist/**', 'dist_*/**', // dist_ts, dist_web, etc. 'build/**', '.next/**', 'out/**', 'public/dist/**', // Compiled/bundled files '**/*.js.map', '**/*.d.ts.map', '**/*.min.js', '**/*.bundle.js', '**/*.chunk.js', // IDE/Editor directories '.claude/**', '.cursor/**', '.vscode/**', '.idea/**', '**/*.swp', '**/*.swo', // Logs and caches '.nogit/**', '**/*.log', '.cache/**', '.rpt2_cache/**', 'coverage/**', '.nyc_output/**', ]; // Pass glob patterns directly to smartgit - it handles matching internally const diffStringArray = await gitRepo.getUncommittedDiff(excludePatterns); // Process diffs intelligently using DiffProcessor let processedDiffString; if (diffStringArray.length > 0) { // Diagnostic logging for raw diff statistics const totalChars = diffStringArray.join('\n\n').length; const estimatedTokens = Math.ceil(totalChars / 4); console.log(`📊 Raw git diff statistics:`); console.log(` Files changed: ${diffStringArray.length}`); console.log(` Total characters: ${totalChars.toLocaleString()}`); console.log(` Estimated tokens: ${estimatedTokens.toLocaleString()}`); console.log(` Exclusion patterns: ${excludePatterns.length}`); // Use DiffProcessor to intelligently handle large diffs const diffProcessor = new DiffProcessor({ maxDiffTokens: 100000, // Reserve 100k tokens for diffs smallFileLines: 50, // Include files <= 50 lines fully mediumFileLines: 200, // Summarize files <= 200 lines sampleHeadLines: 20, // Show first 20 lines sampleTailLines: 20, // Show last 20 lines }); const processedDiff = diffProcessor.processDiffs(diffStringArray); processedDiffString = diffProcessor.formatForContext(processedDiff); console.log(`📝 Processed diff statistics:`); console.log(` Full diffs: ${processedDiff.fullDiffs.length} files`); console.log(` Summarized: ${processedDiff.summarizedDiffs.length} files`); console.log(` Metadata only: ${processedDiff.metadataOnly.length} files`); console.log(` Final tokens: ${processedDiff.totalTokens.toLocaleString()}`); if (estimatedTokens > 50000) { console.log(`✅ DiffProcessor reduced token usage: ${estimatedTokens.toLocaleString()}${processedDiff.totalTokens.toLocaleString()}`); } } else { processedDiffString = 'No changes.'; } // Use the new TaskContextFactory for optimized context const taskContextFactory = new (await import('../context/index.js')).TaskContextFactory(this.projectDir, this.aiDocsRef.openaiInstance); await taskContextFactory.initialize(); // Generate context specifically for commit task const contextResult = await taskContextFactory.createContextForCommit(processedDiffString); // Get the optimized context string let contextString = contextResult.context; // Log token usage statistics console.log(`Token usage - Context: ${contextResult.tokenCount}, Files: ${contextResult.includedFiles.length + contextResult.trimmedFiles.length}, Savings: ${contextResult.tokenSavings}`); // Check for token overflow against model limits const MODEL_TOKEN_LIMIT = 200000; // o4-mini if (contextResult.tokenCount > MODEL_TOKEN_LIMIT * 0.9) { console.log(`⚠️ Warning: Context size (${contextResult.tokenCount} tokens) is close to or exceeds model limit (${MODEL_TOKEN_LIMIT} tokens).`); console.log(`The model may not be able to process all information effectively.`); } let result = await this.aiDocsRef.openaiInstance.chat({ systemMessage: ` You create a commit message for a git commit. The commit message should be based on the files in the project. You should not include any licensing information. You should not include any personal information. Important: Answer only in valid JSON. Your answer should be parseable with JSON.parse() without modifying anything. Here is the structure of the JSON you should return: interface { recommendedNextVersionLevel: 'fix' | 'feat' | 'BREAKING CHANGE'; // the recommended next version level of the project recommendedNextVersionScope: string; // the recommended scope name of the next version, like "core" or "cli", or specific class names. recommendedNextVersionMessage: string; // the commit message. Don't put fix() feat() or BREAKING CHANGE in the message. Please just the message itself. recommendedNextVersionDetails: string[]; // detailed bullet points for the changelog recommendedNextVersion: string; // the recommended next version of the project, x.x.x } For the recommendedNextVersionDetails, please only add a detail entries to the array if it has an obvious value to the reader. You are being given the files of the project. You should use them to create the commit message. Also you are given a diff. Never mention CLAUDE code, or codex. `, messageHistory: [], userMessage: contextString, }); // console.log(result.message); const resultObject = JSON.parse(result.message.replace('```json', '').replace('```', '')); const previousChangelogPath = plugins.path.join(this.projectDir, 'changelog.md'); let previousChangelog; if (await plugins.smartfile.fs.fileExists(previousChangelogPath)) { previousChangelog = await plugins.smartfile.SmartFile.fromFilePath(previousChangelogPath); } if (!previousChangelog) { // lets build the changelog based on that const commitMessages = await gitRepo.getAllCommitMessages(); console.log(JSON.stringify(commitMessages, null, 2)); let result2 = await this.aiDocsRef.openaiInstance.chat({ messageHistory: [], systemMessage: ` You are building a changelog.md file for the project. Omit commits and versions that lack relevant changes, but make sure to mention them as a range with a summarizing message instead. A changelog entry should look like this: ## yyyy-mm-dd - x.x.x - scope here main descriptiom here - detailed bullet points follow You are given: * the commit messages of the project Only return the changelog file, so it can be written directly to changelog.md`, userMessage: ` Here are the commit messages: ${JSON.stringify(commitMessages, null, 2)} `, }); previousChangelog = await plugins.smartfile.SmartFile.fromString(previousChangelogPath, result2.message.replaceAll('```markdown', '').replaceAll('```', ''), 'utf8'); } let oldChangelog = previousChangelog.contents.toString().replace('# Changelog\n\n', ''); if (oldChangelog.startsWith('\n')) { oldChangelog = oldChangelog.replace('\n', ''); } let newDateString = new plugins.smarttime.ExtendedDate().exportToHyphedSortableDate(); let newChangelog = `# Changelog\n\n${`## ${newDateString} - {{nextVersion}} - {{nextVersionScope}} {{nextVersionMessage}} {{nextVersionDetails}}`}\n\n${oldChangelog}`; resultObject.changelog = newChangelog; return resultObject; } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29tbWl0LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vdHMvYWlkb2NzX2NsYXNzZXMvY29tbWl0LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sZUFBZSxDQUFDO0FBQ3pDLE9BQU8sRUFBRSxLQUFLLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQztBQUM1QyxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0scUJBQXFCLENBQUM7QUFDckQsT0FBTyxFQUFFLGFBQWEsRUFBRSxNQUFNLDhCQUE4QixDQUFDO0FBVzdELE1BQU0sT0FBTyxNQUFNO0lBSWpCLFlBQVksU0FBZ0IsRUFBRSxhQUFxQjtRQUNqRCxJQUFJLENBQUMsU0FBUyxHQUFHLFNBQVMsQ0FBQztRQUMzQixJQUFJLENBQUMsVUFBVSxHQUFHLGFBQWEsQ0FBQztJQUNsQyxDQUFDO0lBRU0sS0FBSyxDQUFDLHFCQUFxQjtRQUNoQyxNQUFNLGdCQUFnQixHQUFHLElBQUksT0FBTyxDQUFDLFFBQVEsQ0FBQyxRQUFRLEVBQUUsQ0FBQztRQUN6RCxNQUFNLGdCQUFnQixDQUFDLElBQUksRUFBRSxDQUFDO1FBQzlCLE1BQU0sT0FBTyxHQUFHLE1BQU0sT0FBTyxDQUFDLFFBQVEsQ0FBQyxPQUFPLENBQUMsa0JBQWtCLENBQy9ELGdCQUFnQixFQUNoQixJQUFJLENBQUMsVUFBVSxDQUNoQixDQUFDO1FBRUYsMENBQTBDO1FBQzFDLGtEQUFrRDtRQUNsRCxNQUFNLGVBQWUsR0FBRztZQUN0QixhQUFhO1lBQ2IsZ0JBQWdCO1lBQ2hCLG1CQUFtQjtZQUNuQixxQkFBcUI7WUFDckIsV0FBVztZQUNYLFdBQVc7WUFDWCxXQUFXO1lBRVgsa0RBQWtEO1lBQ2xELFNBQVM7WUFDVCxXQUFXLEVBQVksMEJBQTBCO1lBQ2pELFVBQVU7WUFDVixVQUFVO1lBQ1YsUUFBUTtZQUNSLGdCQUFnQjtZQUVoQix5QkFBeUI7WUFDekIsYUFBYTtZQUNiLGVBQWU7WUFDZixh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