UNPKG

ollama-code-qwen

Version:

Un assistant IA en ligne de commande utilisant Ollama et le modèle qwen2.5-coder pour aider au développement, avec des capacités MCP améliorées et détection d'intentions en français et anglais

279 lines (248 loc) 8.12 kB
/** * Service pour analyser et extraire des blocs de code des réponses de l'IA */ import { FileService } from './file-service.js'; import path from 'path'; import { promisify } from 'util'; import { exec as execCallback } from 'child_process'; const exec = promisify(execCallback); export class CodeAnalyzer { /** * Initialise l'analyseur de code * @param {FileService} fileService - Service de gestion de fichiers (optionnel) */ constructor(fileService = null) { this.fileService = fileService || new FileService(); } /** * Analyse et extrait les blocs de code d'une réponse * @param {string} response - Réponse de l'IA * @returns {Array} - Liste des blocs de code extraits avec leur langue et contenu */ extractCodeBlocks(response) { const codeBlockRegex = /```([a-zA-Z0-9_\-+#:.]*)\n([\s\S]*?)```/g; const blocks = []; let match; while ((match = codeBlockRegex.exec(response)) !== null) { const language = match[1].trim().toLowerCase() || 'text'; const code = match[2].trim(); blocks.push({ language, code, original: match[0], start: match.index, end: match.index + match[0].length }); } return blocks; } /** * Extrait les blocs de code spéciaux pour la création de fichiers * @param {string} response - Réponse de l'IA * @returns {Array} - Liste des blocs de code pour les fichiers */ extractFileBlocks(response) { const fileBlockRegex = /```file:([^\n]+)\n([\s\S]*?)```/g; const blocks = []; let match; while ((match = fileBlockRegex.exec(response)) !== null) { const filePath = match[1].trim(); const content = match[2]; blocks.push({ filePath, content, original: match[0], start: match.index, end: match.index + match[0].length }); } return blocks; } /** * Détecte et remplace les blocs de commandes shell * @param {string} response - Réponse de l'IA * @param {boolean} autoExecute - Exécuter automatiquement les commandes * @returns {Promise<object>} - Réponse modifiée et résultats d'exécution */ async processShellCommands(response, autoExecute = false) { const shellBlockRegex = /```(sh|bash|shell|cmd|powershell)\n([\s\S]*?)```/g; let modifiedResponse = response; const executions = []; let match; while ((match = shellBlockRegex.exec(response)) !== null) { const shell = match[1].trim().toLowerCase(); const commands = match[2].trim(); const original = match[0]; // Ajouter à la liste des exécutions possibles executions.push({ shell, commands, original, executed: false, result: null }); // Remplacer uniquement si l'exécution automatique est activée if (autoExecute) { try { const { stdout, stderr } = await exec(commands); const result = stdout + (stderr ? '\nErrors:\n' + stderr : ''); const replacement = `\`\`\`${shell}\n${commands}\n\`\`\`\n\n**Command executed:**\n\`\`\`\n${result}\n\`\`\``; modifiedResponse = modifiedResponse.replace(original, replacement); // Mettre à jour les résultats executions[executions.length - 1].executed = true; executions[executions.length - 1].result = result; } catch (error) { const replacement = `\`\`\`${shell}\n${commands}\n\`\`\`\n\n**Command execution failed:**\n\`\`\`\n${error.message}\n\`\`\``; modifiedResponse = modifiedResponse.replace(original, replacement); // Mettre à jour les résultats executions[executions.length - 1].executed = true; executions[executions.length - 1].result = error.message; } } } return { modifiedResponse, executions }; } /** * Détecte et traite les blocs de création de fichiers * @param {string} response - Réponse de l'IA * @param {boolean} autoSave - Enregistrer automatiquement les fichiers * @returns {Promise<object>} - Réponse modifiée et résultats d'enregistrement */ async processFileBlocks(response, autoSave = false) { const fileBlocks = this.extractFileBlocks(response); let modifiedResponse = response; const savedFiles = []; for (const block of fileBlocks) { if (autoSave) { try { await this.fileService.writeFile(block.filePath, block.content); modifiedResponse = modifiedResponse.replace( block.original, `\`\`\`file:${block.filePath}\n${block.content}\`\`\`\n✅ File saved successfully.` ); savedFiles.push({ filePath: block.filePath, success: true }); } catch (error) { modifiedResponse = modifiedResponse.replace( block.original, `\`\`\`file:${block.filePath}\n${block.content}\`\`\`\n❌ Error saving file: ${error.message}` ); savedFiles.push({ filePath: block.filePath, success: false, error: error.message }); } } } return { modifiedResponse, savedFiles }; } /** * Détecte le langage de programmation d'après l'extension d'un fichier * @param {string} filePath - Chemin du fichier * @returns {string} - Langage de programmation */ getLanguageFromFilePath(filePath) { const extension = path.extname(filePath).toLowerCase(); const languageMap = { '.js': 'javascript', '.ts': 'typescript', '.jsx': 'jsx', '.tsx': 'tsx', '.py': 'python', '.rb': 'ruby', '.go': 'go', '.rs': 'rust', '.c': 'c', '.cpp': 'cpp', '.cs': 'csharp', '.java': 'java', '.php': 'php', '.sh': 'bash', '.ps1': 'powershell', '.html': 'html', '.css': 'css', '.json': 'json', '.md': 'markdown', '.sql': 'sql', '.xml': 'xml', '.yaml': 'yaml', '.yml': 'yaml', '.toml': 'toml' }; return languageMap[extension] || 'text'; } /** * Exécute un bloc de code dans l'environnement approprié * @param {string} language - Langage de programmation * @param {string} code - Code à exécuter * @returns {Promise<object>} - Résultat de l'exécution */ async executeCode(language, code) { let command = ''; let tempFile = null; switch (language) { case 'javascript': case 'js': command = `node -e "${code.replace(/"/g, '\\"')}"`; break; case 'python': case 'py': // Créer un fichier temporaire tempFile = `temp_${Date.now()}.py`; await this.fileService.writeFile(tempFile, code); command = `python ${tempFile}`; break; case 'bash': case 'sh': // Créer un fichier temporaire tempFile = `temp_${Date.now()}.sh`; await this.fileService.writeFile(tempFile, code); command = `bash ${tempFile}`; break; default: throw new Error(`Unsupported language for execution: ${language}`); } try { const { stdout, stderr } = await exec(command); // Nettoyer le fichier temporaire si nécessaire if (tempFile) { try { await this.fileService.executeCommand(`rm ${tempFile}`); } catch (error) { // Ignorer les erreurs de nettoyage } } return { success: true, stdout, stderr, language }; } catch (error) { // Nettoyer le fichier temporaire en cas d'erreur if (tempFile) { try { await this.fileService.executeCommand(`rm ${tempFile}`); } catch (cleanError) { // Ignorer les erreurs de nettoyage } } return { success: false, error: error.message, stderr: error.stderr, stdout: error.stdout, language }; } } }