blue-beatle
Version:
š¤ AI-Powered Development Assistant - Intelligent code analysis, problem solving, and terminal automation using Gemini API
1,197 lines (998 loc) ⢠69.3 kB
JavaScript
/**
* š¤ AI Assistant - Gemini API Integration
* Handles all AI-powered features and interactions
*/
const { GoogleGenerativeAI } = require('@google/generative-ai');
const chalk = require('chalk');
const ora = require('ora');
const inquirer = require('inquirer');
const fs = require('fs-extra');
const path = require('path');
const { execSync, spawn } = require('child_process');
class AIAssistant {
constructor(configManager) {
this.configManager = configManager;
this.genAI = null;
this.model = null;
this.conversationHistory = [];
this.currentContext = {
workingDirectory: process.cwd(),
projectType: null,
files: [],
recentCommands: [],
openFiles: [],
workspaceStructure: null,
gitInfo: null,
dependencies: null
};
this.fileWatcher = null;
this.pendingChanges = new Map();
this.initialize();
}
async initialize() {
try {
const apiKey = await this.configManager.get('gemini.apiKey');
if (!apiKey) {
console.log(chalk.yellow('ā ļø Gemini API key not found. Run "blue-beatle config --setup-api" to configure.'));
return false;
}
this.genAI = new GoogleGenerativeAI(apiKey);
// Use the most advanced model with enhanced configuration
this.model = this.genAI.getGenerativeModel({
model: 'gemini-2.0-flash',
generationConfig: {
temperature: 0.7,
topK: 40,
topP: 0.95,
maxOutputTokens: 8192,
},
safetySettings: [
{
category: 'HARM_CATEGORY_HARASSMENT',
threshold: 'BLOCK_MEDIUM_AND_ABOVE'
},
{
category: 'HARM_CATEGORY_HATE_SPEECH',
threshold: 'BLOCK_MEDIUM_AND_ABOVE'
}
]
});
// Initialize context
await this.updateContext();
// Validate AI connection with a test query
await this.validateAIConnection();
return true;
} catch (error) {
console.error(chalk.red('ā Failed to initialize AI Assistant:'), error.message);
return false;
}
}
async validateAIConnection() {
try {
const testPrompt = "Respond with exactly: 'Blue Beatle AI is connected and operational.'";
const result = await this.model.generateContent(testPrompt);
const response = result.response.text().trim();
if (!response.includes('Blue Beatle AI is connected and operational')) {
console.log(chalk.yellow('ā ļø AI connection validated but response format may vary.'));
}
return true;
} catch (error) {
console.error(chalk.red('ā AI validation failed:'), error.message);
throw new Error('AI connection could not be validated');
}
}
async updateContext() {
try {
// Detect project type
this.currentContext.projectType = await this.detectProjectType();
// Get file list
this.currentContext.files = await this.getProjectFiles();
// Update working directory
this.currentContext.workingDirectory = process.cwd();
// Analyze workspace structure
this.currentContext.workspaceStructure = await this.analyzeWorkspaceStructure();
// Get Git information
this.currentContext.gitInfo = await this.getGitInfo();
// Analyze dependencies
this.currentContext.dependencies = await this.analyzeDependencies();
// Setup file watcher
await this.setupFileWatcher();
} catch (error) {
console.error(chalk.red('ā Failed to update context:'), error.message);
}
}
async detectProjectType() {
const cwd = process.cwd();
if (await fs.pathExists(path.join(cwd, 'package.json'))) {
const pkg = await fs.readJson(path.join(cwd, 'package.json'));
if (pkg.dependencies?.react) return 'React';
if (pkg.dependencies?.vue) return 'Vue';
if (pkg.dependencies?.angular) return 'Angular';
if (pkg.dependencies?.express) return 'Express/Node.js';
return 'Node.js';
}
if (await fs.pathExists(path.join(cwd, 'requirements.txt')) ||
await fs.pathExists(path.join(cwd, 'pyproject.toml'))) {
return 'Python';
}
if (await fs.pathExists(path.join(cwd, 'Cargo.toml'))) return 'Rust';
if (await fs.pathExists(path.join(cwd, 'go.mod'))) return 'Go';
if (await fs.pathExists(path.join(cwd, 'pom.xml'))) return 'Java/Maven';
if (await fs.pathExists(path.join(cwd, 'build.gradle'))) return 'Java/Gradle';
if (await fs.pathExists(path.join(cwd, 'Makefile'))) return 'C/C++';
return 'Unknown';
}
async getProjectFiles(maxFiles = 50) {
try {
const files = [];
const extensions = ['.js', '.ts', '.jsx', '.tsx', '.py', '.java', '.cpp', '.c', '.go', '.rs', '.php', '.rb'];
const walkDir = async (dir, depth = 0) => {
if (depth > 3 || files.length >= maxFiles) return;
const items = await fs.readdir(dir);
for (const item of items) {
if (item.startsWith('.') || item === 'node_modules') continue;
const fullPath = path.join(dir, item);
const stat = await fs.stat(fullPath);
if (stat.isDirectory()) {
await walkDir(fullPath, depth + 1);
} else if (extensions.some(ext => item.endsWith(ext))) {
files.push({
name: item,
path: fullPath,
relativePath: path.relative(process.cwd(), fullPath),
size: stat.size,
modified: stat.mtime
});
}
}
};
await walkDir(process.cwd());
return files.slice(0, maxFiles);
} catch (error) {
console.error(chalk.red('ā Failed to get project files:'), error.message);
return [];
}
}
async processPrompt(prompt, options = {}) {
if (!this.model) {
console.log(chalk.red('ā AI Assistant not initialized. Please check your API key.'));
return;
}
// Check if this is an authentication-related query
const authKeywords = /auth|login|register|password|session|jwt|security|vulnerability|bcrypt/i;
if (authKeywords.test(prompt) && !options.skipAuthAudit) {
console.log(chalk.cyan('š Detected authentication-related query. Performing real-time code analysis...'));
await this.performAuthenticationAudit();
console.log(chalk.cyan('\nš¤ Now providing AI analysis based on your actual code:\n'));
}
const spinner = ora('š¤ AI is analyzing your code...').start();
try {
// Build context for the AI
let contextualPrompt = await this.buildContextualPrompt(prompt, options);
// Get AI response
const result = await this.model.generateContent(contextualPrompt);
const response = result.response.text();
spinner.stop();
// Display response
this.displayAIResponse(response, options);
// Extract and execute commands if requested
if (options.execute) {
await this.executeAISuggestions(response);
}
// Add to conversation history
this.conversationHistory.push({
prompt: prompt,
response: response,
timestamp: new Date(),
context: { ...this.currentContext }
});
} catch (error) {
spinner.stop();
console.error(chalk.red('ā AI request failed:'), error.message);
}
}
async buildContextualPrompt(prompt, options) {
let contextualPrompt = `You are Blue Beatle, a sophisticated AI development assistant with deep expertise in software engineering, architecture, and best practices. You provide intelligent, context-aware solutions with professional-grade analysis.
## Your Capabilities:
- Advanced code analysis and optimization
- Architecture design and patterns
- Security vulnerability assessment
- Performance optimization strategies
- Best practices enforcement
- Cross-platform development guidance
- Modern development workflows
## Current Development Context:
- Working Directory: ${this.currentContext.workingDirectory}
- Project Type: ${this.currentContext.projectType || 'Auto-detecting...'}
- Operating System: ${process.platform}
- Project Files: ${this.currentContext.files.slice(0, 10).map(f => f.relativePath).join(', ')}
- Recent Commands: ${this.currentContext.recentCommands.slice(-3).join(', ') || 'None'}
## Response Guidelines:
- Provide detailed, actionable solutions
- Include code examples with explanations
- Consider security and performance implications
- Suggest modern best practices
- Explain the reasoning behind recommendations
- Offer alternative approaches when applicable
`;
// Add file content if specified
if (options.file) {
try {
const filePath = path.resolve(options.file);
const content = await fs.readFile(filePath, 'utf8');
contextualPrompt += `\nFile Content (${options.file}):\n\`\`\`\n${content}\n\`\`\`\n`;
} catch (error) {
contextualPrompt += `\nError reading file ${options.file}: ${error.message}\n`;
}
}
// Add directory analysis if specified
if (options.directory) {
const dirAnalysis = await this.analyzeDirectory(options.directory);
contextualPrompt += `\nDirectory Analysis (${options.directory}):\n${dirAnalysis}\n`;
}
// Add full project context if requested
if (options.context) {
const projectContext = await this.getFullProjectContext();
contextualPrompt += `\nFull Project Context:\n${projectContext}\n`;
}
// Add conversation history
if (this.conversationHistory.length > 0) {
const recentHistory = this.conversationHistory.slice(-3);
contextualPrompt += `\nRecent Conversation:\n${recentHistory.map(h =>
`User: ${h.prompt}\nAssistant: ${h.response.substring(0, 200)}...`
).join('\n\n')}\n`;
}
contextualPrompt += `\nUser Request: ${prompt}
CRITICAL INSTRUCTIONS:
- ALWAYS read and analyze actual files from the user's project
- Provide specific feedback based on the actual code you find
- Show real-time analysis with file paths and line numbers
- When suggesting changes, provide exact code replacements
- Use the file reading capabilities to examine the codebase thoroughly
- Never provide generic examples - always work with the user's actual code
Response Format:
1. Start by reading relevant files from the project
2. Analyze the actual code found
3. Identify specific issues with file paths and line numbers
4. Provide exact code fixes for the user's files
5. Show before/after comparisons when making changes
IMPORTANT: This is a real codebase analysis, not a tutorial. Read the files and work with the actual code.`;
return contextualPrompt;
}
async analyzeDirectory(dirPath) {
try {
const fullPath = path.resolve(dirPath);
const files = await fs.readdir(fullPath);
let analysis = `Directory: ${dirPath}\n`;
analysis += `Files: ${files.length}\n`;
const fileTypes = {};
for (const file of files) {
const ext = path.extname(file);
fileTypes[ext] = (fileTypes[ext] || 0) + 1;
}
analysis += `File Types: ${Object.entries(fileTypes).map(([ext, count]) => `${ext || 'no-ext'}: ${count}`).join(', ')}\n`;
return analysis;
} catch (error) {
return `Error analyzing directory: ${error.message}`;
}
}
async getFullProjectContext() {
try {
let context = `Project Type: ${this.currentContext.projectType}\n`;
context += `Total Files: ${this.currentContext.files.length}\n`;
// Add package.json info if available
const pkgPath = path.join(process.cwd(), 'package.json');
if (await fs.pathExists(pkgPath)) {
const pkg = await fs.readJson(pkgPath);
context += `\nPackage.json:\n`;
context += `- Name: ${pkg.name}\n`;
context += `- Version: ${pkg.version}\n`;
context += `- Dependencies: ${Object.keys(pkg.dependencies || {}).join(', ')}\n`;
context += `- Scripts: ${Object.keys(pkg.scripts || {}).join(', ')}\n`;
}
// Add recent git commits if available
try {
const gitLog = execSync('git log --oneline -5', { encoding: 'utf8', cwd: process.cwd() });
context += `\nRecent Git Commits:\n${gitLog}`;
} catch (error) {
// Git not available or not a git repo
}
return context;
} catch (error) {
return `Error getting project context: ${error.message}`;
}
}
displayAIResponse(response, options) {
const boxen = require('boxen');
const gradient = require('gradient-string');
// Create a professional header
const header = gradient.cristal('š¤ Blue Beatle AI Assistant');
console.log('\n' + boxen(header, {
padding: 1,
margin: 1,
borderStyle: 'round',
borderColor: 'cyan'
}));
// Parse and format the response with classic, clean styling
const sections = response.split('\n\n');
sections.forEach((section, index) => {
if (section.includes('```')) {
// Clean code block formatting
const lines = section.split('\n');
let inCodeBlock = false;
let language = '';
lines.forEach(line => {
if (line.startsWith('```')) {
inCodeBlock = !inCodeBlock;
if (inCodeBlock) {
language = line.replace('```', '').trim();
console.log(chalk.cyan(`\n${language ? language.toUpperCase() + ' ' : ''}CODE:`));
console.log(chalk.gray('ā'.repeat(50)));
} else {
console.log(chalk.gray('ā'.repeat(50)));
}
} else if (inCodeBlock) {
console.log(chalk.green(line));
} else if (line.trim()) {
console.log(chalk.white(line));
}
});
} else if (section.match(/^\d+\./)) {
// Numbered list items - clean formatting
console.log(chalk.white('\n' + section));
} else if (section.includes('SOLUTION:') || section.includes('EXPLANATION:') || section.includes('STEPS:')) {
// Section headers - clean and professional
console.log(chalk.bold.cyan('\n' + section.toUpperCase()));
console.log(chalk.gray('ā'.repeat(30)));
} else if (section.includes('Terminal Commands:') || section.includes('Commands:') || section.includes('$ ')) {
// Command section with clean formatting
console.log(chalk.yellow('\nCOMMANDS:'));
console.log(chalk.gray('ā'.repeat(30)));
console.log(chalk.white(section));
} else if (section.includes('Warning') || section.includes('IMPORTANT') || section.includes('Note:')) {
// Important information with clean formatting
console.log(chalk.red.bold('\nIMPORTANT:'));
console.log(chalk.gray('ā'.repeat(30)));
console.log(chalk.white(section));
} else if (section.includes('Recommendation') || section.includes('Best Practice')) {
// Recommendations with clean formatting
console.log(chalk.green.bold('\nRECOMMENDATION:'));
console.log(chalk.gray('ā'.repeat(30)));
console.log(chalk.white(section));
} else if (section.trim()) {
// Regular text with clean formatting - remove asterisks and format cleanly
const cleanText = section.replace(/\*\*(.*?)\*\*/g, '$1').replace(/\*(.*?)\*/g, '$1');
console.log(chalk.white('\n' + cleanText));
}
});
// Add a professional footer
console.log(chalk.gray('\n' + 'ā'.repeat(60)));
console.log(chalk.cyan('š” Need more help? Use "blue-beatle interactive" for detailed assistance'));
console.log('');
}
async executeAISuggestions(response) {
// Extract terminal commands from the response
const commandRegex = /```(?:bash|shell|cmd)?\n(.*?)\n```/gs;
const commands = [];
let match;
while ((match = commandRegex.exec(response)) !== null) {
const command = match[1].trim();
if (command && !command.startsWith('#') && !command.startsWith('//')) {
commands.push(command);
}
}
if (commands.length === 0) {
console.log(chalk.yellow('ā¹ļø No executable commands found in AI response.'));
return;
}
console.log(chalk.cyan(`\nš§ Found ${commands.length} command(s) to execute:\n`));
commands.forEach((cmd, i) => {
console.log(chalk.gray(`${i + 1}. ${cmd}`));
});
const { shouldExecute } = await inquirer.prompt([{
type: 'confirm',
name: 'shouldExecute',
message: 'Do you want to execute these commands?',
default: false
}]);
if (!shouldExecute) {
console.log(chalk.yellow('āļø Command execution skipped.'));
return;
}
for (const command of commands) {
console.log(chalk.blue(`\nā¶ļø Executing: ${command}`));
try {
const result = execSync(command, {
encoding: 'utf8',
cwd: process.cwd(),
stdio: 'inherit'
});
this.currentContext.recentCommands.push({
command,
timestamp: new Date(),
success: true
});
} catch (error) {
console.error(chalk.red(`ā Command failed: ${error.message}`));
this.currentContext.recentCommands.push({
command,
timestamp: new Date(),
success: false,
error: error.message
});
const { continueExecution } = await inquirer.prompt([{
type: 'confirm',
name: 'continueExecution',
message: 'Continue with remaining commands?',
default: false
}]);
if (!continueExecution) break;
}
}
}
async startInteractiveMode(options = {}) {
const boxen = require('boxen');
const gradient = require('gradient-string');
// Professional welcome message
const welcomeMsg = gradient.pastel.multiline([
'šÆ Blue Beatle Interactive AI Assistant',
'āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā',
'Advanced AI-powered development assistance',
'Context-aware ⢠Intelligent ⢠Professional'
].join('\n'));
console.log('\n' + boxen(welcomeMsg, {
padding: 1,
margin: 1,
borderStyle: 'double',
borderColor: 'cyan'
}));
console.log(chalk.gray('š” Commands: exit, help, clear, context, analyze, security, performance, read, suggest, workspace'));
console.log(chalk.gray('š Powerful features: Real-time analysis, File editing, Code intelligence, Git integration\n'));
while (true) {
try {
const { input } = await inquirer.prompt([{
type: 'input',
name: 'input',
message: chalk.cyan('Blue Beatle āÆ'),
prefix: ''
}]);
if (input.toLowerCase() === 'exit') {
console.log(chalk.yellow('š Goodbye!'));
break;
}
if (input.toLowerCase() === 'clear') {
this.conversationHistory = [];
console.log(chalk.green('ā
Conversation history cleared.'));
continue;
}
if (input.toLowerCase() === 'help') {
this.showInteractiveHelp();
continue;
}
if (input.toLowerCase() === 'context') {
await this.updateContext();
console.log(chalk.green('ā
Context updated.'));
console.log(chalk.gray(`Project: ${this.currentContext.projectType}, Files: ${this.currentContext.files.length}`));
continue;
}
if (input.toLowerCase() === 'analyze') {
await this.processPrompt('Perform a comprehensive analysis of this project including code quality, architecture, and potential improvements', { context: true });
continue;
}
if (input.toLowerCase() === 'security') {
await this.processPrompt('Conduct a security audit of this project and identify potential vulnerabilities', { context: true });
continue;
}
if (input.toLowerCase() === 'performance') {
await this.processPrompt('Analyze the performance characteristics of this project and suggest optimizations', { context: true });
continue;
}
if (input.toLowerCase().startsWith('review ')) {
const filePath = input.substring(7).trim();
await this.processPrompt(`Please review this file for code quality, best practices, and potential improvements`, { file: filePath });
continue;
}
if (input.toLowerCase().startsWith('read ')) {
const filePath = input.substring(5).trim();
try {
const fileInfo = await this.readFileWithContext(filePath);
console.log(chalk.cyan(`\nš FILE: ${fileInfo.path}`));
console.log(chalk.gray(`Size: ${fileInfo.size} bytes | Lines: ${fileInfo.lines} | Language: ${fileInfo.language}`));
console.log(chalk.gray('ā'.repeat(50)));
console.log(chalk.white(fileInfo.content));
console.log(chalk.gray('ā'.repeat(50)));
} catch (error) {
console.log(chalk.red(`ā ${error.message}`));
}
continue;
}
if (input.toLowerCase().startsWith('analyze-file ')) {
const filePath = input.substring(13).trim();
try {
const analysis = await this.analyzeCodeFile(filePath);
console.log(chalk.cyan(`\nš CODE ANALYSIS: ${analysis.path}`));
console.log(chalk.gray('ā'.repeat(50)));
console.log(chalk.white(`Language: ${analysis.language}`));
console.log(chalk.white(`Lines: ${analysis.lines}`));
console.log(chalk.white(`Complexity: ${analysis.complexity}`));
console.log(chalk.white(`Functions: ${analysis.metrics.functions}`));
console.log(chalk.white(`Classes: ${analysis.metrics.classes}`));
console.log(chalk.white(`Imports: ${analysis.metrics.imports}`));
if (analysis.issues.length > 0) {
console.log(chalk.red(`Issues: ${analysis.issues.join(', ')}`));
}
console.log(chalk.gray('ā'.repeat(50)));
} catch (error) {
console.log(chalk.red(`ā ${error.message}`));
}
continue;
}
if (input.toLowerCase().startsWith('suggest ')) {
const parts = input.substring(8).trim().split(' ');
const filePath = parts[0];
const requirements = parts.slice(1).join(' ') || 'general improvements';
try {
console.log(chalk.cyan(`\nš¤ Analyzing ${filePath} and generating suggestions...`));
const suggestions = await this.suggestFileChanges(filePath, requirements);
console.log(chalk.cyan(`\nš” SUGGESTIONS FOR: ${suggestions.filePath}`));
console.log(chalk.gray('ā'.repeat(50)));
this.displayAIResponse(suggestions.suggestions, {});
if (suggestions.canAutoApply.length > 0) {
console.log(chalk.yellow(`\nā” Found ${suggestions.canAutoApply.length} auto-applicable changes`));
const { apply } = await inquirer.prompt([{
type: 'confirm',
name: 'apply',
message: 'Would you like to apply these changes automatically?',
default: false
}]);
if (apply) {
// This would need more sophisticated implementation
console.log(chalk.green('ā
Changes would be applied (implementation needed)'));
}
}
} catch (error) {
console.log(chalk.red(`ā ${error.message}`));
}
continue;
}
if (input.toLowerCase().startsWith('check-auth')) {
const projectPath = input.substring(10).trim() || process.cwd();
await this.performAuthenticationAudit(projectPath);
continue;
}
if (input.toLowerCase() === 'workspace') {
console.log(chalk.cyan('\nšļø WORKSPACE ANALYSIS'));
console.log(chalk.gray('ā'.repeat(50)));
if (this.currentContext.workspaceStructure) {
const ws = this.currentContext.workspaceStructure;
console.log(chalk.white(`Total Files: ${ws.totalFiles}`));
console.log(chalk.white(`Total Directories: ${ws.directories.length}`));
console.log(chalk.white(`Total Size: ${(ws.totalSize / 1024).toFixed(2)} KB`));
console.log(chalk.white(`Complexity: ${ws.complexity}`));
console.log(chalk.cyan('\nFile Types:'));
Object.entries(ws.fileTypes).forEach(([ext, count]) => {
console.log(chalk.gray(` ${ext || 'no-ext'}: ${count} files`));
});
}
if (this.currentContext.gitInfo?.isRepo) {
const git = this.currentContext.gitInfo;
console.log(chalk.cyan('\nGit Information:'));
console.log(chalk.white(`Branch: ${git.branch || 'unknown'}`));
console.log(chalk.white(`Status: ${git.status || 'unknown'}`));
if (git.lastCommit) {
console.log(chalk.white(`Last Commit: ${git.lastCommit}`));
}
}
if (this.currentContext.dependencies) {
const deps = this.currentContext.dependencies;
console.log(chalk.cyan('\nDependencies:'));
console.log(chalk.white(`Production: ${Object.keys(deps.production).length}`));
console.log(chalk.white(`Development: ${Object.keys(deps.development).length}`));
if (deps.vulnerabilities.length > 0) {
console.log(chalk.red(`Potential Issues: ${deps.vulnerabilities.length}`));
}
}
continue;
}
if (input.trim()) {
await this.processPrompt(input, options);
}
} catch (error) {
if (error.isTtyError) {
console.log(chalk.yellow('š Interactive mode ended.'));
break;
} else {
console.error(chalk.red('ā Error:'), error.message);
}
}
}
}
showInteractiveHelp() {
const Table = require('cli-table3');
console.log(chalk.cyan('\nš Blue Beatle Interactive Commands\n'));
const table = new Table({
head: [chalk.cyan('Command'), chalk.cyan('Description'), chalk.cyan('Example')],
colWidths: [15, 35, 25],
style: {
head: ['cyan'],
border: ['gray']
}
});
table.push(
['exit', 'Exit interactive mode', 'exit'],
['clear', 'Clear conversation history', 'clear'],
['context', 'Update project context', 'context'],
['analyze', 'Full project analysis', 'analyze'],
['security', 'Security audit', 'security'],
['performance', 'Performance analysis', 'performance'],
['review <file>', 'Review specific file', 'review src/app.js'],
['read <file>', 'Read and display file content', 'read package.json'],
['analyze-file <file>', 'Analyze code file metrics', 'analyze-file src/app.js'],
['suggest <file> [req]', 'AI-powered file improvements', 'suggest app.js performance'],
['check-auth [path]', 'Real-time authentication audit', 'check-auth ./server'],
['workspace', 'Show workspace analysis', 'workspace'],
['help', 'Show this help', 'help']
);
console.log(table.toString());
console.log(chalk.yellow('\nšÆ Powerful Features:'));
console.log(chalk.gray('⢠Real-time workspace analysis and monitoring'));
console.log(chalk.gray('⢠Intelligent code reading and understanding'));
console.log(chalk.gray('⢠AI-powered file editing suggestions'));
console.log(chalk.gray('⢠Automatic code quality assessment'));
console.log(chalk.gray('⢠Security vulnerability detection'));
console.log(chalk.gray('⢠Performance optimization recommendations'));
console.log(chalk.gray('⢠Git integration and project insights'));
console.log(chalk.gray('⢠Dependency analysis and management'));
console.log(chalk.blue('\nš” Pro Tips:'));
console.log(chalk.gray('⢠Use "read filename" to view any file with syntax highlighting'));
console.log(chalk.gray('⢠Use "analyze-file filename" for detailed code metrics'));
console.log(chalk.gray('⢠Use "suggest filename requirements" for AI improvements'));
console.log(chalk.gray('⢠Use "workspace" to see complete project overview'));
console.log(chalk.gray('⢠Ask specific questions for context-aware responses\n'));
}
async learnMode(topic, options = {}) {
const spinner = ora(`š Preparing learning material for: ${topic}`).start();
try {
const learningPrompt = `You are an expert programming tutor. Create a comprehensive learning guide for the topic: "${topic}".
Learning Level: ${options.level}
Include Examples: ${options.examples ? 'Yes' : 'No'}
Interactive Quiz: ${options.quiz ? 'Yes' : 'No'}
Please provide:
1. Clear explanation of the concept
2. Key points and principles
3. ${options.examples ? 'Practical code examples' : 'Theoretical overview'}
4. Common pitfalls and how to avoid them
5. Best practices
6. ${options.quiz ? 'Interactive quiz questions' : 'Further reading suggestions'}
Format the response with clear sections and use markdown for code blocks.`;
const result = await this.model.generateContent(learningPrompt);
const response = result.response.text();
spinner.stop();
this.displayAIResponse(response, options);
if (options.quiz) {
await this.runInteractiveQuiz(response);
}
} catch (error) {
spinner.stop();
console.error(chalk.red('ā Learning mode failed:'), error.message);
}
}
async runInteractiveQuiz(learningContent) {
console.log(chalk.cyan('\nš Starting Interactive Quiz!\n'));
// Extract quiz questions from the learning content
const quizRegex = /(?:Quiz|Question)\s*\d*[:.]\s*(.*?)(?=(?:Quiz|Question)\s*\d*[:.|\n\n]|$)/gs;
const questions = [];
let match;
while ((match = quizRegex.exec(learningContent)) !== null) {
questions.push(match[1].trim());
}
if (questions.length === 0) {
console.log(chalk.yellow('ā¹ļø No quiz questions found in the learning content.'));
return;
}
let score = 0;
for (let i = 0; i < questions.length; i++) {
console.log(chalk.blue(`\nQuestion ${i + 1}: ${questions[i]}`));
const { answer } = await inquirer.prompt([{
type: 'input',
name: 'answer',
message: 'Your answer:'
}]);
// Get AI evaluation of the answer
const evaluationPrompt = `Evaluate this answer to the question: "${questions[i]}"
Student Answer: "${answer}"
Please provide:
1. Whether the answer is correct (Yes/No)
2. Brief explanation
3. Correct answer if the student was wrong
4. Encouragement or tips
Keep the response concise and educational.`;
try {
const evaluation = await this.model.generateContent(evaluationPrompt);
const feedback = evaluation.response.text();
console.log(chalk.green('\nš Feedback:'));
console.log(feedback);
// Simple scoring based on AI feedback
if (feedback.toLowerCase().includes('correct') || feedback.toLowerCase().includes('yes')) {
score++;
console.log(chalk.green('ā
Correct!'));
} else {
console.log(chalk.red('ā Incorrect'));
}
} catch (error) {
console.log(chalk.yellow('ā ļø Could not evaluate answer automatically.'));
}
}
console.log(chalk.cyan(`\nš Quiz Complete! Score: ${score}/${questions.length}`));
if (score === questions.length) {
console.log(chalk.green('š Perfect score! Excellent work!'));
} else if (score >= questions.length * 0.7) {
console.log(chalk.yellow('š Good job! Keep practicing!'));
} else {
console.log(chalk.blue('š Keep learning! Review the material and try again.'));
}
}
async debugMode(file, options = {}) {
console.log(chalk.cyan('š Starting Debug Mode'));
let debugContext = `Debug Session Started
Working Directory: ${process.cwd()}
Target File: ${file || 'Not specified'}
`;
if (file) {
try {
const content = await fs.readFile(file, 'utf8');
debugContext += `\nFile Content:\n\`\`\`\n${content}\n\`\`\`\n`;
} catch (error) {
debugContext += `\nError reading file: ${error.message}\n`;
}
}
if (options.error) {
debugContext += `\nError Message: ${options.error}\n`;
}
if (options.logs) {
try {
const logs = await fs.readFile(options.logs, 'utf8');
debugContext += `\nLog File Content:\n\`\`\`\n${logs.slice(-2000)}\n\`\`\`\n`;
} catch (error) {
debugContext += `\nError reading log file: ${error.message}\n`;
}
}
const debugPrompt = `${debugContext}
You are an expert debugger. Please analyze the provided information and help debug the issue.
Provide:
1. Problem identification
2. Possible causes
3. Step-by-step debugging approach
4. Suggested fixes
5. Prevention strategies
Be specific and actionable in your recommendations.`;
await this.processPrompt(debugPrompt, { execute: false });
}
async generateCode(type, options = {}) {
const spinner = ora(`šØ Generating ${type} code...`).start();
try {
let generationPrompt = `Generate ${type} code with the following specifications:
Type: ${type}
Name: ${options.name || 'Unnamed'}
Framework: ${options.framework || 'None specified'}
Language: ${options.language || 'Auto-detect from project'}
Template: ${options.template || 'Standard'}
Project Context:
- Project Type: ${this.currentContext.projectType}
- Working Directory: ${this.currentContext.workingDirectory}
Please provide:
1. Complete, working code
2. Explanation of the code structure
3. Usage examples
4. Any required dependencies
5. Setup instructions if needed
Make the code production-ready with proper error handling, comments, and best practices.`;
const result = await this.model.generateContent(generationPrompt);
const response = result.response.text();
spinner.stop();
this.displayAIResponse(response, options);
// Offer to save the generated code
const { shouldSave } = await inquirer.prompt([{
type: 'confirm',
name: 'shouldSave',
message: 'Would you like to save the generated code to a file?',
default: true
}]);
if (shouldSave) {
await this.saveGeneratedCode(response, type, options);
}
} catch (error) {
spinner.stop();
console.error(chalk.red('ā Code generation failed:'), error.message);
}
}
async saveGeneratedCode(response, type, options) {
// Extract code blocks from the response
const codeRegex = /```(?:(\w+))?\n(.*?)\n```/gs;
const codeBlocks = [];
let match;
while ((match = codeRegex.exec(response)) !== null) {
codeBlocks.push({
language: match[1] || 'text',
code: match[2]
});
}
if (codeBlocks.length === 0) {
console.log(chalk.yellow('ā¹ļø No code blocks found to save.'));
return;
}
for (let i = 0; i < codeBlocks.length; i++) {
const block = codeBlocks[i];
const extension = this.getFileExtension(block.language);
const defaultName = `${options.name || type}-${i + 1}${extension}`;
const { filename } = await inquirer.prompt([{
type: 'input',
name: 'filename',
message: `Enter filename for code block ${i + 1}:`,
default: defaultName
}]);
try {
await fs.writeFile(filename, block.code);
console.log(chalk.green(`ā
Code saved to: ${filename}`));
} catch (error) {
console.error(chalk.red(`ā Failed to save ${filename}:`), error.message);
}
}
}
getFileExtension(language) {
const extensions = {
javascript: '.js',
typescript: '.ts',
python: '.py',
java: '.java',
cpp: '.cpp',
c: '.c',
go: '.go',
rust: '.rs',
php: '.php',
ruby: '.rb',
html: '.html',
css: '.css',
json: '.json',
yaml: '.yml',
xml: '.xml',
sql: '.sql',
bash: '.sh',
powershell: '.ps1'
};
return extensions[language.toLowerCase()] || '.txt';
}
// ===== POWERFUL WORKSPACE ANALYSIS METHODS =====
async analyzeWorkspaceStructure() {
try {
const structure = {
directories: [],
fileTypes: {},
totalFiles: 0,
totalSize: 0,
complexity: 'low'
};
const walkDir = async (dir, depth = 0) => {
if (depth > 5) return; // Prevent infinite recursion
const items = await fs.readdir(dir);
for (const item of items) {
if (item.startsWith('.') || item === 'node_modules') continue;
const fullPath = path.join(dir, item);
const stat = await fs.stat(fullPath);
if (stat.isDirectory()) {
structure.directories.push({
name: item,
path: fullPath,
relativePath: path.relative(process.cwd(), fullPath),
depth
});
await walkDir(fullPath, depth + 1);
} else {
const ext = path.extname(item);
structure.fileTypes[ext] = (structure.fileTypes[ext] || 0) + 1;
structure.totalFiles++;
structure.totalSize += stat.size;
}
}
};
await walkDir(process.cwd());
// Determine complexity
if (structure.totalFiles > 100 || structure.directories.length > 20) {
structure.complexity = 'high';
} else if (structure.totalFiles > 50 || structure.directories.length > 10) {
structure.complexity = 'medium';
}
return structure;
} catch (error) {
console.error(chalk.red('ā Failed to analyze workspace structure:'), error.message);
return null;
}
}
async getGitInfo() {
try {
const { execSync } = require('child_process');
const gitInfo = {
isRepo: false,
branch: null,
remoteUrl: null,
lastCommit: null,
status: null
};
// Check if it's a git repository
try {
execSync('git rev-parse --git-dir', { stdio: 'ignore' });
gitInfo.isRepo = true;
} catch {
return gitInfo;
}
// Get current branch
try {
gitInfo.branch = execSync('git branch --show-current', { encoding: 'utf8' }).trim();
} catch {}
// Get remote URL
try {
gitInfo.remoteUrl = execSync('git config --get remote.origin.url', { encoding: 'utf8' }).trim();
} catch {}
// Get last commit
try {
gitInfo.lastCommit = execSync('git log -1 --pretty=format:"%h - %s (%cr)"', { encoding: 'utf8' }).trim();
} catch {}
// Get status
try {
const status = execSync('git status --porcelain', { encoding: 'utf8' });
gitInfo.status = status ? 'dirty' : 'clean';
} catch {}
return gitInfo;
} catch (error) {
return { isRepo: false, error: error.message };
}
}
async analyzeDependencies() {
try {
const dependencies = {
production: {},
development: {},
outdated: [],
vulnerabilities: [],
totalCount: 0
};
// Analyze package.json
const pkgPath = path.join(process.cwd(), 'package.json');
if (await fs.pathExists(pkgPath)) {
const pkg = await fs.readJson(pkgPath);
dependencies.production = pkg.dependencies || {};
dependencies.development = pkg.devDependencies || {};
dependencies.totalCount = Object.keys(dependencies.production).length +
Object.keys(dependencies.development).length;
// Check for common vulnerable packages
const vulnerablePackages = ['lodash', 'moment', 'request', 'node-sass'];
for (const vuln of vulnerablePackages) {
if (dependencies.production[vuln] || dependencies.development[vuln]) {
dependencies.vulnerabilities.push({
package: vuln,
reason: 'Known security issues - consider alternatives'
});
}
}
}
return dependencies;
} catch (error) {
console.error(chalk.red('ā Failed to analyze dependencies:'), error.message);
return null;
}
}
async setupFileWatcher() {
try {
if (this.fileWatcher) {
this.fileWatcher.close();
}
const chokidar = require('chokidar');
this.fileWatcher = chokidar.watch(process.cwd(), {
ignored: /(^|[\/\\])\../, // ignore dotfiles
ignoreInitial: true,
depth: 3
});
this.fileWatcher.on('change', (filePath) => {
this.currentContext.recentCommands.unshift(`File changed: ${path.relative(process.cwd(), filePath)}`);
if (this.currentContext.recentCommands.length > 10) {
this.currentContext.recentCommands.pop();