feat: version 4.0.1 -- Nuevo con IA: aviso de proyectos parecidos y objetivo redactado

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Milton Gonzalez
2026-10-23 16:38:28 -05:00
parent 4b83a24e4c
commit 7e59f0181d
9 changed files with 75 additions and 6 deletions
+28
View File
@@ -0,0 +1,28 @@
// Similitud por palabras clave (sin IA, sin tokens): normaliza tildes/mayusculas, quita palabras
// vacias, une singular/plural y aplica equivalencias comunes de TI.
const STOPWORDS = new Set(('de del la las el los un una unos unas y o a en con para por al lo su sus que se ' +
'implementar implementacion implantar implantacion proyecto sistema solucion nuevo nueva mejorar mejora ' +
'realizar crear desarrollar desarrollo the of and for to a an in').split(' '));
const SYNONYMS = {
helpdesk: 'mesa ayuda', servicedesk: 'mesa ayuda', soporte: 'ayuda',
pc: 'equipo', pcs: 'equipo', computador: 'equipo', computadora: 'equipo', laptop: 'equipo', portatil: 'equipo',
email: 'correo', mail: 'correo', wifi: 'red', lan: 'red', backup: 'respaldo', backups: 'respaldo',
erp: 'erp', crm: 'crm',
};
const stem = (w) => (w.length > 4 && w.endsWith('es') ? w.slice(0, -2) : w.length > 3 && w.endsWith('s') ? w.slice(0, -1) : w);
export function keywords(text) {
const words = String(text || '').toLowerCase().normalize('NFD').replace(/[̀-ͯ]/g, '')
.replace(/[^a-z0-9\s]/g, ' ').split(/\s+/).filter(Boolean)
.flatMap((w) => (SYNONYMS[w] ? SYNONYMS[w].split(' ') : [w]));
return new Set(words.filter((w) => w.length > 2 && !STOPWORDS.has(w)).map(stem));
}
// Parecido si comparten >= 2 palabras clave y al menos la mitad del conjunto mas chico.
export function isSimilar(a, b) {
let shared = 0;
for (const w of a) if (b.has(w)) shared += 1;
return shared >= 2 && shared / Math.max(1, Math.min(a.size, b.size)) >= 0.5;
}