diff --git a/package.json b/package.json index a9eb2f5..eb3f538 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "neuraterm", - "version": "1.0.0", - "description": "Terminal IA professionnel avec support multi-providers (ChatGPT, Mistral) et suivi des coûts", + "version": "2.0.0", + "description": "Terminal IA professionnel autonome avec architecture C4, support multi-providers et capacités d'exécution avancées", "main": "dist/cli.js", "type": "module", "bin": { @@ -23,7 +23,12 @@ "chatgpt", "mistral", "openai", - "professional" + "professional", + "automation", + "c4-architecture", + "autonomous", + "command-execution", + "task-planning" ], "author": "NeuraTerm Team", "license": "MIT", diff --git a/src/core/engine.ts b/src/core/engine.ts new file mode 100644 index 0000000..d88eef9 --- /dev/null +++ b/src/core/engine.ts @@ -0,0 +1,243 @@ +/** + * Engine - Actions fondamentales protégées de NeuraTerm + * Équivalent du "Cœur" de ZeroTwo, adapté pour le contexte professionnel + */ + +import { CommandExecutor, CommandResult } from '../execution/commandExecutor.js'; +import { AIClient } from '../ai/client.js'; +import { logger } from '../utils/logger.js'; + +export interface EngineResult { + success: boolean; + data?: any; + error?: string; + metadata?: { + duration: number; + provider?: string; + tokensUsed?: number; + cost?: number; + }; +} + +export class NeuraEngine { + private commandExecutor: CommandExecutor; + private securityLevel: 'strict' | 'balanced' | 'permissive'; + + constructor( + private aiClient: AIClient, + commandExecutor: CommandExecutor, + private config: any + ) { + this.commandExecutor = commandExecutor; + this.securityLevel = config.security?.level || 'balanced'; + logger.info('🔧 NeuraEngine initialisé avec sécurité:', this.securityLevel); + } + + /** + * Exécution sécurisée de commandes système + */ + async executeSecureCommand(command: string, options: { + timeout?: number; + workingDir?: string; + requireConfirmation?: boolean; + } = {}): Promise { + const startTime = Date.now(); + + try { + // Validation de sécurité renforcée + if (!this.validateCommandSecurity(command)) { + throw new Error(`Commande refusée par le système de sécurité: ${command}`); + } + + // Confirmation utilisateur si requise + if (options.requireConfirmation && !await this.requestUserConfirmation(command)) { + throw new Error('Exécution annulée par l\'utilisateur'); + } + + // Exécution protégée + const result = await this.commandExecutor.executeCommand(command, { + timeout: options.timeout || 30000, + cwd: options.workingDir, + maxOutputLength: 50000 + }); + + const duration = Date.now() - startTime; + + return { + success: result.success, + data: { + stdout: result.stdout, + stderr: result.stderr, + command: result.command, + exitCode: result.exitCode + }, + metadata: { + duration, + provider: 'system' + } + }; + + } catch (error) { + const duration = Date.now() - startTime; + logger.error('Erreur Engine executeSecureCommand:', error); + + return { + success: false, + error: error instanceof Error ? error.message : String(error), + metadata: { duration } + }; + } + } + + /** + * Communication IA avec contexte professionnel + */ + async queryAI(prompt: string, context: { + systemRole?: string; + temperature?: number; + maxTokens?: number; + provider?: string; + } = {}): Promise { + const startTime = Date.now(); + + try { + const systemRole = context.systemRole || + 'Tu es NeuraTerm, un assistant IA professionnel spécialisé dans l\'automatisation et l\'analyse système. ' + + 'Réponds de manière précise, technique et orientée solutions d\'entreprise.'; + + const response = await this.aiClient.generateResponse({ + messages: [ + { role: 'system', content: systemRole }, + { role: 'user', content: prompt } + ], + temperature: context.temperature || 0.3, + maxTokens: context.maxTokens + }, context.provider); + + const duration = Date.now() - startTime; + + return { + success: true, + data: { + content: response.content, + usage: response.usage, + model: response.model + }, + metadata: { + duration, + provider: response.provider, + tokensUsed: response.usage.totalTokens, + cost: response.cost.totalCost + } + }; + + } catch (error) { + const duration = Date.now() - startTime; + logger.error('Erreur Engine queryAI:', error); + + return { + success: false, + error: error instanceof Error ? error.message : String(error), + metadata: { duration } + }; + } + } + + /** + * Auto-questionnement IA pour amélioration continue + */ + async selfReflection(lastAction: EngineResult, userFeedback?: string): Promise { + const reflectionPrompt = ` +Analyse cette dernière action NeuraTerm: +- Succès: ${lastAction.success} +- Durée: ${lastAction.metadata?.duration}ms +- Erreur: ${lastAction.error || 'Aucune'} +${userFeedback ? `- Feedback utilisateur: ${userFeedback}` : ''} + +Questions d'auto-amélioration: +1. Cette action était-elle optimale ? +2. Y a-t-il des améliorations possibles ? +3. Des patterns récurrents d'erreur ? +4. Suggestions pour les prochaines actions ? + +Réponds par une analyse concise orientée amélioration continue. +`; + + return await this.queryAI(reflectionPrompt, { + systemRole: 'Tu es un système d\'auto-évaluation pour NeuraTerm. Analyse et propose des améliorations.', + temperature: 0.2 + }); + } + + /** + * Validation de sécurité renforcée + */ + private validateCommandSecurity(command: string): boolean { + const cmd = command.trim().toLowerCase(); + + // Commandes dangereuses selon le niveau de sécurité + const dangerousCommands = { + strict: [ + 'rm', 'del', 'delete', 'format', 'fdisk', 'mkfs', + 'sudo', 'su', 'passwd', 'chmod 777', 'chown', + 'shutdown', 'reboot', 'halt', 'poweroff', + 'dd', 'netcat', 'nc', 'telnet', 'ssh', 'scp', + 'iptables', 'firewall', 'crontab', 'systemctl', + 'killall', 'pkill', 'kill -9' + ], + balanced: [ + 'rm -rf', 'del /f', 'format', 'fdisk', 'mkfs', + 'sudo rm', 'passwd', 'chmod 777', + 'shutdown', 'reboot', 'halt', + 'dd if=', 'iptables', 'systemctl stop' + ], + permissive: [ + 'rm -rf /', 'del /f /q', 'format c:', + 'fdisk /dev', 'mkfs.ext4', + 'sudo rm -rf', 'passwd root' + ] + }; + + const blockedCommands = dangerousCommands[this.securityLevel]; + + return !blockedCommands.some(dangerous => + cmd.includes(dangerous) || cmd.startsWith(dangerous) + ); + } + + /** + * Demande de confirmation utilisateur pour actions sensibles + */ + private async requestUserConfirmation(command: string): Promise { + // Pour l'instant, on accepte automatiquement + // TODO: Implémenter une vraie demande de confirmation interactive + logger.warn(`Commande nécessitant confirmation: ${command}`); + return true; + } + + /** + * Obtenir les métriques de performance + */ + getPerformanceMetrics(): { + commandsExecuted: number; + aiQueriesExecuted: number; + averageResponseTime: number; + errorRate: number; + } { + // TODO: Implémenter le tracking des métriques + return { + commandsExecuted: 0, + aiQueriesExecuted: 0, + averageResponseTime: 0, + errorRate: 0 + }; + } + + /** + * Configuration du niveau de sécurité + */ + setSecurityLevel(level: 'strict' | 'balanced' | 'permissive'): void { + this.securityLevel = level; + logger.info(`Niveau de sécurité modifié: ${level}`); + } +} \ No newline at end of file diff --git a/src/core/identity.ts b/src/core/identity.ts new file mode 100644 index 0000000..8763836 --- /dev/null +++ b/src/core/identity.ts @@ -0,0 +1,252 @@ +/** + * Identity - Identité et personnalité de NeuraTerm + * Équivalent "Personnalité" de ZeroTwo, adapté pour le contexte professionnel + */ + +export interface NeuraPersonality { + name: string; + version: string; + description: string; + expertise: string[]; + communicationStyle: { + tone: 'professional' | 'friendly' | 'technical'; + verbosity: 'concise' | 'detailed' | 'comprehensive'; + language: string; + }; + behaviors: { + proactive: boolean; + cautious: boolean; + learningOriented: boolean; + securityFocused: boolean; + }; + capabilities: string[]; + limitations: string[]; +} + +export class NeuraIdentity { + private readonly personality: NeuraPersonality; + + constructor() { + this.personality = { + name: 'NeuraTerm', + version: '2.0.0', + description: 'Assistant IA professionnel spécialisé dans l\'automatisation système et l\'analyse de données', + + expertise: [ + 'Automatisation système et scripts', + 'Analyse de performance et optimisation', + 'Sécurité informatique et audit', + 'DevOps et intégration continue', + 'Gestion de données et analytics', + 'Architecture logicielle et microservices', + 'Cloud computing et infrastructure', + 'Monitoring et observabilité' + ], + + communicationStyle: { + tone: 'professional', + verbosity: 'concise', + language: 'fr' + }, + + behaviors: { + proactive: true, // Propose des améliorations + cautious: true, // Valide avant actions dangereuses + learningOriented: true, // Apprend des interactions + securityFocused: true // Privilégie la sécurité + }, + + capabilities: [ + 'Exécution sécurisée de commandes système', + 'Planification automatique de tâches complexes', + 'Analyse de logs et métriques en temps réel', + 'Génération de rapports détaillés', + 'Optimisation de performance système', + 'Détection d\'anomalies et alertes', + 'Recommandations d\'architecture', + 'Automatisation de workflows DevOps' + ], + + limitations: [ + 'Ne peut pas exécuter de commandes dangereuses sans confirmation', + 'Accès limité aux systèmes de production sans autorisation', + 'Ne modifie pas les configurations critiques automatiquement', + 'Respecte les politiques de sécurité de l\'entreprise', + 'Nécessite des clés API valides pour fonctionner' + ] + }; + } + + /** + * Obtenir la personnalité complète + */ + getPersonality(): NeuraPersonality { + return { ...this.personality }; + } + + /** + * Générer le prompt système contextuel + */ + generateSystemPrompt(context?: { + task?: string; + securityLevel?: string; + userRole?: string; + }): string { + const basePrompt = `Tu es ${this.personality.name} v${this.personality.version}, ${this.personality.description}. + +🎯 EXPERTISE PRINCIPALE: +${this.personality.expertise.map(skill => `• ${skill}`).join('\n')} + +🎭 STYLE DE COMMUNICATION: +• Ton: ${this.personality.communicationStyle.tone} +• Niveau de détail: ${this.personality.communicationStyle.verbosity} +• Toujours en français professionnel + +🔧 CAPACITÉS TECHNIQUES: +${this.personality.capabilities.map(cap => `• ${cap}`).join('\n')} + +⚠️ LIMITATIONS IMPORTANTES: +${this.personality.limitations.map(limit => `• ${limit}`).join('\n')} + +🎯 COMPORTEMENTS CLÉS: +• ${this.personality.behaviors.proactive ? 'Proactif' : 'Réactif'} - ${this.personality.behaviors.proactive ? 'Propose des améliorations' : 'Répond aux demandes'} +• ${this.personality.behaviors.cautious ? 'Prudent' : 'Direct'} - ${this.personality.behaviors.cautious ? 'Valide les actions sensibles' : 'Exécute directement'} +• ${this.personality.behaviors.securityFocused ? 'Sécurisé' : 'Flexible'} - ${this.personality.behaviors.securityFocused ? 'Privilégie la sécurité' : 'Privilégie l\'efficacité'} +• ${this.personality.behaviors.learningOriented ? 'Apprenant' : 'Statique'} - ${this.personality.behaviors.learningOriented ? 'Apprend et s\'améliore' : 'Fonctionnement fixe'}`; + + // Ajouter le contexte spécifique si fourni + if (context) { + let contextualPrompt = basePrompt + '\n\n🎯 CONTEXTE ACTUEL:\n'; + + if (context.task) { + contextualPrompt += `• Tâche: ${context.task}\n`; + } + + if (context.securityLevel) { + contextualPrompt += `• Niveau de sécurité: ${context.securityLevel}\n`; + } + + if (context.userRole) { + contextualPrompt += `• Rôle utilisateur: ${context.userRole}\n`; + } + + return contextualPrompt; + } + + return basePrompt; + } + + /** + * Générer une présentation personnalisée + */ + generateIntroduction(): string { + return ` +╔══════════════════════════════════════════════════════════════╗ +║ 🧠 ${this.personality.name} v${this.personality.version} ║ +║ ${this.personality.description} ║ +╚══════════════════════════════════════════════════════════════╝ + +🎯 SPÉCIALISATIONS: +${this.personality.expertise.slice(0, 4).map(skill => ` • ${skill}`).join('\n')} + • Et bien plus encore... + +💡 CAPACITÉS UNIQUES: + • Planification automatique de tâches complexes + • Exécution sécurisée avec validation intelligente + • Apprentissage continu de vos habitudes de travail + • Support multi-providers (OpenAI, Mistral) avec optimisation des coûts + +🔒 SÉCURITÉ AVANCÉE: + • Validation multi-niveaux des commandes + • Sandboxing des exécutions sensibles + • Audit trail complet de toutes les actions + +Tapez 'help' pour découvrir toutes mes capacités ou posez-moi directement vos questions ! +`; + } + + /** + * Obtenir une réaction contextuelle + */ + getContextualResponse(situation: 'success' | 'error' | 'warning' | 'info'): string { + const responses = { + success: [ + '✅ Parfait ! Tâche accomplie avec succès.', + '🎯 Excellent ! Objectif atteint.', + '✨ Superbe ! Exécution réussie.', + '🚀 Fantastique ! Mission accomplie.' + ], + error: [ + '❌ Oups ! Une erreur s\'est produite. Analysons le problème...', + '🔍 Hmm, quelque chose n\'a pas fonctionné. Diagnostiquons ensemble.', + '⚠️ Erreur détectée. Passons en mode résolution de problème.', + '🛠️ Problème identifié. Explorons les solutions possibles.' + ], + warning: [ + '⚠️ Attention ! Cette action nécessite de la prudence.', + '🔔 Note importante à considérer avant de continuer.', + '👀 Point à surveiller pour éviter les complications.', + '💡 Conseil de sécurité à prendre en compte.' + ], + info: [ + '💡 Information utile à retenir.', + '📝 Point intéressant à noter.', + '🎯 Donnée pertinente pour votre contexte.', + '📊 Insight qui pourrait vous être utile.' + ] + }; + + const options = responses[situation]; + return options[Math.floor(Math.random() * options.length)]; + } + + /** + * Adapter le style selon le contexte + */ + adaptStyle(context: { + userExperience?: 'beginner' | 'intermediate' | 'expert'; + urgency?: 'low' | 'medium' | 'high'; + complexity?: 'simple' | 'moderate' | 'complex'; + }): Partial { + const adaptedStyle: Partial = {}; + + // Adapter la verbosité selon l'expérience + if (context.userExperience) { + switch (context.userExperience) { + case 'beginner': + adaptedStyle.verbosity = 'comprehensive'; + break; + case 'intermediate': + adaptedStyle.verbosity = 'detailed'; + break; + case 'expert': + adaptedStyle.verbosity = 'concise'; + break; + } + } + + // Adapter le ton selon l'urgence + if (context.urgency) { + switch (context.urgency) { + case 'high': + adaptedStyle.tone = 'technical'; + break; + case 'medium': + adaptedStyle.tone = 'professional'; + break; + case 'low': + adaptedStyle.tone = 'friendly'; + break; + } + } + + return adaptedStyle; + } + + /** + * Obtenir la signature NeuraTerm + */ + getSignature(): string { + return `\n─ ${this.personality.name} v${this.personality.version} | Assistant IA Professionnel ─`; + } +} \ No newline at end of file diff --git a/src/core/memory.ts b/src/core/memory.ts new file mode 100644 index 0000000..e87e3bb --- /dev/null +++ b/src/core/memory.ts @@ -0,0 +1,358 @@ +/** + * Memory - Système de mémoire adaptative pour NeuraTerm + * Équivalent du "Cerveau" de ZeroTwo, adapté pour le contexte professionnel + */ + +import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs'; +import { join, dirname } from 'path'; +import { homedir } from 'os'; +import { logger } from '../utils/logger.js'; + +export interface MemoryEntry { + id: string; + timestamp: Date; + type: 'command' | 'query' | 'plan' | 'result' | 'feedback' | 'system'; + content: any; + metadata?: { + success?: boolean; + duration?: number; + cost?: number; + provider?: string; + tags?: string[]; + }; +} + +export interface ConversationContext { + recentEntries: MemoryEntry[]; + workingDirectory: string; + activeProvider: string; + sessionStats: { + commandsExecuted: number; + queriesExecuted: number; + totalCost: number; + sessionStart: Date; + }; +} + +export class NeuraMemory { + private memoryFile: string; + private cacheFile: string; + private memory: MemoryEntry[] = []; + private cache: Map = new Map(); + private maxMemoryEntries: number; + + constructor(private config: any) { + const dataDir = join(homedir(), '.neuraterm', 'data'); + if (!existsSync(dataDir)) { + mkdirSync(dataDir, { recursive: true }); + } + + this.memoryFile = join(dataDir, 'memory.json'); + this.cacheFile = join(dataDir, 'cache.json'); + this.maxMemoryEntries = config.memory?.maxEntries || 1000; + + this.loadMemory(); + this.loadCache(); + + logger.info(`💾 NeuraMemory initialisée avec ${this.memory.length} entrées`); + } + + /** + * Ajouter une entrée en mémoire + */ + addEntry(type: MemoryEntry['type'], content: any, metadata?: MemoryEntry['metadata']): string { + const entry: MemoryEntry = { + id: this.generateId(), + timestamp: new Date(), + type, + content, + metadata + }; + + this.memory.unshift(entry); + + // Limiter la taille de la mémoire + if (this.memory.length > this.maxMemoryEntries) { + this.memory = this.memory.slice(0, this.maxMemoryEntries); + } + + this.saveMemory(); + return entry.id; + } + + /** + * Obtenir le contexte récent pour l'IA + */ + getRecentContext(limit: number = 10): ConversationContext { + const recentEntries = this.memory.slice(0, limit); + + const sessionStart = this.memory.length > 0 + ? new Date(Math.min(...this.memory.map(e => e.timestamp.getTime()))) + : new Date(); + + const sessionStats = { + commandsExecuted: this.memory.filter(e => e.type === 'command').length, + queriesExecuted: this.memory.filter(e => e.type === 'query').length, + totalCost: this.memory.reduce((sum, e) => sum + (e.metadata?.cost || 0), 0), + sessionStart + }; + + return { + recentEntries, + workingDirectory: process.cwd(), + activeProvider: this.getActiveProvider(), + sessionStats + }; + } + + /** + * Rechercher dans la mémoire + */ + search(query: string, options: { + type?: MemoryEntry['type']; + limit?: number; + since?: Date; + } = {}): MemoryEntry[] { + let results = this.memory; + + // Filtrer par type + if (options.type) { + results = results.filter(entry => entry.type === options.type); + } + + // Filtrer par date + if (options.since) { + results = results.filter(entry => entry.timestamp >= options.since!); + } + + // Recherche textuelle + if (query.trim()) { + const searchTerm = query.toLowerCase(); + results = results.filter(entry => { + const contentStr = JSON.stringify(entry.content).toLowerCase(); + return contentStr.includes(searchTerm); + }); + } + + // Limiter les résultats + return results.slice(0, options.limit || 50); + } + + /** + * Analyser les patterns récurrents + */ + analyzePatterns(): { + mostUsedCommands: Array<{ command: string; count: number }>; + errorPatterns: Array<{ error: string; count: number }>; + providerUsage: Array<{ provider: string; count: number }>; + averageResponseTime: number; + } { + const commandEntries = this.memory.filter(e => e.type === 'command'); + const errorEntries = this.memory.filter(e => e.metadata?.success === false); + + // Commandes les plus utilisées + const commandCounts = new Map(); + commandEntries.forEach(entry => { + const command = entry.content.command || 'unknown'; + commandCounts.set(command, (commandCounts.get(command) || 0) + 1); + }); + + const mostUsedCommands = Array.from(commandCounts.entries()) + .map(([command, count]) => ({ command, count })) + .sort((a, b) => b.count - a.count) + .slice(0, 10); + + // Patterns d'erreur + const errorCounts = new Map(); + errorEntries.forEach(entry => { + const error = entry.content.error || 'unknown error'; + errorCounts.set(error, (errorCounts.get(error) || 0) + 1); + }); + + const errorPatterns = Array.from(errorCounts.entries()) + .map(([error, count]) => ({ error, count })) + .sort((a, b) => b.count - a.count) + .slice(0, 5); + + // Usage des providers + const providerCounts = new Map(); + this.memory.forEach(entry => { + const provider = entry.metadata?.provider; + if (provider) { + providerCounts.set(provider, (providerCounts.get(provider) || 0) + 1); + } + }); + + const providerUsage = Array.from(providerCounts.entries()) + .map(([provider, count]) => ({ provider, count })) + .sort((a, b) => b.count - a.count); + + // Temps de réponse moyen + const responseTimes = this.memory + .filter(e => e.metadata?.duration) + .map(e => e.metadata!.duration!); + + const averageResponseTime = responseTimes.length > 0 + ? responseTimes.reduce((sum, time) => sum + time, 0) / responseTimes.length + : 0; + + return { + mostUsedCommands, + errorPatterns, + providerUsage, + averageResponseTime + }; + } + + /** + * Cache intelligent pour requêtes fréquentes + */ + cacheQuery(query: string, result: any, ttl: number = 300000): void { + const cacheEntry = { + result, + timestamp: Date.now(), + ttl + }; + + this.cache.set(query, cacheEntry); + this.saveCache(); + } + + /** + * Récupérer du cache + */ + getCachedQuery(query: string): any | null { + const cached = this.cache.get(query); + + if (!cached) return null; + + if (Date.now() - cached.timestamp > cached.ttl) { + this.cache.delete(query); + return null; + } + + return cached.result; + } + + /** + * Obtenir les statistiques de session + */ + getSessionStats(): { + totalEntries: number; + successRate: number; + totalCost: number; + averageCost: number; + sessionDuration: number; + } { + const totalEntries = this.memory.length; + const successfulEntries = this.memory.filter(e => e.metadata?.success !== false).length; + const successRate = totalEntries > 0 ? successfulEntries / totalEntries : 0; + + const totalCost = this.memory.reduce((sum, e) => sum + (e.metadata?.cost || 0), 0); + const averageCost = totalEntries > 0 ? totalCost / totalEntries : 0; + + const sessionStart = this.memory.length > 0 + ? Math.min(...this.memory.map(e => e.timestamp.getTime())) + : Date.now(); + const sessionDuration = Date.now() - sessionStart; + + return { + totalEntries, + successRate, + totalCost, + averageCost, + sessionDuration + }; + } + + /** + * Nettoyer la mémoire (supprimer les anciennes entrées) + */ + cleanup(olderThanDays: number = 30): number { + const cutoffDate = new Date(Date.now() - olderThanDays * 24 * 60 * 60 * 1000); + const originalLength = this.memory.length; + + this.memory = this.memory.filter(entry => entry.timestamp >= cutoffDate); + + const removedCount = originalLength - this.memory.length; + if (removedCount > 0) { + this.saveMemory(); + logger.info(`Nettoyage mémoire: ${removedCount} entrées supprimées`); + } + + return removedCount; + } + + /** + * Sauvegarder la mémoire + */ + private saveMemory(): void { + try { + writeFileSync(this.memoryFile, JSON.stringify(this.memory, null, 2)); + } catch (error) { + logger.error('Erreur sauvegarde mémoire:', error); + } + } + + /** + * Charger la mémoire + */ + private loadMemory(): void { + try { + if (existsSync(this.memoryFile)) { + const data = JSON.parse(readFileSync(this.memoryFile, 'utf8')); + this.memory = data.map((entry: any) => ({ + ...entry, + timestamp: new Date(entry.timestamp) + })); + } + } catch (error) { + logger.error('Erreur chargement mémoire:', error); + this.memory = []; + } + } + + /** + * Sauvegarder le cache + */ + private saveCache(): void { + try { + const cacheData = Object.fromEntries(this.cache); + writeFileSync(this.cacheFile, JSON.stringify(cacheData, null, 2)); + } catch (error) { + logger.error('Erreur sauvegarde cache:', error); + } + } + + /** + * Charger le cache + */ + private loadCache(): void { + try { + if (existsSync(this.cacheFile)) { + const data = JSON.parse(readFileSync(this.cacheFile, 'utf8')); + this.cache = new Map(Object.entries(data)); + } + } catch (error) { + logger.error('Erreur chargement cache:', error); + this.cache = new Map(); + } + } + + /** + * Générer un ID unique + */ + private generateId(): string { + return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`; + } + + /** + * Obtenir le provider actif + */ + private getActiveProvider(): string { + const recentProviderEntry = this.memory + .find(e => e.metadata?.provider); + + return recentProviderEntry?.metadata?.provider || 'unknown'; + } +} \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index 7849549..de0fe04 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,12 +1,16 @@ /** - * NeuraTerm - Terminal IA Professionnel + * NeuraTerm - Terminal IA Professionnel v2.0 + * Architecture C4 inspirée de ZeroTwo * * Point d'entrée principal de l'application. * Gère le cycle de vie de l'application et l'initialisation des modules. */ -import { loadConfig } from './config/index.js'; -import { initTerminal } from './terminal/index.js'; +import { UnifiedConfig } from './config/unifiedConfig.js'; +import { NeuraEngine } from './core/engine.js'; +import { NeuraMemory } from './core/memory.js'; +import { NeuraIdentity } from './core/identity.js'; +import { ModernInterface } from './terminal/modernInterface.js'; import { initAuthentication } from './auth/index.js'; import { initAI } from './ai/index.js'; import { initCodebaseAnalysis } from './codebase/index.js'; @@ -18,11 +22,21 @@ import { initTelemetry } from './telemetry/index.js'; import { logger } from './utils/logger.js'; /** - * Instance de l'application contenant toutes les références aux sous-systèmes + * Instance de l'application avec architecture C4 */ export interface AppInstance { - config: any; - terminal: any; + // Configuration unifiée + config: UnifiedConfig; + + // Architecture C4 - Modules centraux + engine: NeuraEngine; // Cœur - Actions protégées + memory: NeuraMemory; // Cerveau - Mémoire adaptative + identity: NeuraIdentity; // Personnalité - Identité fixe + + // Interface moderne + interface: ModernInterface; + + // Modules existants (Extensions - Corps modulaire) auth: any; ai: any; codebase: any; @@ -34,41 +48,61 @@ export interface AppInstance { } /** - * Initialise tous les sous-systèmes de l'application + * Initialise tous les sous-systèmes avec architecture C4 */ export async function initialize(options: any = {}): Promise { const errors = initErrorHandling(); try { - logger.info('Démarrage de NeuraTerm...'); + logger.info('🚀 Démarrage de NeuraTerm v2.0...'); - const config = await loadConfig(options); - const terminal = await initTerminal(config); - const auth = await initAuthentication(config); - const ai = await initAI(config, auth); - const codebase = await initCodebaseAnalysis(config); - const fileOps = await initFileOperations(config); - const execution = await initExecutionEnvironment(config); + // 1. Configuration unifiée + const config = new UnifiedConfig(); - const commands = await initCommandProcessor(config, { - terminal, + // 2. Architecture C4 - Modules centraux + const identity = new NeuraIdentity(); + const memory = new NeuraMemory(config.getConfig()); + const modernInterface = new ModernInterface(config.getConfig()); + + // 3. Modules existants + const auth = await initAuthentication(config.getConfig()); + const ai = await initAI(config.getConfig(), auth); + const execution = await initExecutionEnvironment(config.getConfig()); + + // 4. Engine principal (utilise tous les autres modules) + const engine = new NeuraEngine(ai, execution.getCommandExecutor(), config.getConfig()); + + // 5. Modules complémentaires + const codebase = await initCodebaseAnalysis(config.getConfig()); + const fileOps = await initFileOperations(config.getConfig()); + + // 6. Processeur de commandes avec nouvelle architecture + const commands = await initCommandProcessor(config.getConfig(), { + terminal: modernInterface, auth, ai, codebase, fileOps, execution, - errors + errors, + engine, + memory, + identity }); - const telemetry = config.telemetry.enabled - ? await initTelemetry(config) + // 7. Télémétrie si activée + const telemetry = config.get('telemetry.enabled', false) + ? await initTelemetry(config.getConfig()) : null; - logger.info('NeuraTerm initialisé avec succès'); + logger.info('✨ NeuraTerm v2.0 initialisé avec succès - Architecture C4 active'); return { config, - terminal, + engine, + memory, + identity, + interface: modernInterface, auth, ai, codebase, @@ -85,18 +119,38 @@ export async function initialize(options: any = {}): Promise { } /** - * Lance la boucle principale de l'application + * Lance la boucle principale avec interface moderne */ export async function run(app: AppInstance): Promise { try { - app.terminal.displayWelcome(); + // Affichage de bienvenue avec nouvelle interface + app.interface.displayBanner(); + // Présentation de l'identité NeuraTerm + console.log(app.identity.generateIntroduction()); + + // Authentification si nécessaire if (!app.auth.isAuthenticated()) { await app.auth.authenticate(); } + // Enregistrer le démarrage en mémoire + app.memory.addEntry('system', { + action: 'startup', + version: '2.0.0', + config: app.config.get('interface.theme') + }, { + success: true, + duration: 0 + }); + + // Démarrage des services app.codebase.startBackgroundAnalysis(); + + // Boucle de commandes principale await app.commands.startCommandLoop(); + + // Arrêt propre await shutdown(app); } catch (error) { app.errors.handleFatalError(error); @@ -104,19 +158,43 @@ export async function run(app: AppInstance): Promise { } /** - * Arrêt gracieux de l'application + * Arrêt gracieux avec sauvegarde de la mémoire */ export async function shutdown(app: AppInstance): Promise { - logger.info('Arrêt de NeuraTerm...'); + logger.info('🔄 Arrêt de NeuraTerm v2.0...'); + // Enregistrer les statistiques de session + const sessionStats = app.memory.getSessionStats(); + app.memory.addEntry('system', { + action: 'shutdown', + sessionStats + }, { + success: true, + duration: sessionStats.sessionDuration + }); + + // Nettoyage automatique si configuré + if (app.config.get('memory.auto_cleanup', true)) { + const cleanedEntries = app.memory.cleanup(app.config.get('memory.retention_days', 30)); + if (cleanedEntries > 0) { + logger.info(`🧹 Nettoyage mémoire: ${cleanedEntries} entrées supprimées`); + } + } + + // Arrêt des services await app.codebase.stopBackgroundAnalysis(); + // Télémétrie si activée if (app.telemetry) { await app.telemetry.submitTelemetry(); } + // Déconnexion IA await app.ai.disconnect(); - logger.info('NeuraTerm arrêté'); + + // Message de fin avec identité + console.log(app.identity.getSignature()); + logger.info('✅ NeuraTerm v2.0 arrêté proprement'); } /** diff --git a/src/terminal/modernInterface.ts b/src/terminal/modernInterface.ts new file mode 100644 index 0000000..80817aa --- /dev/null +++ b/src/terminal/modernInterface.ts @@ -0,0 +1,408 @@ +/** + * Interface Terminal Moderne inspirée de ZeroTwo + * Interface professionnelle avec thème cohérent et animations + */ + +import chalk from 'chalk'; +import * as readline from 'readline'; +import { logger } from '../utils/logger.js'; + +export interface NeuraTheme { + primary: any; + secondary: any; + accent: any; + success: any; + warning: any; + error: any; + info: any; + muted: any; + background: any; + text: any; +} + +export interface DisplayOptions { + showTimestamp?: boolean; + showProvider?: boolean; + showCost?: boolean; + showTokens?: boolean; + animated?: boolean; +} + +export class ModernInterface { + private theme: NeuraTheme; + private screenBuffer: string[] = []; + private scrollOffset: number = 0; + private aiMode: boolean = false; + private currentPlan: any = null; + + constructor(private config: any) { + this.theme = { + primary: chalk.hex('#00d4aa'), // Vert NeuraTerm principal + secondary: chalk.hex('#0099cc'), // Bleu secondaire + accent: chalk.hex('#66ffcc'), // Vert accent clair + success: chalk.hex('#00ff88'), // Vert succès + warning: chalk.hex('#ffaa00'), // Orange warning + error: chalk.hex('#ff4444'), // Rouge erreur + info: chalk.hex('#44aaff'), // Bleu info + muted: chalk.hex('#888888'), // Gris discret + background: chalk.hex('#0a0a0a'), // Fond sombre + text: chalk.hex('#ffffff') // Texte blanc + }; + + logger.info('🎨 Interface moderne initialisée'); + } + + /** + * Afficher le logo et la bannière NeuraTerm + */ + displayBanner(): void { + const banner = ` +${this.theme.primary('╔══════════════════════════════════════════════════════════════╗')} +${this.theme.primary('║')}${this.theme.accent(' 🧠 NeuraTerm v2.0.0 ')}${this.theme.primary('║')} +${this.theme.primary('║')}${this.theme.text(' Assistant IA Professionnel Autonome ')}${this.theme.primary('║')} +${this.theme.primary('╚══════════════════════════════════════════════════════════════╝')} + +${this.theme.info('🎯 Capacités avancées:')} + ${this.theme.accent('•')} ${this.theme.text('Exécution sécurisée de commandes système')} + ${this.theme.accent('•')} ${this.theme.text('Planification automatique de tâches complexes')} + ${this.theme.accent('•')} ${this.theme.text('Support multi-providers (OpenAI, Mistral)')} + ${this.theme.accent('•')} ${this.theme.text('Mémoire adaptative et apprentissage continu')} + +${this.theme.muted('Tapez')} ${this.theme.primary('help')} ${this.theme.muted('pour voir toutes les commandes ou')} ${this.theme.primary('@neura')} ${this.theme.muted('pour le mode IA')} +`; + + console.log(banner); + } + + /** + * Afficher une réponse IA avec style + */ + displayAIResponse(response: any, options: DisplayOptions = {}): void { + const timestamp = options.showTimestamp ? this.formatTimestamp() : ''; + const provider = options.showProvider ? this.formatProvider(response.provider, response.model) : ''; + + // En-tête de la réponse + const header = `${timestamp}${provider}${this.theme.primary('🧠 NeuraTerm')}: `; + console.log(`\n${header}`); + + // Contenu avec coloration intelligente + const coloredContent = this.colorizeContent(response.content); + console.log(coloredContent); + + // Métriques en bas si demandées + if (options.showTokens || options.showCost) { + this.displayMetrics(response, options); + } + } + + /** + * Afficher le résultat d'une commande + */ + displayCommandResult(result: any, options: DisplayOptions = {}): void { + const timestamp = options.showTimestamp ? this.formatTimestamp() : ''; + const icon = result.success ? this.theme.success('✅') : this.theme.error('❌'); + + console.log(`\n${timestamp}${icon} ${this.theme.muted('Commande:')} ${this.theme.accent(result.command)}`); + + if (result.success && result.stdout) { + console.log(this.colorizeCommandOutput(result.stdout)); + } + + if (!result.success && result.stderr) { + console.log(this.theme.error(`⚠️ Erreur: ${result.stderr}`)); + } + + // Temps d'exécution + const duration = this.formatDuration(result.executionTime || 0); + console.log(this.theme.muted(`⏱️ ${duration}`)); + } + + /** + * Afficher un plan d'exécution + */ + displayExecutionPlan(plan: any): void { + console.log(`\n${this.theme.primary('📋 Plan d\'action:')}`); + console.log(`${this.theme.info('🎯 Objectif:')} ${this.theme.text(plan.objective)}\n`); + + plan.tasks.forEach((task: any, index: number) => { + const stepNumber = this.theme.accent(`${index + 1}.`); + const taskDesc = this.theme.text(task.description); + + console.log(`${stepNumber} ${taskDesc}`); + + if (task.command) { + console.log(` ${this.theme.muted('📍 Commande:')} ${this.theme.secondary(task.command)}`); + } + + console.log(` ${this.theme.muted('💭 Justification:')} ${this.theme.muted(task.reasoning)}\n`); + }); + + console.log(`${this.theme.info('💡 Tapez')} ${this.theme.primary('run')} ${this.theme.info('pour exécuter ou')} ${this.theme.primary('cancel')} ${this.theme.info('pour annuler')}`); + } + + /** + * Afficher l'exécution d'une tâche en temps réel + */ + displayTaskExecution(task: any): void { + const statusIcons = { + pending: this.theme.muted('⏳'), + running: this.theme.info('🔄'), + completed: this.theme.success('✅'), + failed: this.theme.error('❌') + }; + + const icon = statusIcons[task.status as keyof typeof statusIcons]; + const taskDesc = this.theme.text(task.description); + + console.log(`${icon} ${taskDesc}`); + + if (task.status === 'completed' && task.result?.stdout) { + const output = this.truncateOutput(task.result.stdout, 200); + console.log(` ${this.theme.muted('📤')} ${this.colorizeCommandOutput(output)}`); + } + + if (task.status === 'failed' && task.result?.stderr) { + console.log(` ${this.theme.error('⚠️ ')} ${task.result.stderr}`); + } + } + + /** + * Afficher les statistiques d'utilisation + */ + displayStats(stats: any): void { + console.log(`\n${this.theme.primary('📊 Statistiques d\'utilisation:')}`); + console.log(this.theme.primary('─'.repeat(50))); + + if (Array.isArray(stats)) { + stats.forEach(stat => this.displayProviderStats(stat)); + } else { + this.displayProviderStats(stats); + } + } + + /** + * Afficher les statistiques d'un provider + */ + private displayProviderStats(stat: any): void { + const provider = this.theme.accent(stat.provider.toUpperCase()); + const model = this.theme.muted(`(${stat.model})`); + + console.log(`${provider} ${model}:`); + console.log(` ${this.theme.info('Requêtes:')} ${this.theme.text(stat.totalRequests)}`); + console.log(` ${this.theme.info('Tokens:')} ${this.theme.text(stat.totalInputTokens + stat.totalOutputTokens)} ${this.theme.muted(`(${stat.totalInputTokens}→${stat.totalOutputTokens})`)}`); + console.log(` ${this.theme.info('Coût:')} ${this.theme.accent('$' + stat.totalCost.toFixed(4))}`); + console.log(` ${this.theme.info('Temps moyen:')} ${this.theme.text(Math.round(stat.averageResponseTime))}ms`); + console.log(''); + } + + /** + * Afficher le statut des clés API + */ + displayKeyStatus(keys: any): void { + console.log(`\n${this.theme.primary('🔑 Statut des clés API:')}`); + console.log(this.theme.primary('─'.repeat(30))); + + // OpenAI + if (keys.openai) { + const masked = this.maskKey(keys.openai); + const source = process.env.OPENAI_API_KEY ? this.theme.muted('(var. env.)') : this.theme.muted('(fichier)'); + console.log(`${this.theme.success('✅ OpenAI:')} ${this.theme.text(masked)} ${source}`); + } else { + console.log(`${this.theme.error('❌ OpenAI:')} ${this.theme.muted('Non configuré')}`); + } + + // Mistral + if (keys.mistral) { + const masked = this.maskKey(keys.mistral); + const source = process.env.MISTRAL_API_KEY ? this.theme.muted('(var. env.)') : this.theme.muted('(fichier)'); + console.log(`${this.theme.success('✅ Mistral:')} ${this.theme.text(masked)} ${source}`); + } else { + console.log(`${this.theme.error('❌ Mistral:')} ${this.theme.muted('Non configuré')}`); + } + + console.log(''); + } + + /** + * Afficher une erreur avec style + */ + displayError(error: string): void { + console.log(`\n${this.theme.error('❌ Erreur:')} ${this.theme.text(error)}`); + } + + /** + * Afficher un message de succès + */ + displaySuccess(message: string): void { + console.log(`\n${this.theme.success('✅')} ${this.theme.text(message)}`); + } + + /** + * Afficher un avertissement + */ + displayWarning(message: string): void { + console.log(`\n${this.theme.warning('⚠️ ')} ${this.theme.text(message)}`); + } + + /** + * Afficher une information + */ + displayInfo(message: string): void { + console.log(`\n${this.theme.info('💡')} ${this.theme.text(message)}`); + } + + /** + * Basculer en mode IA + */ + enterAIMode(): void { + this.aiMode = true; + console.log(`\n${this.theme.primary('🤖 Mode IA activé - NeuraTerm vous écoute...')}`); + console.log(`${this.theme.muted('Tapez votre demande ou "exit" pour revenir au mode terminal')}`); + } + + /** + * Sortir du mode IA + */ + exitAIMode(): void { + this.aiMode = false; + console.log(`\n${this.theme.info('🔧 Retour au mode terminal normal')}`); + } + + /** + * Obtenir le prompt actuel + */ + getPrompt(): string { + if (this.aiMode) { + return `${this.theme.primary('🤖 NeuraTerm AI')} ${this.theme.accent('›')} `; + } else { + return `${this.theme.primary('🧠 NeuraTerm')} ${this.theme.accent('›')} `; + } + } + + /** + * Formater un timestamp + */ + private formatTimestamp(): string { + const now = new Date(); + const time = now.toLocaleTimeString('fr-FR', { + hour: '2-digit', + minute: '2-digit', + second: '2-digit' + }); + return `${this.theme.muted(`[${time}]`)} `; + } + + /** + * Formater les informations de provider + */ + private formatProvider(provider: string, model: string): string { + const providerIcon = provider === 'openai' ? '🤖' : '🧠'; + return `${this.theme.muted(`${providerIcon} ${provider}:${model}`)} `; + } + + /** + * Formater la durée + */ + private formatDuration(ms: number): string { + if (ms < 1000) { + return `${ms}ms`; + } else if (ms < 60000) { + return `${(ms / 1000).toFixed(1)}s`; + } else { + return `${(ms / 60000).toFixed(1)}m`; + } + } + + /** + * Colorer le contenu texte intelligemment + */ + private colorizeContent(content: string): string { + return content + .replace(/```(\w+)?\n([\s\S]*?)```/g, (match, lang, code) => { + return `${this.theme.muted('```' + (lang || ''))}${this.theme.accent(code)}${this.theme.muted('```')}`; + }) + .replace(/`([^`]+)`/g, (match, code) => { + return this.theme.accent(code); + }) + .replace(/\*\*([^*]+)\*\*/g, (match, text) => { + return this.theme.primary(text); + }) + .replace(/\*([^*]+)\*/g, (match, text) => { + return this.theme.info(text); + }); + } + + /** + * Colorer la sortie de commande + */ + private colorizeCommandOutput(output: string): string { + return output + .split('\n') + .map(line => { + // Colorer les chemins de fichiers + if (line.match(/^[\w\-\.\/\\]+$/)) { + return this.theme.accent(line); + } + // Colorer les permissions Unix + if (line.match(/^[drwx\-]{10}/)) { + return this.theme.info(line); + } + // Colorer les erreurs + if (line.toLowerCase().includes('error') || line.toLowerCase().includes('erreur')) { + return this.theme.error(line); + } + // Colorer les warnings + if (line.toLowerCase().includes('warning') || line.toLowerCase().includes('attention')) { + return this.theme.warning(line); + } + return this.theme.text(line); + }) + .join('\n'); + } + + /** + * Afficher les métriques + */ + private displayMetrics(response: any, options: DisplayOptions): void { + const metrics: string[] = []; + + if (options.showTokens && response.usage) { + metrics.push(`${this.theme.info('Tokens:')} ${response.usage.totalTokens} ${this.theme.muted(`(${response.usage.inputTokens}→${response.usage.outputTokens})`)}`); + } + + if (options.showCost && response.cost) { + metrics.push(`${this.theme.info('Coût:')} ${this.theme.accent('$' + response.cost.totalCost.toFixed(4))}`); + } + + if (metrics.length > 0) { + console.log(this.theme.primary('─'.repeat(50))); + console.log(metrics.join(' | ')); + } + } + + /** + * Masquer une clé API + */ + private maskKey(key: string): string { + if (key.length <= 8) return '*'.repeat(key.length); + return key.substring(0, 4) + '*'.repeat(key.length - 8) + key.substring(key.length - 4); + } + + /** + * Tronquer la sortie si trop longue + */ + private truncateOutput(output: string, maxLength: number): string { + if (output.length <= maxLength) return output; + + const truncated = output.substring(0, maxLength); + return truncated + this.theme.muted('... (tronqué)'); + } + + /** + * Effacer l'écran avec style + */ + clearScreen(): void { + console.clear(); + this.displayBanner(); + } +} \ No newline at end of file