This commit is contained in:
164
.ai-context-generator-cli/src/generators/contextGenerator.ts
Normal file
164
.ai-context-generator-cli/src/generators/contextGenerator.ts
Normal file
@@ -0,0 +1,164 @@
|
||||
import * as path from 'path';
|
||||
import yaml from 'js-yaml';
|
||||
import {
|
||||
ProjectInfo,
|
||||
ContextConfig,
|
||||
TemplateData,
|
||||
FileInfo,
|
||||
ConventionInfo,
|
||||
} from '../types';
|
||||
import { ProjectTypeDetector } from '../analyzers/projectTypeDetector';
|
||||
import { DependencyAnalyzer } from '../analyzers/dependencyAnalyzer';
|
||||
import { ConventionExtractor } from '../analyzers/conventionExtractor';
|
||||
import { FileUtils } from '../utils/fileUtils';
|
||||
import { ConfigLoader } from '../config/configLoader';
|
||||
|
||||
export class ContextGenerator {
|
||||
private projectTypeDetector: ProjectTypeDetector;
|
||||
private dependencyAnalyzer: DependencyAnalyzer;
|
||||
private conventionExtractor: ConventionExtractor;
|
||||
private fileUtils: FileUtils;
|
||||
|
||||
constructor() {
|
||||
this.projectTypeDetector = new ProjectTypeDetector();
|
||||
this.dependencyAnalyzer = new DependencyAnalyzer();
|
||||
this.conventionExtractor = new ConventionExtractor();
|
||||
this.fileUtils = FileUtils.getInstance();
|
||||
}
|
||||
|
||||
async generate(
|
||||
dir: string,
|
||||
config?: ContextConfig
|
||||
): Promise<ProjectInfo> {
|
||||
const resolvedDir = this.fileUtils.resolveDirectory(dir);
|
||||
|
||||
const contextConfig = config ?? await ConfigLoader.load();
|
||||
|
||||
if (contextConfig.respectGitignore) {
|
||||
await this.fileUtils.loadGitignore(resolvedDir);
|
||||
this.fileUtils.addCustomPatterns(contextConfig.excludes);
|
||||
}
|
||||
|
||||
const files = await this.fileUtils.getFiles(
|
||||
resolvedDir,
|
||||
contextConfig.includes,
|
||||
contextConfig.excludes
|
||||
);
|
||||
|
||||
const projectType = await this.projectTypeDetector.detect(resolvedDir);
|
||||
const dependencies = await this.dependencyAnalyzer.analyze(
|
||||
resolvedDir,
|
||||
contextConfig.includeDevDependencies
|
||||
);
|
||||
|
||||
let conventions: ConventionInfo | null = null;
|
||||
if (contextConfig.analyzeConventions) {
|
||||
conventions = await this.conventionExtractor.extract(resolvedDir, files) ?? null;
|
||||
}
|
||||
|
||||
return {
|
||||
projectType,
|
||||
language: projectType.primaryLanguage,
|
||||
framework: projectType.frameworks[0] || null,
|
||||
dependencies,
|
||||
conventions,
|
||||
fileCount: files.length,
|
||||
analysisDate: new Date().toISOString(),
|
||||
};
|
||||
}
|
||||
|
||||
async generateWithFiles(
|
||||
dir: string,
|
||||
config?: ContextConfig
|
||||
): Promise<TemplateData> {
|
||||
const projectInfo = await this.generate(dir, config);
|
||||
const contextConfig = config ?? await ConfigLoader.load();
|
||||
const resolvedDir = this.fileUtils.resolveDirectory(dir);
|
||||
|
||||
const files = await this.fileUtils.getFiles(
|
||||
resolvedDir,
|
||||
contextConfig.includes,
|
||||
contextConfig.excludes
|
||||
);
|
||||
|
||||
const fileInfos: FileInfo[] = [];
|
||||
for (const file of files) {
|
||||
try {
|
||||
const size = await this.fileUtils.getFileSize(file);
|
||||
fileInfos.push({
|
||||
path: path.relative(resolvedDir, file),
|
||||
size,
|
||||
type: this.fileUtils.getFileExtension(file),
|
||||
language: this.detectLanguage(file),
|
||||
});
|
||||
} catch {
|
||||
// Skip files that can't be read
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
projectInfo,
|
||||
files: fileInfos,
|
||||
config: contextConfig,
|
||||
generatedAt: new Date().toISOString(),
|
||||
};
|
||||
}
|
||||
|
||||
async generateJson(dir: string, config?: ContextConfig): Promise<string> {
|
||||
const data = await this.generateWithFiles(dir, config);
|
||||
return JSON.stringify(data, null, 2);
|
||||
}
|
||||
|
||||
async generateYaml(dir: string, config?: ContextConfig): Promise<string> {
|
||||
const data = await this.generateWithFiles(dir, config);
|
||||
return yaml.dump(data, { indent: 2, lineWidth: -1 });
|
||||
}
|
||||
|
||||
async saveContext(
|
||||
dir: string,
|
||||
outputPath: string,
|
||||
format: 'json' | 'yaml',
|
||||
config?: ContextConfig
|
||||
): Promise<void> {
|
||||
let content: string;
|
||||
let finalPath = outputPath;
|
||||
|
||||
if (format === 'json') {
|
||||
content = await this.generateJson(dir, config);
|
||||
if (!finalPath.endsWith('.json')) {
|
||||
finalPath = `${finalPath}.json`;
|
||||
}
|
||||
} else {
|
||||
content = await this.generateYaml(dir, config);
|
||||
if (!finalPath.endsWith('.yaml') && !finalPath.endsWith('.yml')) {
|
||||
finalPath = `${finalPath}.yaml`;
|
||||
}
|
||||
}
|
||||
|
||||
await this.fileUtils.writeFile(finalPath, content);
|
||||
}
|
||||
|
||||
private detectLanguage(filePath: string): string {
|
||||
const ext = this.fileUtils.getFileExtension(filePath);
|
||||
const languageMap: Record<string, string> = {
|
||||
'.ts': 'TypeScript',
|
||||
'.tsx': 'TypeScript',
|
||||
'.js': 'JavaScript',
|
||||
'.jsx': 'JavaScript',
|
||||
'.py': 'Python',
|
||||
'.go': 'Go',
|
||||
'.rs': 'Rust',
|
||||
'.java': 'Java',
|
||||
'.c': 'C',
|
||||
'.cpp': 'C++',
|
||||
'.h': 'C',
|
||||
'.hpp': 'C++',
|
||||
'.cs': 'C#',
|
||||
'.rb': 'Ruby',
|
||||
'.php': 'PHP',
|
||||
'.swift': 'Swift',
|
||||
};
|
||||
|
||||
return languageMap[ext] || 'Unknown';
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user