@cachly-dev/mcp-server 0.5.47 → 0.5.49

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -14,6 +14,8 @@ declare function katakanaToRomaji(kata: string): string;
14
14
  /**
15
15
  * Arabic light stemmer — strips definite article and common prefix particles.
16
16
  * Handles: ال (al-), و (wa-), ب (bi-), ل (li-), ف (fa-), ك (ka-).
17
+ * Runs iteratively (max 3 passes) so compound prefixes like فال- are fully
18
+ * resolved: فالخطأ → الخطأ → خطأ.
17
19
  * Only strips when the result is still ≥ 3 chars to avoid over-stemming.
18
20
  */
19
21
  declare function arabicLightStem(word: string): string;
@@ -31,8 +33,9 @@ declare function expandCrossLingual(token: string): string[];
31
33
  * Features:
32
34
  * • CJK → character bigrams
33
35
  * • Katakana → additionally emits Hepburn romaji tokens (enables romaji queries)
34
- * • Arabic/Hebrew → word tokenization with light Arabic stemming
35
- * • All scripts → cross-lingual synonym expansion (EN↔JA↔ZH↔KO↔AR↔HE)
36
+ * • Arabic → word tokenization + iterative prefix stemming (al-, wa-, bi-, li-, fa-)
37
+ * • Hebrew → word tokenization + iterative prefix stemming (ha-, ve-, be-, le-, mi-, ke-)
38
+ * • All scripts → cross-lingual synonym expansion (EN↔JA↔ZH↔KO↔AR↔HE↔DE↔FR↔ES↔IT↔PT)
36
39
  */
37
40
  declare function tokenize(text: string): string[];
38
41
  /**
package/dist/index.js CHANGED
@@ -47,7 +47,7 @@ import { Redis } from 'ioredis';
47
47
  const API_URL = process.env.CACHLY_API_URL ?? 'https://api.cachly.dev';
48
48
  const JWT = process.env.CACHLY_JWT ?? '';
49
49
  const EMBED_MODEL = process.env.CACHLY_EMBED_MODEL ?? '';
50
- const CURRENT_VERSION = '0.5.26';
50
+ const CURRENT_VERSION = '0.5.49';
51
51
  /**
52
52
  * ┌──────────────────────────────────────────────────────────────────────┐
53
53
  * │ EMBEDDING PROVIDER — pluggable, client-side first │
@@ -409,6 +409,10 @@ const CJK_RE = /[\u3040-\u30ff\u3400-\u4dbf\u4e00-\u9fff\uf900-\ufaff\uac00-\ud7
409
409
  const SEGMENT_RE = /([\u3040-\u30ff\u3400-\u4dbf\u4e00-\u9fff\uf900-\ufaff\uac00-\ud7af]+|[^\u3040-\u30ff\u3400-\u4dbf\u4e00-\u9fff\uf900-\ufaff\uac00-\ud7af]+)/g;
410
410
  // RTL Unicode ranges: Arabic (U+0600–U+06FF + extended), Hebrew (U+0590–U+05FF)
411
411
  const RTL_RE = /[\u0590-\u05ff\u0600-\u06ff\u0750-\u077f\u08a0-\u08ff\ufb1d-\ufb4f\ufb50-\ufdff\ufe70-\ufeff]/;
412
+ // Distinguish Arabic from Hebrew within RTL segments
413
+ const HEBREW_CHAR_RE = /[\u0590-\u05ff\ufb1d-\ufb4f]/;
414
+ const ARABIC_CHAR_RE = /[\u0600-\u06ff\u0750-\u077f\u08a0-\u08ff\ufb50-\ufdff\ufe70-\ufeff]/;
415
+ ;
412
416
  // ── Katakana → Romaji conversion table (Hepburn system) ──────────────────────
413
417
  // Digraphs must be listed before single chars so they match first.
414
418
  const KATA_ROMAJI_TABLE = [
@@ -490,14 +494,51 @@ function katakanaToRomaji(kata) {
490
494
  /**
491
495
  * Arabic light stemmer — strips definite article and common prefix particles.
492
496
  * Handles: ال (al-), و (wa-), ب (bi-), ل (li-), ف (fa-), ك (ka-).
497
+ * Runs iteratively (max 3 passes) so compound prefixes like فال- are fully
498
+ * resolved: فالخطأ → الخطأ → خطأ.
493
499
  * Only strips when the result is still ≥ 3 chars to avoid over-stemming.
494
500
  */
