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
JavaScript
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.'));
}
}
}