@clduab11/gemini-flow
Version:
Revolutionary AI agent swarm coordination platform with Google Services integration, multimedia processing, and production-ready monitoring. Features 8 Google AI services, quantum computing capabilities, and enterprise-grade security.
618 lines (531 loc) • 18.4 kB
text/typescript
/**
* Context Gatherer - Collects code context for AI processing
*/
import * as vscode from 'vscode';
import * as path from 'path';
import { CodeContext, WorkspaceAnalysis } from '../types';
import { Logger } from './logger';
export class ContextGatherer {
constructor(private readonly _logger: Logger) {}
/**
* Gather context from a file and optional selection
*/
gatherFileContext(document: vscode.TextDocument, selection?: vscode.Selection): CodeContext {
try {
const workspaceFolder = vscode.workspace.getWorkspaceFolder(document.uri);
const workspaceRoot = workspaceFolder?.uri.fsPath;
const context: CodeContext = {
fileName: path.basename(document.fileName),
language: document.languageId,
fullText: document.getText(),
relativeFilePath: workspaceRoot
? path.relative(workspaceRoot, document.fileName)
: document.fileName,
workspaceRoot
};
if (selection && !selection.isEmpty) {
context.selectedText = document.getText(selection);
context.lineNumber = selection.start.line + 1;
context.columnNumber = selection.start.character + 1;
}
this._logger.debug('File context gathered', {
fileName: context.fileName,
language: context.language,
hasSelection: !!context.selectedText,
textLength: context.fullText.length
});
return context;
} catch (error) {
this._logger.error('Failed to gather file context', error as Error);
throw error;
}
}
/**
* Gather comprehensive project context
*/
async gatherProjectContext(workspaceFolder: vscode.WorkspaceFolder): Promise<WorkspaceAnalysis> {
try {
this._logger.info('Gathering project context...');
const analysis: WorkspaceAnalysis = {
projectType: 'unknown',
languages: [],
dependencies: [],
architecture: {
patterns: [],
complexity: 0,
maintainability: 0
},
recommendations: []
};
// Analyze project structure
await this.analyzeProjectStructure(workspaceFolder, analysis);
// Detect project type
analysis.projectType = await this.detectProjectType(workspaceFolder);
// Analyze languages used
analysis.languages = await this.detectLanguages(workspaceFolder);
// Analyze dependencies
analysis.dependencies = await this.analyzeDependencies(workspaceFolder);
// Analyze architecture patterns
analysis.architecture = await this.analyzeArchitecture(workspaceFolder);
// Generate recommendations
analysis.recommendations = this.generateRecommendations(analysis);
this._logger.info('Project context analysis completed', {
projectType: analysis.projectType,
languageCount: analysis.languages.length,
dependencyCount: analysis.dependencies.length
});
return analysis;
} catch (error) {
this._logger.error('Failed to gather project context', error as Error);
throw error;
}
}
/**
* Gather workspace-wide context
*/
async gatherWorkspaceContext(): Promise<{
folders: string[];
openFiles: string[];
recentChanges: string[];
}> {
try {
const context = {
folders: [] as string[],
openFiles: [] as string[],
recentChanges: [] as string[]
};
// Get workspace folders
if (vscode.workspace.workspaceFolders) {
context.folders = vscode.workspace.workspaceFolders.map(folder =>
path.basename(folder.uri.fsPath)
);
}
// Get open files
context.openFiles = vscode.workspace.textDocuments
.filter(doc => !doc.isUntitled && doc.uri.scheme === 'file')
.map(doc => path.basename(doc.fileName));
// Get recent changes (from Git if available)
context.recentChanges = await this.getRecentChanges();
this._logger.debug('Workspace context gathered', {
folderCount: context.folders.length,
openFileCount: context.openFiles.length,
recentChangeCount: context.recentChanges.length
});
return context;
} catch (error) {
this._logger.error('Failed to gather workspace context', error as Error);
throw error;
}
}
/**
* Get surrounding context for a specific line
*/
getSurroundingContext(document: vscode.TextDocument, line: number, contextLines = 5): {
before: string[];
after: string[];
target: string;
} {
const totalLines = document.lineCount;
const startLine = Math.max(0, line - contextLines);
const endLine = Math.min(totalLines - 1, line + contextLines);
const before: string[] = [];
const after: string[] = [];
for (let i = startLine; i < line; i++) {
before.push(document.lineAt(i).text);
}
for (let i = line + 1; i <= endLine; i++) {
after.push(document.lineAt(i).text);
}
const target = line < totalLines ? document.lineAt(line).text : '';
return { before, after, target };
}
/**
* Extract imports and dependencies from code
*/
extractImportsAndDependencies(document: vscode.TextDocument): {
imports: string[];
dependencies: string[];
} {
const text = document.getText();
const language = document.languageId;
const imports: string[] = [];
const dependencies: string[] = [];
try {
switch (language) {
case 'typescript':
case 'javascript':
this.extractJavaScriptImports(text, imports, dependencies);
break;
case 'python':
this.extractPythonImports(text, imports, dependencies);
break;
case 'java':
this.extractJavaImports(text, imports, dependencies);
break;
case 'go':
this.extractGoImports(text, imports, dependencies);
break;
case 'rust':
this.extractRustImports(text, imports, dependencies);
break;
default:
this._logger.debug(`Import extraction not implemented for language: ${language}`);
}
} catch (error) {
this._logger.error('Failed to extract imports', error as Error);
}
return { imports, dependencies };
}
/**
* Analyze project structure
*/
private async analyzeProjectStructure(
workspaceFolder: vscode.WorkspaceFolder,
analysis: WorkspaceAnalysis
): Promise<void> {
try {
const files = await vscode.workspace.findFiles(
new vscode.RelativePattern(workspaceFolder, '**/*'),
'**/node_modules/**',
1000
);
// Count files by type
const fileTypes = new Map<string, number>();
for (const file of files) {
const ext = path.extname(file.fsPath);
fileTypes.set(ext, (fileTypes.get(ext) || 0) + 1);
}
// Determine complexity based on file count and structure
analysis.architecture.complexity = this.calculateComplexity(files.length, fileTypes);
this._logger.debug('Project structure analyzed', {
totalFiles: files.length,
fileTypes: Array.from(fileTypes.entries())
});
} catch (error) {
this._logger.error('Failed to analyze project structure', error as Error);
}
}
/**
* Detect project type
*/
private async detectProjectType(workspaceFolder: vscode.WorkspaceFolder): Promise<string> {
const indicators = [
{ files: ['package.json'], type: 'Node.js' },
{ files: ['requirements.txt', 'setup.py', 'pyproject.toml'], type: 'Python' },
{ files: ['pom.xml', 'build.gradle'], type: 'Java' },
{ files: ['Cargo.toml'], type: 'Rust' },
{ files: ['go.mod'], type: 'Go' },
{ files: ['Gemfile'], type: 'Ruby' },
{ files: ['composer.json'], type: 'PHP' },
{ files: ['.csproj', '.sln'], type: 'C#' },
{ files: ['CMakeLists.txt'], type: 'C++' }
];
for (const indicator of indicators) {
for (const file of indicator.files) {
try {
const uri = vscode.Uri.joinPath(workspaceFolder.uri, file);
await vscode.workspace.fs.stat(uri);
return indicator.type;
} catch {
// File doesn't exist, continue
}
}
}
return 'unknown';
}
/**
* Detect languages used in the project
*/
private async detectLanguages(workspaceFolder: vscode.WorkspaceFolder): Promise<string[]> {
const languageMap = new Map<string, number>();
try {
const files = await vscode.workspace.findFiles(
new vscode.RelativePattern(workspaceFolder, '**/*'),
'**/node_modules/**',
500
);
for (const file of files) {
const ext = path.extname(file.fsPath).toLowerCase();
const language = this.getLanguageFromExtension(ext);
if (language) {
languageMap.set(language, (languageMap.get(language) || 0) + 1);
}
}
} catch (error) {
this._logger.error('Failed to detect languages', error as Error);
}
// Return languages sorted by usage
return Array.from(languageMap.entries())
.sort((a, b) => b[1] - a[1])
.map(([language]) => language);
}
/**
* Analyze project dependencies
*/
private async analyzeDependencies(workspaceFolder: vscode.WorkspaceFolder): Promise<string[]> {
const dependencies: string[] = [];
try {
// Check package.json
const packageJsonUri = vscode.Uri.joinPath(workspaceFolder.uri, 'package.json');
try {
const content = await vscode.workspace.fs.readFile(packageJsonUri);
const packageJson = JSON.parse(Buffer.from(content).toString());
if (packageJson.dependencies) {
dependencies.push(...Object.keys(packageJson.dependencies));
}
if (packageJson.devDependencies) {
dependencies.push(...Object.keys(packageJson.devDependencies));
}
} catch {
// package.json doesn't exist or is invalid
}
// Check requirements.txt
const requirementsUri = vscode.Uri.joinPath(workspaceFolder.uri, 'requirements.txt');
try {
const content = await vscode.workspace.fs.readFile(requirementsUri);
const lines = Buffer.from(content).toString().split('\\n');
for (const line of lines) {
const dep = line.trim().split(/[>=<!=]/)[0];
if (dep) {
dependencies.push(dep);
}
}
} catch {
// requirements.txt doesn't exist
}
// Add more dependency file checks as needed...
} catch (error) {
this._logger.error('Failed to analyze dependencies', error as Error);
}
return [...new Set(dependencies)]; // Remove duplicates
}
/**
* Analyze architecture patterns
*/
private async analyzeArchitecture(workspaceFolder: vscode.WorkspaceFolder): Promise<{
patterns: string[];
complexity: number;
maintainability: number;
}> {
const patterns: string[] = [];
let complexity = 0;
let maintainability = 0;
try {
const files = await vscode.workspace.findFiles(
new vscode.RelativePattern(workspaceFolder, '**/*.{ts,js,py,java,go,rs}'),
'**/node_modules/**',
200
);
// Detect common patterns
const folderStructure = new Set<string>();
for (const file of files) {
const relativePath = path.relative(workspaceFolder.uri.fsPath, file.fsPath);
const parts = relativePath.split(path.sep);
if (parts.length > 1) {
folderStructure.add(parts[0]);
}
}
// Detect architectural patterns
if (folderStructure.has('src') && folderStructure.has('test')) {
patterns.push('Standard Source Structure');
}
if (folderStructure.has('components') || folderStructure.has('views')) {
patterns.push('Component-Based Architecture');
}
if (folderStructure.has('controllers') && folderStructure.has('models')) {
patterns.push('MVC Pattern');
}
if (folderStructure.has('services') || folderStructure.has('api')) {
patterns.push('Service Layer');
}
// Calculate metrics
complexity = this.calculateComplexity(files.length, new Map());
maintainability = this.calculateMaintainability(patterns.length, folderStructure.size);
} catch (error) {
this._logger.error('Failed to analyze architecture', error as Error);
}
return { patterns, complexity, maintainability };
}
/**
* Generate recommendations based on analysis
*/
private generateRecommendations(analysis: WorkspaceAnalysis): string[] {
const recommendations: string[] = [];
if (analysis.architecture.complexity > 80) {
recommendations.push('Consider refactoring to reduce complexity');
}
if (analysis.architecture.maintainability < 50) {
recommendations.push('Improve code organization and documentation');
}
if (analysis.dependencies.length > 50) {
recommendations.push('Review dependencies for unused packages');
}
if (analysis.languages.length > 5) {
recommendations.push('Consider consolidating technology stack');
}
if (analysis.architecture.patterns.length === 0) {
recommendations.push('Consider adopting architectural patterns for better organization');
}
return recommendations;
}
/**
* Get recent changes from Git
*/
private async getRecentChanges(): Promise<string[]> {
// This would integrate with Git extension or run git commands
// For now, return empty array
return [];
}
/**
* Extract JavaScript/TypeScript imports
*/
private extractJavaScriptImports(text: string, imports: string[], dependencies: string[]): void {
const importRegex = /import.*?from\\s+['"]([^'"]+)['"]/g;
const requireRegex = /require\\(['"]([^'"]+)['"]\\)/g;
let match;
while ((match = importRegex.exec(text)) !== null) {
imports.push(match[1]);
if (!match[1].startsWith('.')) {
dependencies.push(match[1].split('/')[0]);
}
}
while ((match = requireRegex.exec(text)) !== null) {
imports.push(match[1]);
if (!match[1].startsWith('.')) {
dependencies.push(match[1].split('/')[0]);
}
}
}
/**
* Extract Python imports
*/
private extractPythonImports(text: string, imports: string[], dependencies: string[]): void {
const importRegex = /(?:from\\s+([\\w.]+)\\s+)?import\\s+([\\w.,\\s]+)/g;
let match;
while ((match = importRegex.exec(text)) !== null) {
if (match[1]) {
imports.push(match[1]);
dependencies.push(match[1].split('.')[0]);
} else {
const modules = match[2].split(',').map(m => m.trim());
imports.push(...modules);
dependencies.push(...modules);
}
}
}
/**
* Extract Java imports
*/
private extractJavaImports(text: string, imports: string[], dependencies: string[]): void {
const importRegex = /import\\s+([\\w.]+);/g;
let match;
while ((match = importRegex.exec(text)) !== null) {
imports.push(match[1]);
const parts = match[1].split('.');
if (parts.length > 2) {
dependencies.push(parts.slice(0, 2).join('.'));
}
}
}
/**
* Extract Go imports
*/
private extractGoImports(text: string, imports: string[], dependencies: string[]): void {
const importRegex = /import\\s+(?:\\(([^)]+)\\)|"([^"]+)")/g;
let match;
while ((match = importRegex.exec(text)) !== null) {
if (match[1]) {
// Multiple imports
const lines = match[1].split('\\n');
for (const line of lines) {
const importMatch = line.match(/"([^"]+)"/);
if (importMatch) {
imports.push(importMatch[1]);
dependencies.push(importMatch[1]);
}
}
} else if (match[2]) {
// Single import
imports.push(match[2]);
dependencies.push(match[2]);
}
}
}
/**
* Extract Rust imports
*/
private extractRustImports(text: string, imports: string[], dependencies: string[]): void {
const useRegex = /use\\s+([\\w:]+)/g;
let match;
while ((match = useRegex.exec(text)) !== null) {
imports.push(match[1]);
const parts = match[1].split('::');
if (parts.length > 0) {
dependencies.push(parts[0]);
}
}
}
/**
* Get language from file extension
*/
private getLanguageFromExtension(ext: string): string | null {
const extensionMap: Record<string, string> = {
'.js': 'JavaScript',
'.ts': 'TypeScript',
'.jsx': 'JavaScript React',
'.tsx': 'TypeScript React',
'.py': 'Python',
'.java': 'Java',
'.go': 'Go',
'.rs': 'Rust',
'.rb': 'Ruby',
'.php': 'PHP',
'.cs': 'C#',
'.cpp': 'C++',
'.c': 'C',
'.h': 'C/C++ Header',
'.hpp': 'C++ Header',
'.css': 'CSS',
'.scss': 'SCSS',
'.html': 'HTML',
'.xml': 'XML',
'.json': 'JSON',
'.yaml': 'YAML',
'.yml': 'YAML',
'.md': 'Markdown',
'.sql': 'SQL'
};
return extensionMap[ext] || null;
}
/**
* Calculate complexity score
*/
private calculateComplexity(fileCount: number, fileTypes: Map<string, number>): number {
let complexity = Math.min(fileCount / 10, 50); // Base complexity from file count
// Add complexity for diverse file types
complexity += Math.min(fileTypes.size * 2, 30);
// Add complexity for specific file types that indicate complexity
const complexTypes = ['.ts', '.js', '.java', '.py', '.go', '.rs'];
for (const type of complexTypes) {
const count = fileTypes.get(type) || 0;
complexity += Math.min(count * 0.5, 20);
}
return Math.min(Math.round(complexity), 100);
}
/**
* Calculate maintainability score
*/
private calculateMaintainability(patternCount: number, folderCount: number): number {
let score = 50; // Base score
// Add points for architectural patterns
score += patternCount * 10;
// Add points for organized folder structure
if (folderCount > 2 && folderCount < 10) {
score += 20;
} else if (folderCount >= 10) {
score += 10;
}
// Cap at 100
return Math.min(score, 100);
}
}