update_project
Update project indexing with incremental RAG capabilities to maintain current semantic search across codebases by processing modified files and adjusting embeddings.
Instructions
Mettre à jour l'indexation d'un projet (indexation incrémentale) avec options RAG
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| project_path | Yes | Chemin absolu vers le projet à mettre à jour | |
| file_patterns | No | Patterns de fichiers à inclure | |
| recursive | No | Parcourir les sous-dossiers récursivement | |
| embedding_provider | No | Fournisseur d'embeddings (fake, ollama, sentence-transformers) | fake |
| embedding_model | No | Modèle d'embeddings (pour Ollama: 'nomic-embed-text', 'all-minilm', etc.) | nomic-embed-text |
| chunk_size | No | Taille des chunks pour le découpage (en tokens) | |
| chunk_overlap | No | Chevauchement entre les chunks (en tokens) |
Implementation Reference
- build/tools/rag/register-rag-tools.js:11-46 (registration)Registers the update_project tool (along with other RAG tools) using toolRegistry.register(updateProjectTool, updateProjectHandler). This is called during initialization.export function registerRagTools() { console.log("📝 Enregistrement des outils RAG..."); // Enregistrer index_project try { toolRegistry.register(indexProjectTool, indexProjectHandler); console.log(`✅ Outil enregistré: ${indexProjectTool.name}`); } catch (error) { console.error(`❌ Erreur lors de l'enregistrement de ${indexProjectTool.name}:`, error); } // Enregistrer search_code try { toolRegistry.register(searchCodeTool, searchCodeHandler); console.log(`✅ Outil enregistré: ${searchCodeTool.name}`); } catch (error) { console.error(`❌ Erreur lors de l'enregistrement de ${searchCodeTool.name}:`, error); } // Enregistrer manage_projects try { toolRegistry.register(manageProjectsTool, manageProjectsHandler); console.log(`✅ Outil enregistré: ${manageProjectsTool.name}`); } catch (error) { console.error(`❌ Erreur lors de l'enregistrement de ${manageProjectsTool.name}:`, error); } // Enregistrer update_project try { toolRegistry.register(updateProjectTool, updateProjectHandler); console.log(`✅ Outil enregistré: ${updateProjectTool.name}`); } catch (error) { console.error(`❌ Erreur lors de l'enregistrement de ${updateProjectTool.name}:`, error); } console.log(`🎉 Outils RAG terminés. Outils enregistrés au total: ${toolRegistry.size()}`); }
- Tool schema definition for 'update_project', including input schema with project_path (required), file_patterns, and recursive options.export const updateProjectTool = { name: "update_project", description: "Mettre à jour l'indexation d'un projet (indexation incrémentale) avec options RAG", inputSchema: { type: "object", properties: { project_path: { type: "string", description: "Chemin absolu vers le projet à mettre à jour" }, file_patterns: { type: "array", items: { type: "string" }, description: "Patterns de fichiers à inclure", default: ["**/*.{js,ts,py,md,txt,json,yaml,yml,html,css,scss}"] }, recursive: { type: "boolean", description: "Parcourir les sous-dossiers récursivement", default: true } }, required: ["project_path"] }, };
- build/tools/rag/update-project.js:37-83 (handler)Main handler for the update_project tool. Validates project_path, loads RAG config defaults and limits, configures embeddings, calls core updateProject, and formats response as MCP content.export const updateProjectHandler = async (args) => { if (!args.project_path || typeof args.project_path !== 'string') { throw new Error("The 'project_path' parameter is required and must be a string"); } // Charger la configuration const configManager = getRagConfigManager(); const defaults = configManager.getDefaults(); // Utiliser les valeurs par défaut de la configuration const file_patterns = args.file_patterns || defaults.file_patterns; const recursive = args.recursive !== undefined ? args.recursive : defaults.recursive; const embedding_provider = defaults.embedding_provider; const embedding_model = defaults.embedding_model; // Appliquer les limites aux valeurs numériques de la configuration const chunk_size = configManager.applyLimits('chunk_size', defaults.chunk_size); const chunk_overlap = configManager.applyLimits('chunk_overlap', defaults.chunk_overlap); // Configurer le fournisseur d'embeddings setEmbeddingProvider(embedding_provider, embedding_model); const options = { filePatterns: file_patterns, recursive: recursive, chunkSize: chunk_size, chunkOverlap: chunk_overlap }; try { const result = await updateProject(args.project_path, options); return { content: [{ type: "text", text: JSON.stringify({ ...result, config_used: { embedding_provider, embedding_model, chunk_size, chunk_overlap, recursive, file_patterns_count: file_patterns.length } }, null, 2) }] }; } catch (error) { console.error("Error in update_project tool:", error); throw error; } };
- build/rag/indexer.js:328-458 (helper)Core helper function implementing incremental project update using Git status for changed/added/deleted files, intelligent chunking, embedding, and vector store updates/deletes.export async function updateProject(projectPath, options = {}) { const { filePatterns = ["**/*.{js,ts,py,md,txt,json,yaml,yml,html,css,scss}"], recursive = true, chunkSize = 1000, chunkOverlap = 200, } = options; const stats = { totalFiles: 0, indexedFiles: 0, ignoredFiles: 0, errors: 0, chunksCreated: 0, modifiedFiles: 0, deletedFiles: 0, unchangedFiles: 0, }; try { // Vérifier que le projet existe if (!fs.existsSync(projectPath)) { throw new Error(`Project path does not exist: ${projectPath}`); } // Vérifier si c'est un dépôt Git const isGitRepo = await isGitRepository(projectPath); if (!isGitRepo) { console.error(`Project ${projectPath} is not a Git repository, performing full reindex`); const fullStats = await indexProject(projectPath, options); return { ...fullStats, modifiedFiles: fullStats.indexedFiles, deletedFiles: 0, unchangedFiles: 0, }; } // Récupérer les fichiers modifiés depuis le dernier commit const changedFiles = await getChangedFiles(projectPath); // Récupérer tous les fichiers du projet const allFiles = await fg(filePatterns, { cwd: projectPath, absolute: true, dot: false, onlyFiles: true, followSymbolicLinks: false, ...(recursive ? {} : { deep: 1 }), }); stats.totalFiles = allFiles.length; // Traiter les fichiers supprimés const deletedFiles = changedFiles.deleted || []; for (const filePath of deletedFiles) { try { await deleteFileFromIndex(projectPath, filePath); stats.deletedFiles++; console.error(`Deleted from index: ${filePath}`); } catch (error) { console.error(`Error deleting file ${filePath} from index:`, error); stats.errors++; } } // Traiter les fichiers modifiés et ajoutés const filesToProcess = [...(changedFiles.modified || []), ...(changedFiles.added || [])]; for (const filePath of filesToProcess) { try { // Vérifier si le fichier doit être ignoré if (shouldIgnoreFile(filePath, projectPath)) { stats.ignoredFiles++; continue; } // Vérifier si le fichier existe toujours if (!fs.existsSync(filePath)) { stats.deletedFiles++; await deleteFileFromIndex(projectPath, filePath); continue; } // Lire le contenu du fichier const content = fs.readFileSync(filePath, "utf8"); // Ignorer les fichiers vides ou trop petits if (content.trim().length < 10) { stats.ignoredFiles++; continue; } // Détecter le type de contenu et le langage const detection = detectContentType(filePath, content); const contentType = detection.contentType; const language = detection.language; // Découper en chunks de manière intelligente const chunks = chunkSize > 0 ? await chunkIntelligently(content, filePath, contentType, language, chunkSize, chunkOverlap) : [content]; // Supprimer les anciens chunks de ce fichier await deleteFileFromIndex(projectPath, filePath); // Stocker chaque chunk dans le vector store avec métadonnées for (let i = 0; i < chunks.length; i++) { const chunk = chunks[i]; const chunkFilePath = chunks.length > 1 ? `${filePath}#chunk${i}` : filePath; await embedAndStore(projectPath, chunkFilePath, chunk, { chunkIndex: i, totalChunks: chunks.length, contentType: contentType, language: language, fileExtension: filePath.split('.').pop() || undefined, linesCount: chunk.split('\n').length, role: contentType === 'code' ? 'core' : contentType === 'doc' ? 'example' : contentType === 'config' ? 'template' : 'other' }); stats.chunksCreated++; } stats.indexedFiles++; stats.modifiedFiles++; // Log progress if (stats.indexedFiles % 10 === 0) { console.error(`Indexed ${stats.indexedFiles}/${filesToProcess.length} changed files, ${stats.chunksCreated} chunks...`); } } catch (error) { console.error(`Error processing file ${filePath}:`, error); stats.errors++; } } // Compter les fichiers inchangés stats.unchangedFiles = stats.totalFiles - (stats.modifiedFiles + stats.deletedFiles + stats.ignoredFiles); console.error(`Incremental reindex completed for ${projectPath}`); console.error(` Total files: ${stats.totalFiles}`); console.error(` Modified/added: ${stats.modifiedFiles}`); console.error(` Deleted: ${stats.deletedFiles}`); console.error(` Unchanged: ${stats.unchangedFiles}`); console.error(` Chunks created: ${stats.chunksCreated}`); console.error(` Ignored: ${stats.ignoredFiles}`); console.error(` Errors: ${stats.errors}`); return stats; } catch (error) { console.error(`Error updating project ${projectPath}:`, error); throw error; }