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

266 lines (233 loc) 9.93 kB
import { Command } from 'commander'; import chalk from 'chalk'; import inquirer from 'inquirer'; import ora from 'ora'; import { marked } from 'marked'; import TerminalRenderer from 'marked-terminal'; import { OllamaService } from './services/ollama-service.js'; import { ContextManager } from './services/context-manager.js'; import { CodeExecutor } from './services/code-executor.js'; import { GitService } from './services/git-service.js'; import { CacheService } from './services/cache-service.js'; import { PromptService } from './services/prompt-service.js'; import { Config } from './utils/config.js'; import { InteractiveMode } from './ui/interactive-mode.js'; import { LiteMode } from './ui/lite-mode.js'; // Configuration du renderer pour Markdown dans le terminal marked.setOptions({ renderer: new TerminalRenderer({ code: chalk.cyan, blockquote: chalk.gray.italic, table: chalk.white, listitem: chalk.yellow, strong: chalk.bold.green, em: chalk.italic.cyan, heading: chalk.bold.blueBright, }) }); /** * Affiche l'en-tête de l'application */ function displayHeader() { console.clear(); console.log('\n' + chalk.bgGreen(' Ollama Code ') + ' - Assistant IA pour le développement\n'); // Afficher la version et le modèle const config = new Config(); const version = '0.5.2'; const model = config.get('defaultModel'); const host = config.get('ollamaHost'); console.log(`Version: ${chalk.yellow(version)} | Modèle: ${chalk.yellow(model)} | Serveur: ${chalk.yellow(host)}\n`); } /** * Fonction principale du CLI */ export async function main(argv) { // Initialiser la configuration const config = new Config(); // Initialiser le service de cache let cacheService = null; if (config.get('enableCache')) { cacheService = new CacheService(); await cacheService.init(); } // Créer le programme CLI const program = new Command(); program .name('ollama-code') .description('Assistant IA en ligne de commande avec Ollama et capacités MCP avancées') .version('0.5.2'); program .argument('[prompt]', 'Question ou demande pour Ollama') .option('-m, --model <model>', 'Modèle Ollama à utiliser', config.get('defaultModel')) .option('-c, --context', 'Inclure le contexte du répertoire courant') .option('-e, --execute', 'Exécuter automatiquement le code généré') .option('-i, --interactive', 'Démarrer en mode interactif') .option('-l, --lite', 'Utiliser une interface légère pour les connexions distantes') .option('-f, --focus <path>', 'Démarrer en mode focus sur un fichier ou dossier spécifique') .option('-t, --temperature <temp>', 'Température pour la génération (0.0-1.0)', parseFloat, config.get('temperature')) .option('--stream', 'Utiliser le streaming pour les réponses', config.get('enableStreaming')) .option('--no-stream', 'Désactiver le streaming') .option('--thinking', 'Afficher le message "Réflexion en cours..." pendant le traitement', true) .option('--no-thinking', 'Désactiver l\'affichage du message de réflexion') .option('--host <host>', 'URL du serveur Ollama', config.get('ollamaHost')) .action(async (prompt, options) => { try { // Initialiser les services const ollamaService = new OllamaService(options.model, options.host || config.get('ollamaHost')); const contextManager = new ContextManager(process.cwd(), { config, cacheService }); const codeExecutor = new CodeExecutor({ timeout: config.get('codeExecutionTimeout') || 10000 }); const promptService = new PromptService(config); // Afficher l'en-tête displayHeader(); // Vérifier si Ollama est en cours d'exécution const isServerRunning = await ollamaService.checkServer(); if (!isServerRunning) { console.error(chalk.red('Erreur: Serveur Ollama non disponible.')); console.error(chalk.yellow(`Assurez-vous que Ollama est installé et en cours d'exécution à l'adresse ${ollamaService.host}.`)); console.error(chalk.yellow(`Vous pouvez télécharger Ollama depuis: https://ollama.ai`)); process.exit(1); } // Mode interactif ou mode léger if (options.interactive || options.lite) { if (options.lite) { // Mode léger pour les connexions distantes const liteMode = new LiteMode(ollamaService, contextManager); await liteMode.start(); } else { // Mode interactif standard avec interface TUI const interactiveMode = new InteractiveMode(ollamaService, contextManager); // Si un chemin focus est spécifié, l'utiliser if (options.focus) { await interactiveMode.start(); await interactiveMode.enableFocusMode(options.focus); } else { await interactiveMode.start(); } } return; } // Si aucun prompt n'est fourni en argument, demander à l'utilisateur if (!prompt) { const answer = await inquirer.prompt([{ type: 'input', name: 'prompt', message: 'Comment puis-je vous aider ?' }]); prompt = answer.prompt; } await processPrompt(prompt, options, ollamaService, contextManager, codeExecutor, promptService); } catch (error) { console.error(chalk.red(`Erreur: ${error.message}`)); process.exit(1); } }); program.parse(argv); } /** * Traite un prompt avec les options spécifiées */ async function processPrompt(prompt, options, ollamaService, contextManager, codeExecutor, promptService) { console.log(chalk.blue(`Répertoire de travail: ${process.cwd()}`)); // Récupérer le contexte si demandé let projectContext = ""; if (options.context) { const spinner = ora('Analyse du contexte du projet...').start(); projectContext = await contextManager.getContext(); spinner.succeed('Contexte du projet analysé'); } // Détection du type de tâche et optimisation du prompt const promptConfig = promptService.generateSystemPrompt(prompt, projectContext); // Si on utilise le modèle qwen2.5-coder, utiliser son système de prompt spécifique let systemPrompt = promptConfig.systemPrompt; // Préparer le prompt complet avec contexte let fullPrompt = prompt; if (projectContext) { fullPrompt = `Contexte du projet:\n${projectContext}\n\nDemande: ${prompt}`; } // Obtenir une réponse d'Ollama const spinner = ora(options.thinking ? 'Réflexion en cours...' : `Consultation du modèle ${options.model}...`).start(); let response; if (options.stream) { // Mode streaming (version CLI simplifiée) response = ''; let lastUpdate = Date.now(); // Afficher la barre de progression initiale spinner.text = options.thinking ? 'Réflexion en cours...' : `${options.model} réfléchit...`; await ollamaService.streamGenerate( fullPrompt, systemPrompt, options.temperature || promptConfig.temperature, options.maxTokens || 4096, (token) => { response += token; // Mettre à jour l'UI de temps en temps pour éviter le clignotement const now = Date.now(); if (now - lastUpdate > 500) { // Mise à jour toutes les 500ms maximum spinner.text = `${options.model} génère... (${response.length} caractères)`; lastUpdate = now; } } ); spinner.succeed('Réponse générée'); } else { // Mode classique (sans streaming) response = await ollamaService.generate( fullPrompt, systemPrompt, options.temperature || promptConfig.temperature, options.maxTokens || 4096 ); spinner.succeed('Réponse générée'); } // Afficher la réponse console.log('\n' + chalk.green.bold('Réponse:')); console.log(marked(response)); // Exécuter le code si demandé if (options.execute) { const codeBlocks = codeExecutor.extractCodeBlocks(response); if (codeBlocks.length > 0) { const { shouldExecute } = await inquirer.prompt([{ type: 'confirm', name: 'shouldExecute', message: 'Voulez-vous exécuter le code généré?', default: false }]); if (shouldExecute) { for (const [language, code] of codeBlocks) { // Version améliorée avec prise en charge de plusieurs langages console.log(chalk.yellow(`\nExécution du code ${language}:`)); const execSpinner = ora('Exécution en cours...').start(); try { const result = await codeExecutor.executeCode(language, code); execSpinner.stop(); if (result.success) { console.log(chalk.green('\nExécution réussie:')); if (result.stdout) { console.log(chalk.white(result.stdout)); } else { console.log(chalk.gray('(Aucune sortie)')); } if (result.stderr && result.stderr.trim()) { console.log(chalk.yellow('\nMessages d\'erreur (non critiques):')); console.log(chalk.gray(result.stderr)); } } else { console.log(chalk.red('\nL\'exécution a échoué:')); console.log(chalk.red(result.stderr || result.error)); } } catch (error) { execSpinner.stop(); console.log(chalk.red(`\nErreur d'exécution: ${error.message}`)); } } } } else { console.log(chalk.yellow('\nAucun bloc de code exécutable trouvé dans la réponse.')); } } }