/** * Format a project into a string representation * @param project Project to format * @returns Formatted project string */ export function formatProject(project) { return `Title: ${project.settings.title}\nContext7-compatible library ID: ${project.settings.project}\n`; } /** * Format a list of projects into a string representation * @param projects Projects to format * @returns Formatted projects string */ export function formatProjectsList(projects) { const formattedProjects = projects.map(formatProject); return (formattedProjects.length + " available documentation libraries:\n\n" + formattedProjects.join("\n")); } /** * Rerank projects based on a search term * @param projects Projects to rerank * @param searchTerm Search term to rerank by * @returns Reranked projects */ export function rerankProjects(projects, searchTerm) { if (!searchTerm) return projects; // Normalize the search term - remove special characters and convert to lowercase const normalizedSearchTerm = searchTerm.toLowerCase().replace(/[^\w\s]/g, ""); return [...projects].sort((a, b) => { const aTitle = a.settings.title.toLowerCase(); const aProject = a.settings.project.toLowerCase(); const aProjectName = aProject.split("/").pop() || ""; const aProjectPath = aProject.split("/").slice(0, -1).join("/"); const bTitle = b.settings.title.toLowerCase(); const bProject = b.settings.project.toLowerCase(); const bProjectName = bProject.split("/").pop() || ""; const bProjectPath = bProject.split("/").slice(0, -1).join("/"); // Normalize project names for better matching - remove special characters const normalizedATitle = aTitle.replace(/[^\w\s]/g, ""); const normalizedAProject = aProject.replace(/[^\w\s]/g, ""); const normalizedAProjectName = aProjectName.replace(/[^\w\s]/g, ""); const normalizedBTitle = bTitle.replace(/[^\w\s]/g, ""); const normalizedBProject = bProject.replace(/[^\w\s]/g, ""); const normalizedBProjectName = bProjectName.replace(/[^\w\s]/g, ""); // Calculate match scores for better ranking const aScore = calculateMatchScore(normalizedSearchTerm, { original: { title: aTitle, project: aProject, projectName: aProjectName, projectPath: aProjectPath, }, normalized: { title: normalizedATitle, project: normalizedAProject, projectName: normalizedAProjectName, }, }); const bScore = calculateMatchScore(normalizedSearchTerm, { original: { title: bTitle, project: bProject, projectName: bProjectName, projectPath: bProjectPath, }, normalized: { title: normalizedBTitle, project: normalizedBProject, projectName: normalizedBProjectName, }, }); // Higher score first if (aScore !== bScore) { return bScore - aScore; } // Default to alphabetical by project name return aProject.localeCompare(bProject); }); } /** * Calculate a match score for ranking * Higher score means better match */ function calculateMatchScore(searchTerm, projectData) { const { original, normalized } = projectData; let score = 0; // Exact matches (highest priority) if (original.project === searchTerm || original.title === searchTerm || original.projectName === searchTerm) { score += 100; } // Normalized exact matches if (normalized.project === searchTerm || normalized.title === searchTerm || normalized.projectName === searchTerm) { score += 90; } // Starts with matches if (original.project.startsWith(searchTerm) || original.title.startsWith(searchTerm) || original.projectName.startsWith(searchTerm)) { score += 80; } // Normalized starts with matches if (normalized.project.startsWith(searchTerm) || normalized.title.startsWith(searchTerm) || normalized.projectName.startsWith(searchTerm)) { score += 70; } // Contains matches if (original.project.includes(searchTerm) || original.title.includes(searchTerm) || original.projectName.includes(searchTerm) || original.projectPath.includes(searchTerm)) { score += 60; } // Normalized contains matches if (normalized.project.includes(searchTerm) || normalized.title.includes(searchTerm) || normalized.projectName.includes(searchTerm)) { score += 50; } return score; }