495
501
  function arabicLightStem(word) {
496
- if (word.startsWith('ال') && word.length > 4)
497
- return word.slice(2);
498
- if (word.length > 3 && 'وبلفك'.includes(word[0]) && RTL_RE.test(word[1]))
499
- return word.slice(1);
500
- return word;
502
+ let result = word;
503
+ for (let i = 0; i < 3; i++) {
504
+ const prev = result;
505
+ if (result.startsWith('ال') && result.length > 4) {
506
+ result = result.slice(2);
507
+ }
508
+ else if (result.length > 3 && 'وبلفك'.includes(result[0]) && RTL_RE.test(result[1])) {
509
+ result = result.slice(1);
510
+ }
511
+ if (result === prev)
512
+ break; // stable — no more prefixes to strip
513
+ }
514
+ return result;
515
+ }
516
+ /**
517
+ * Hebrew light stemmer — strips the definite article and common prefix particles
518
+ * that attach directly to words (no space) in Hebrew.
519
+ *
520
+ * Handles:
521
+ * ה (ha-) — definite article: הפריסה → פריסה
522
+ * ו (ve-/u-) — conjunction “and”: ופריסה → פריסה
523
+ * ב (be-/bi-) — preposition “in”: בסביבה → סביבה
524
+ * ל (le-/li-) — preposition “for”: לשרת → שרת
525
+ * מ (mi-/me-) — preposition “from”: מהשרת → השרת → שרת
526
+ * כ (ke-/ki-) — preposition “like”: כשרת → שרת
527
+ *
528
+ * Iterative (max 3 passes): מהפריסה → הפריסה → פריסה.
529
+ * Only strips when result is still ≥ 3 chars.
530
+ */
531
+ function hebrewLightStem(word) {
532
+ let result = word;
533
+ for (let i = 0; i < 3; i++) {
534
+ const prev = result;
535
+ if (result.length > 3 && 'הובלמכ'.includes(result[0]) && HEBREW_CHAR_RE.test(result[1])) {
536
+ result = result.slice(1);
537
+ }
538
+ if (result === prev)
539
+ break;
540
+ }
541
+ return result;
501
542
  }
502
543
  // ── Cross-lingual tech term dictionary ────────────────────────────────────────
503
544
  // Maps tokens (English, Japanese katakana, Chinese, Korean, Arabic, Hebrew)
