UNPKG

vibe-coder-mcp

Version:

Production-ready MCP server with complete agent integration, multi-transport support, and comprehensive development automation tools for AI-assisted workflows.

147 lines (146 loc) 4.82 kB
export class StringSimilarity { static fuzzyMatch(query, target, options = {}) { const { caseSensitive = false, maxEditDistance = 2, threshold = 0.6 } = options; if (!query || !target) { return { score: 0, isMatch: false, editDistance: Infinity, matchType: 'none' }; } const q = caseSensitive ? query : query.toLowerCase(); const t = caseSensitive ? target : target.toLowerCase(); if (q === t) { return { score: 1.0, isMatch: true, editDistance: 0, matchType: 'exact' }; } if (t.includes(q)) { const ratio = q.length / t.length; const score = 0.8 + (ratio * 0.2); return { score, isMatch: score >= threshold, editDistance: t.length - q.length, matchType: 'substring' }; } const editDistance = this.levenshteinDistance(q, t); if (editDistance > maxEditDistance) { return { score: 0, isMatch: false, editDistance, matchType: 'none' }; } const maxLength = Math.max(q.length, t.length); let similarity = 1 - (editDistance / maxLength); let prefixBonus = 0; const minLength = Math.min(q.length, t.length); for (let i = 0; i < minLength; i++) { if (q[i] === t[i]) { prefixBonus += 0.1; } else { break; } } let suffixBonus = 0; for (let i = 1; i <= minLength; i++) { if (q[q.length - i] === t[t.length - i]) { suffixBonus += 0.05; } else { break; } } similarity = Math.min(similarity + prefixBonus + suffixBonus, 0.79); return { score: similarity, isMatch: similarity >= threshold, editDistance, matchType: similarity > 0 ? 'fuzzy' : 'none' }; } static isTypoMatch(query, target, options = {}) { const result = this.fuzzyMatch(query, target, { threshold: 0.6, maxEditDistance: 2, ...options }); if (!result.isMatch && result.editDistance <= 2) { if (this.hasTransposition(query, target)) { return true; } if (this.hasDoubledChar(query, target)) { return true; } } return result.isMatch; } static levenshteinDistance(a, b) { if (a === b) return 0; if (a.length === 0) return b.length; if (b.length === 0) return a.length; let previousRow = Array(b.length + 1).fill(0).map((_, i) => i); for (let i = 0; i < a.length; i++) { const currentRow = [i + 1]; for (let j = 0; j < b.length; j++) { const insertCost = currentRow[j] + 1; const deleteCost = previousRow[j + 1] + 1; const replaceCost = previousRow[j] + (a[i] !== b[j] ? 1 : 0); currentRow.push(Math.min(insertCost, deleteCost, replaceCost)); } previousRow = currentRow; } return previousRow[b.length]; } static hasTransposition(a, b) { if (Math.abs(a.length - b.length) !== 0) return false; let differences = 0; for (let i = 0; i < a.length; i++) { if (a[i] !== b[i]) { differences++; if (differences > 2) return false; if (i + 1 < a.length && a[i] === b[i + 1] && a[i + 1] === b[i]) { i++; } } } return differences === 2; } static hasDoubledChar(a, b) { const longer = a.length > b.length ? a : b; const shorter = a.length > b.length ? b : a; if (longer.length - shorter.length !== 1) return false; let shorterIndex = 0; let differences = 0; for (let i = 0; i < longer.length; i++) { if (shorterIndex < shorter.length && longer[i] === shorter[shorterIndex]) { shorterIndex++; } else { differences++; if (differences > 1) return false; } } return shorterIndex === shorter.length && differences === 1; } static similarity(a, b) { return this.fuzzyMatch(a, b).score; } }