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
JavaScript
/**
* 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
};
}
}
}