@@ -507,8 +548,8 @@ function arabicLightStem(word) {
507
548
  // Keys must be lowercase. CJK values will be bigram-expanded at use time.
508
549
  const CROSS_LINGUAL_MAP = new Map([
509
550
  // English → all
510
- ['deploy', ['デプロイ', '部署', '배포', 'deployment', 'deploying', 'bereitstellen', 'bereitstellung', 'déployer', 'déploiement', 'desplegar', 'despliegue', 'distribuire', 'distribuzione', 'implantar', 'implantação']],
511
- ['deployment', ['デプロイ', '部署', '배포', 'deploy', 'bereitstellung', 'déploiement', 'despliegue', 'distribuzione', 'implantação']],
551
+ ['deploy', ['デプロイ', '部署', '배포', 'deployment', 'deploying', 'نشر', 'פריסה', 'bereitstellen', 'bereitstellung', 'déployer', 'déploiement', 'desplegar', 'despliegue', 'distribuire', 'distribuzione', 'implantar', 'implantação']],
552
+ ['deployment', ['デプロイ', '部署', '배포', 'deploy', 'نشر', 'פריסה', 'bereitstellung', 'déploiement', 'despliegue', 'distribuzione', 'implantação']],
512
553
  ['container', ['コンテナ', '容器', '컨테이너', 'docker']],
513
554
  ['server', ['サーバー', 'サーバ', '服务器', '서버', 'سيرفر', 'שרת']],
514
555
  ['database', ['データベース', '数据库', '데이터베이스', 'db', 'قاعدة البيانات', 'datenbank', 'base de données', 'base de datos', 'banco de dados']],
@@ -519,6 +560,10 @@ const CROSS_LINGUAL_MAP = new Map([
519
560
  ['build', ['ビルド', '构建', '빌드', 'بناء', 'בנייה', 'bauen', 'construire', 'construir', 'costruire']],
520
561
  ['test', ['テスト', '测试', '테스트', 'اختبار', 'בדיקה', 'testen', 'tester', 'probar', 'testare', 'testar']],
521
562
  ['auth', ['認証', '认证', '인증', 'authentication', 'login', 'oauth', 'مصادقة', 'אימות', 'authentifizierung', 'authentification', 'autenticación', 'autenticazione', 'autenticação']],
563
+ ['authentication', ['auth', 'مصادقة', 'אימות', '認証', '认证', '인증', 'login', 'oauth']],
564
+ ['environment', ['環境', '环境', '환경', 'env', 'بيئة', 'סביבה', 'umgebung', 'environnement', 'entorno', 'ambiente']],
565
+ ['secret', ['key', 'token', 'password', 'jwt', 'مفتاح', 'מפתח', 'geheimnis', 'schlüssel', 'clave', 'clé', 'chiave', 'chave']],
566
+ ['problem', ['error', 'issue', 'bug', 'failure', 'مشكلة', 'بعيا', '問題', '問題', '오류', 'fehler', 'problème', 'problema']],
522
567
  ['kubernetes', ['クベルネテス', 'k8s', 'kube']],
523
568
  ['network', ['ネットワーク', '网络', '네트워크', 'شبكة', 'רשת', 'netzwerk', 'réseau', 'red', 'rete', 'rede']],
524
569
  ['timeout', ['タイムアウト', '超时', '타임아웃', 'مهلة']],
@@ -557,7 +602,7 @@ const CROSS_LINGUAL_MAP = new Map([
557
602
  ['ci', ['pipeline', 'github actions', 'gitlab', 'jenkins', 'build']],
558
603
  ['cd', ['deploy', 'deployment', 'release']],
559
604
  ['debug', ['デバッグ', '调试', '디버그', 'debugging', 'breakpoint', 'تصحيح', 'איתור באגים', 'debuggen', 'déboguer', 'depurar']],
560
- ['crash', ['クラッシュ', '崩溃', '크래시', 'panic', 'segfault', 'انهيار', 'absturz', 'panne', 'plantage', 'caída', 'falha']],
605
+ ['crash', ['クラッシュ', '崩溃', '크래시', 'panic', 'segfault', 'انهيار', 'קריסה', 'absturz', 'panne', 'plantage', 'caída', 'falha']],
561
606
  ['restart', ['再起動', '重启', '재시작', 'reboot', 'إعادة تشغيل', 'הפעלה מחדש', 'neustart', 'redémarrer', 'reiniciar', 'riavviare']],
562
607
  ['permission', ['権限', '权限', '권한', 'access', 'acl', 'chmod', 'صلاحية', 'הרשאה', 'berechtigung', 'berechtigungen', 'permiso', 'permesso', 'permissão']],
563
608
  // Japanese katakana → English
@@ -662,39 +707,89 @@ const CROSS_LINGUAL_MAP = new Map([
662
707
  ['إصلاح', ['fix']],
663
708
  ['بناء', ['build']],
664
709
  ['اختبار', ['test']],
665
- ['مصادقة', ['auth']],
666
- ['إعدادات', ['config']],
710
+ ['مصادقة', ['auth', 'authentication']],
711
+ ['إعدادات', ['config', 'configuration']],
667
712
  ['تثبيت', ['install']],
668
- ['تحديث', ['update']],
713
+ ['تحديث', ['update', 'upgrade']],
669
714
  ['سجل', ['log']],
670
715
  ['صورة', ['image']],
671
716
  ['خدمة', ['service']],
672
717
  ['نسخ احتياطي', ['backup']],
673
- ['مراقبة', ['monitor']],
718
+ ['مراقبة', ['monitor', 'monitoring']],
674
719
  ['اتصال', ['connection']],
675
720
  ['ذاكرة', ['memory']],
676
- ['تصحيح', ['debug']],
721
+ ['تصحيح', ['debug', 'debugging']],
677
722
  ['شبكة', ['network']],
723
+ // Additional Arabic → English (covering blog post examples + common terms)
724
+ ['نشر', ['deploy', 'deployment']],
725
+ ['انهيار', ['crash', 'panic']],
726
+ ['طابور', ['queue']],
727
+ ['رمز', ['token', 'secret']],
728
+ ['كاش', ['cache']],
729
+ ['منفذ', ['port']],
730
+ ['سيرفر', ['server']],
731
+ ['قاعدة البيانات', ['database', 'db']],
732
+ ['إعادة تشغيل', ['restart', 'reboot']],
733
+ ['صلاحية', ['permission', 'access']],
734
+ ['تطبيق', ['application', 'app']],
735
+ ['مفتاح', ['key', 'token', 'secret']],
736
+ ['واجهة برمجية', ['api', 'interface']],
737
+ ['فحص الصحة', ['healthcheck', 'health']],
738
+ ['حاوية', ['container', 'docker']],
739
+ ['عقدة', ['node']],
740
+ ['سرعة', ['performance', 'speed']],
741
+ ['قرص', ['disk', 'storage']],
742
+ ['خادم', ['server']],
743
+ ['برنامج', ['application', 'software', 'app']],
744
+ // Arabic terms from blog examples
745
+ ['مشكلة', ['error', 'issue', 'bug', 'problem', 'failure']],
746
+ ['بيئة', ['environment', 'env']],
747
+ ['متغيرات', ['variables', 'env', 'environment']],
748
+ ['إنتاج', ['production', 'prod']],
749
+ ['سري', ['secret', 'key', 'token']],
750
+ ['مفتاح سري', ['secret', 'key']],
678
751
  // Hebrew → English
679
752
  ['שגיאה', ['error']],
680
753
  ['תיקון', ['fix']],
681
754
  ['בנייה', ['build']],
682
755
  ['בדיקה', ['test']],
683
- ['אימות', ['auth']],
684
- ['הגדרות', ['config']],
756
+ ['אימות', ['auth', 'authentication']],
757
+ ['הגדרות', ['config', 'configuration']],
685
758
  ['התקנה', ['install']],
686
- ['עדכון', ['update']],
759
+ ['עדכון', ['update', 'upgrade']],
687
760
  ['יומן', ['log']],
688
761
  ['שרת', ['server']],
689
762
  ['שירות', ['service']],
690
763
  ['גיבוי', ['backup']],
691
- ['ניטור', ['monitor']],
764
+ ['ניטור', ['monitor', 'monitoring']],
692
765
  ['חיבור', ['connection']],
693
766
  ['זיכרון', ['memory']],
694
767
  ['רשת', ['network']],
695
768
  ['תמונה', ['image']],
696
769
  ['סיסמה', ['password']],
697
- ['הרשאה', ['permission']],
770
+ ['הרשאה', ['permission', 'access']],
771
+ // Additional Hebrew → English (covering blog post examples + common terms)
772
+ ['קריסה', ['crash', 'panic']],
773
+ ['תור', ['queue']],
774
+ ['מטמון', ['cache']],
775
+ ['אסימון', ['token', 'secret']],
776
+ ['פורט', ['port']],
777
+ ['איתור באגים', ['debug', 'debugging']],
778
+ ['הפעלה מחדש', ['restart', 'reboot']],
779
+ ['מיכל', ['container', 'docker']],
780
+ ['יישום', ['application', 'app']],
781
+ ['מפתח', ['key', 'token', 'secret']],
782
+ ['בדיקת תקינות', ['healthcheck', 'health']],
783
+ ['ממשק תכנות', ['api', 'interface']],
784
+ ['ביצועים', ['performance']],
785
+ ['אחסון', ['storage', 'disk']],
786
+ // Hebrew terms from blog examples
787
+ ['פריסה', ['deploy', 'deployment']],
788
+ ['בעיה', ['error', 'issue', 'bug', 'problem']],
789
+ ['סביבה', ['environment', 'env']],
790
+ ['ייצור', ['production', 'prod']],
791
+ ['סודי', ['secret', 'key', 'token']],
792
+ ['מפתח סודי', ['secret', 'key']],
698
793
  // ── European languages → English ─────────────────────────────────────────
699
794
  // German (Deutsch) → English
700
795
  ['fehler', ['error', 'err']],
@@ -892,8 +987,9 @@ function expandCrossLingual(token) {
892
987
  * Features:
893
988
  * • CJK → character bigrams
894
989
  * • Katakana → additionally emits Hepburn romaji tokens (enables romaji queries)
895
- * • Arabic/Hebrew → word tokenization with light Arabic stemming
896
- * • All scripts → cross-lingual synonym expansion (EN↔JA↔ZH↔KO↔AR↔HE)
990
+ * • Arabic → word tokenization + iterative prefix stemming (al-, wa-, bi-, li-, fa-)
991
+ * • Hebrew → word tokenization + iterative prefix stemming (ha-, ve-, be-, le-, mi-, ke-)
992
+ * • All scripts → cross-lingual synonym expansion (EN↔JA↔ZH↔KO↔AR↔HE↔DE↔FR↔ES↔IT↔PT)
897
993
  */
898
994
  function tokenize(text) {
899
995
  const tokens = [];
@@ -932,12 +1028,15 @@ function tokenize(text) {
932
1028
  }
933
1029
  }
934
1030
  else if (RTL_RE.test(seg)) {
935
- // RTL segment (Arabic / Hebrew) — space-separated words with light stemming
1031
+ // RTL segment — split on spaces, apply language-specific prefix stemming
1032
+ // Arabic: strips ال/و/ب/ل/ف/ك prefixes iteratively
1033
+ // Hebrew: strips ה/ו/ב/ל/מ/כ prefixes iteratively
1034
+ const isHebrew = HEBREW_CHAR_RE.test(seg);
936
1035
  const words = seg.trim().split(/\s+/);
937
1036
  for (const w of words) {
938
1037
  if (w.length <= 1 || STOPWORDS.has(w))
939
1038
  continue;
940
- const stemmed = arabicLightStem(w);
1039
+ const stemmed = isHebrew ? hebrewLightStem(w) : arabicLightStem(w);
941
1040
  if (stemmed.length > 1 && !STOPWORDS.has(stemmed))
942
1041
  tokens.push(stemmed);
943
1042
  }