skill-creator 1.5.1 → 1.5.2

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.
Files changed (84) hide show
  1. package/README.md +173 -103
  2. package/dist/buildIndex-B_9ABRtx.mjs +2 -0
  3. package/dist/buildIndex-B_9ABRtx.mjs.map +1 -0
  4. package/dist/buildIndex-Cea97e0l.mjs +1 -0
  5. package/dist/cli.d.mts +0 -0
  6. package/dist/cli.d.mts.map +1 -1
  7. package/dist/cli.mjs +17 -33
  8. package/dist/cli.mjs.map +1 -1
  9. package/dist/contentManager-CoVTiSDT.mjs +13 -0
  10. package/dist/contentManager-CoVTiSDT.mjs.map +1 -0
  11. package/dist/contentManager-CwquwC1s.mjs +1 -0
  12. package/dist/context7-BejCCqkm.mjs +1 -0
  13. package/dist/context7-BxMpyQiW.mjs +2 -0
  14. package/dist/context7-BxMpyQiW.mjs.map +1 -0
  15. package/dist/index.d.mts +317 -123
  16. package/dist/index.d.mts.map +1 -1
  17. package/dist/index.mjs +1 -20
  18. package/dist/index.mjs.map +1 -1
  19. package/dist/{listContext7-DKXzgWdn.mjs → listContext7-DHj9AL-z.mjs} +2 -2
  20. package/dist/{listContext7-DKXzgWdn.mjs.map → listContext7-DHj9AL-z.mjs.map} +1 -1
  21. package/dist/package-CFJazwTO.mjs +2 -0
  22. package/dist/package-CFJazwTO.mjs.map +1 -0
  23. package/dist/referenceSearchUtils-DZgupMZp.mjs +2 -0
  24. package/dist/referenceSearchUtils-DZgupMZp.mjs.map +1 -0
  25. package/dist/removeContext7-BZtL4_CJ.mjs +3 -0
  26. package/dist/{removeContext7-DOXrTXeI.mjs.map → removeContext7-BZtL4_CJ.mjs.map} +1 -1
  27. package/dist/{removeSkill-C-ehFexk.mjs → removeSkill-Bat_plVG.mjs} +4 -4
  28. package/dist/removeSkill-Bat_plVG.mjs.map +1 -0
  29. package/dist/removeSkill-DjDZjhJU.mjs +1 -0
  30. package/dist/runScript-CSKqb831.mjs +13 -0
  31. package/dist/runScript-CSKqb831.mjs.map +1 -0
  32. package/dist/scopeSelection-BWteJQSv.mjs +2 -0
  33. package/dist/scopeSelection-BWteJQSv.mjs.map +1 -0
  34. package/dist/searchQuality-DVdHmamh.mjs +2 -0
  35. package/dist/searchQuality-DVdHmamh.mjs.map +1 -0
  36. package/dist/search_format-RyjXACJa.mjs +5 -0
  37. package/dist/search_format-RyjXACJa.mjs.map +1 -0
  38. package/dist/shared-DsVh4gOV.mjs +2 -0
  39. package/dist/shared-DsVh4gOV.mjs.map +1 -0
  40. package/dist/skillCreator-BomXoqdw.mjs +2 -0
  41. package/dist/skillCreator-BomXoqdw.mjs.map +1 -0
  42. package/dist/skillMdManager-DGBZ6M5J.mjs +7 -0
  43. package/dist/skillMdManager-DGBZ6M5J.mjs.map +1 -0
  44. package/dist/sqliteVectorSearchAdapter-56kZjbUT.mjs +28 -0
  45. package/dist/sqliteVectorSearchAdapter-56kZjbUT.mjs.map +1 -0
  46. package/dist/sqliteVectorSearchAdapter-DNkyKuZs.mjs +1 -0
  47. package/dist/unifiedSearch-CLdazMYU.mjs +4 -0
  48. package/dist/unifiedSearch-CLdazMYU.mjs.map +1 -0
  49. package/dist/unifiedSearch-DCFPrtzZ.mjs +1 -0
  50. package/package.json +32 -28
  51. package/templates/SKILL.md +19 -4
  52. package/templates/SKILL.zh-CN.md +54 -12
  53. package/templates/skill-creator.md +52 -19
  54. package/templates/skill-creator.zh-CN.md +55 -22
  55. package/dist/chunk-Da8dvM5T.mjs +0 -1
  56. package/dist/contentManager-8rmWJMb6.mjs +0 -22
  57. package/dist/contentManager-8rmWJMb6.mjs.map +0 -1
  58. package/dist/contentManager-Xt8N5psY.mjs +0 -1
  59. package/dist/dist-DZL34HNo.mjs +0 -8
  60. package/dist/dist-DZL34HNo.mjs.map +0 -1
  61. package/dist/dist-X_kVEzbK.mjs +0 -1
  62. package/dist/esm-8qfZdcpd.mjs +0 -33
  63. package/dist/esm-8qfZdcpd.mjs.map +0 -1
  64. package/dist/esm-BF5pvSbc.mjs +0 -1
  65. package/dist/esm-BbhxxeGF.mjs +0 -4
  66. package/dist/esm-BbhxxeGF.mjs.map +0 -1
  67. package/dist/package-Cxx_VSyD.mjs +0 -2
  68. package/dist/package-Cxx_VSyD.mjs.map +0 -1
  69. package/dist/path-CuoiIaIe.mjs +0 -2
  70. package/dist/path-CuoiIaIe.mjs.map +0 -1
  71. package/dist/removeContext7-DOXrTXeI.mjs +0 -3
  72. package/dist/removeSkill-C-ehFexk.mjs.map +0 -1
  73. package/dist/removeSkill-DWwhvzej.mjs +0 -1
  74. package/dist/runScript-Cr9RRN9y.mjs +0 -14
  75. package/dist/runScript-Cr9RRN9y.mjs.map +0 -1
  76. package/dist/search_format-DIs-1lKk.mjs +0 -5
  77. package/dist/search_format-DIs-1lKk.mjs.map +0 -1
  78. package/dist/shared-CFoY2Xhg.mjs +0 -2
  79. package/dist/shared-CFoY2Xhg.mjs.map +0 -1
  80. package/dist/skillMdManager-DP8fGZb7.mjs +0 -7
  81. package/dist/skillMdManager-DP8fGZb7.mjs.map +0 -1
  82. package/dist/unifiedSearch-CGZXw0NB.mjs +0 -1
  83. package/dist/unifiedSearch-l-lrSc9C.mjs +0 -4
  84. package/dist/unifiedSearch-l-lrSc9C.mjs.map +0 -1
@@ -0,0 +1,4 @@
1
+ import{i as e,n as t,r as n,t as r}from"./referenceSearchUtils-DZgupMZp.mjs";import{t as i}from"./searchQuality-DVdHmamh.mjs";import{t as a}from"./search_format-RyjXACJa.mjs";import"node:module";import{basename as o,join as s}from"node:path";import{existsSync as c,mkdirSync as l,readFileSync as u,rmSync as d,writeFileSync as f}from"node:fs";import{glob as p}from"glob";import m from"minisearch";var h=Object.create,g=Object.defineProperty,_=Object.getOwnPropertyDescriptor,v=Object.getOwnPropertyNames,y=Object.getPrototypeOf,b=Object.prototype.hasOwnProperty,x=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports),S=(e,t,n,r)=>{if(t&&typeof t==`object`||typeof t==`function`)for(var i=v(t),a=0,o=i.length,s;a<o;a++)s=i[a],!b.call(e,s)&&s!==n&&g(e,s,{get:(e=>t[e]).bind(null,s),enumerable:!(r=_(t,s))||r.enumerable});return e},C=((e,t,n)=>(n=e==null?{}:h(y(e)),S(t||!e||!e.__esModule?g(n,`default`,{value:e,enumerable:!0}):n,e)))(x(((e,t)=>{let n=(e,t)=>e>t?1:e<t?-1:0,r=1/0,i=e=>e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`),a=`eexxaacctt`,o=/\p{P}/gu,s=[`en`,{numeric:!0,sensitivity:`base`}],c=(e,t,n)=>e.replace(`A-Z`,t).replace(`a-z`,n),l={unicode:!1,alpha:null,interSplit:`[^A-Za-z\\d']+`,intraSplit:`[a-z][A-Z]`,interBound:`[^A-Za-z\\d]`,intraBound:`[A-Za-z]\\d|\\d[A-Za-z]|[a-z][A-Z]`,interLft:0,interRgt:0,interChars:`.`,interIns:r,intraChars:`[a-z\\d']`,intraIns:null,intraContr:`'[a-z]{1,2}\\b`,intraMode:0,intraSlice:[1,r],intraSub:null,intraTrn:null,intraDel:null,intraFilt:(e,t,n)=>!0,toUpper:e=>e.toLocaleUpperCase(),toLower:e=>e.toLocaleLowerCase(),compare:null,sort:(e,t,r,i=n)=>{let{idx:a,chars:o,terms:s,interLft2:c,interLft1:l,start:u,intraIns:d,interIns:f,cases:p}=e;return a.map((e,t)=>t).sort((e,n)=>o[n]-o[e]||d[e]-d[n]||s[n]+c[n]+.5*l[n]-(s[e]+c[e]+.5*l[e])||f[e]-f[n]||u[e]-u[n]||p[n]-p[e]||i(t[a[e]],t[a[n]]))}},u=(e,t)=>t==0?``:t==1?e+`??`:t==r?e+`*?`:e+`{0,${t}}?`,d=`(?:\\b|_)`;function f(e){e=Object.assign({},l,e);let{unicode:t,interLft:r,interRgt:f,intraMode:p,intraSlice:h,intraIns:g,intraSub:_,intraTrn:v,intraDel:y,intraContr:b,intraSplit:x,interSplit:S,intraBound:C,interBound:w,intraChars:T,toUpper:E,toLower:D,compare:O}=e;g??=p,_??=p,v??=p,y??=p,O??=typeof Intl>`u`?n:new Intl.Collator(...s).compare;let k=e.letters??e.alpha;if(k!=null){let e=E(k),t=D(k);S=c(S,e,t),x=c(x,e,t),w=c(w,e,t),C=c(C,e,t),T=c(T,e,t),b=c(b,e,t)}let A=t?`u`:``,j=`".+?"`,M=new RegExp(j,`gi`+A),N=RegExp(`(?:\\s+|^)-(?:${T}+|${j})`,`gi`+A),{intraRules:P}=e;P??=e=>{let t=l.intraSlice,n=0,r=0,i=0,a=0;if(/[^\d]/.test(e)){let o=e.length;o<=4?o>=3&&(i=Math.min(v,1),o==4&&(n=Math.min(g,1))):(t=h,n=g,r=_,i=v,a=y)}return{intraSlice:t,intraIns:n,intraSub:r,intraTrn:i,intraDel:a}};let F=!!x,I=new RegExp(x,`g`+A),L=new RegExp(S,`g`+A),R=RegExp(`^`+S+`|`+S+`$`,`g`+A),z=new RegExp(b,`gi`+A),B=(e,t=!1)=>{let n=[];e=e.replace(M,e=>(n.push(e),a)),e=e.replace(R,``),t||(e=D(e)),F&&(e=e.replace(I,e=>e[0]+` `+e[1]));let r=0;return e.split(L).filter(e=>e!=``).map(e=>e===a?n[r++]:e)},V=/[^\d]+|\d+/g,H=(t,n=0,a=!1)=>{let o=B(t);if(o.length==0)return[];let s=Array(o.length).fill(``);o=o.map((e,t)=>e.replace(z,e=>(s[t]=e,``)));let c;if(p==1)c=o.map((e,t)=>{if(e[0]===`"`)return i(e.slice(1,-1));let n=``;for(let r of e.matchAll(V)){let e=r[0],{intraSlice:i,intraIns:a,intraSub:o,intraTrn:c,intraDel:l}=P(e);if(a+o+c+l==0)n+=e+s[t];else{let[r,d]=i,f=e.slice(0,r),p=e.slice(d),m=e.slice(r,d);a==1&&f.length==1&&f!=m[0]&&(f+=`(?!`+f+`)`);let h=m.length,g=[e];if(o)for(let e=0;e<h;e++)g.push(f+m.slice(0,e)+T+m.slice(e+1)+p);if(c)for(let e=0;e<h-1;e++)m[e]!=m[e+1]&&g.push(f+m.slice(0,e)+m[e+1]+m[e]+m.slice(e+2)+p);if(l)for(let e=0;e<h;e++)g.push(f+m.slice(0,e+1)+`?`+m.slice(e+1)+p);if(a){let e=u(T,1);for(let t=0;t<h;t++)g.push(f+m.slice(0,t)+e+m.slice(t)+p)}n+=`(?:`+g.join(`|`)+`)`+s[t]}}return n});else{let e=u(T,g);n==2&&g>0&&(e=`)(`+e+`)(`),c=o.map((t,n)=>t[0]===`"`?i(t.slice(1,-1)):t.split(``).map((e,t,n)=>(g==1&&t==0&&n.length>1&&e!=n[t+1]&&(e+=`(?!`+e+`)`),e)).join(e)+s[n])}let l=r==2?d:``,m=f==2?d:``,h=m+u(e.interChars,e.interIns)+l;return n>0?a?c=l+`(`+c.join(`)`+m+`|`+l+`(`)+`)`+m:(c=`(`+c.join(`)(`+h+`)(`)+`)`,c=`(.??`+l+`)`+c+`(`+m+`.*)`):(c=c.join(h),c=l+c+m),[new RegExp(c,`i`+A),o,s]},U=(e,t,n)=>{let[r]=H(t);if(r==null)return null;let i=[];if(n!=null)for(let t=0;t<n.length;t++){let a=n[t];r.test(e[a])&&i.push(a)}else for(let t=0;t<e.length;t++)r.test(e[t])&&i.push(t);return i},W=!!C,G=new RegExp(w,A),K=new RegExp(C,A),q=(t,n,i)=>{let[a,o,s]=H(i,1),c=B(i,!0),[l]=H(i,2),u=o.length,d=Array(u),p=Array(u);for(let e=0;e<u;e++){let t=o[e],n=c[e],r=t[0]==`"`?t.slice(1,-1):t+s[e],i=n[0]==`"`?n.slice(1,-1):n+s[e];d[e]=r,p[e]=i}let m=t.length,h=Array(m).fill(0),g={idx:Array(m),start:h.slice(),chars:h.slice(),cases:h.slice(),terms:h.slice(),interIns:h.slice(),intraIns:h.slice(),interLft2:h.slice(),interRgt2:h.slice(),interLft1:h.slice(),interRgt1:h.slice(),ranges:Array(m)},_=r==1||f==1,v=0;for(let i=0;i<t.length;i++){let o=n[t[i]],s=o.match(a),c=s.index+s[1].length,m=c,h=!1,y=0,b=0,x=0,S=0,C=0,w=0,T=0,E=0,O=0,k=[];for(let t=0,n=2;t<u;t++,n+=2){let i=D(s[n]),a=d[t],l=p[t],g=a.length,v=i.length,j=i==a;if(s[n]==l&&T++,!j&&s[n+1].length>=g){let e=D(s[n+1]).indexOf(a);e>-1&&(k.push(m,v,e,g),m+=J(s,n,e,g),i=a,v=g,j=!0,t==0&&(c=m))}if(_||j){let e=m-1,l=m+v,u=!1,d=!1;if(e==-1||G.test(o[e]))j&&y++,u=!0;else{if(r==2){h=!0;break}if(W&&K.test(o[e]+o[e+1]))j&&b++,u=!0;else if(r==1){let e=s[n+1],r=m+v;if(e.length>=g){let l=0,d=!1,f=new RegExp(a,`ig`+A),p;for(;p=f.exec(e);){l=p.index;let e=r+l,t=e-1;if(t==-1||G.test(o[t])){y++,d=!0;break}else if(K.test(o[t]+o[e])){b++,d=!0;break}}d&&(u=!0,k.push(m,v,l,g),m+=J(s,n,l,g),i=a,v=g,j=!0,t==0&&(c=m))}if(!u){h=!0;break}}}if(l==o.length||G.test(o[l]))j&&x++,d=!0;else{if(f==2){h=!0;break}if(W&&K.test(o[l-1]+o[l]))j&&S++,d=!0;else if(f==1){h=!0;break}}j&&(C+=g,u&&d&&w++)}if(v>g&&(O+=v-g),t>0&&(E+=s[n-1].length),!e.intraFilt(a,i,m)){h=!0;break}t<u-1&&(m+=v+s[n+1].length)}if(!h){g.idx[v]=t[i],g.interLft2[v]=y,g.interLft1[v]=b,g.interRgt2[v]=x,g.interRgt1[v]=S,g.chars[v]=C,g.terms[v]=w,g.cases[v]=T,g.interIns[v]=E,g.intraIns[v]=O,g.start[v]=c;let e=o.match(l),n=e.index+e[1].length,r=k.length,a=r>0?0:1/0,s=r-4;for(let t=2;t<e.length;){let r=e[t].length;if(a<=s&&k[a]==n){let r=k[a+1],i=k[a+2],o=k[a+3],s=t,c=``;for(let t=0;t<r;s++)c+=e[s],t+=e[s].length;e.splice(t,s-t,c),n+=J(e,t,i,o),a+=4}else n+=r,t++}n=e.index+e[1].length;let u=g.ranges[v]=[],d=n,f=n;for(let t=2;t<e.length;t++){let r=e[t].length;n+=r,t%2==0?f=n:r>0&&(u.push(d,f),d=f=n)}f>d&&u.push(d,f),v++}}if(v<t.length)for(let e in g)g[e]=g[e].slice(0,v);return g},J=(e,t,n,r)=>{let i=e[t]+e[t+1].slice(0,n);return e[t-1]+=i,e[t]=e[t+1].slice(n,n+r),e[t+1]=e[t+1].slice(n+r),i.length},Y=(t,n,r,a=1e3,s)=>{r=r?r===!0?5:r:0;let c=null,l=null,u=[];n=n.replace(N,e=>{let t=e.trim().slice(1);return t=t[0]===`"`?i(t.slice(1,-1)):t.replace(o,``),t!=``&&u.push(t),``});let d=B(n),f;if(u.length>0){if(f=new RegExp(u.join(`|`),`i`+A),d.length==0){let e=[];for(let n=0;n<t.length;n++)f.test(t[n])||e.push(n);return[e,null,null]}}else if(d.length==0)return[null,null,null];if(r>0){let e=B(n);if(e.length>1){let n=e.slice().sort((e,t)=>t.length-e.length);for(let e=0;e<n.length;e++){if(s?.length==0)return[[],null,null];s=U(t,n[e],s)}if(e.length>r)return[s,null,null];c=m(e).map(e=>e.join(` `)),l=[];let i=new Set;for(let e=0;e<c.length;e++)if(i.size<s.length){let n=s.filter(e=>!i.has(e)),r=U(t,c[e],n);for(let e=0;e<r.length;e++)i.add(r[e]);l.push(r)}else l.push([])}}c??(c=[n],l=[s?.length>0?s:U(t,n)]);let p=null,h=null;if(u.length>0&&(l=l.map(e=>e.filter(e=>!f.test(t[e])))),l.reduce((e,t)=>e+t.length,0)<=a){p={},h=[];for(let n=0;n<l.length;n++){let r=l[n];if(r==null||r.length==0)continue;let i=c[n],a=q(r,t,i),o=e.sort(a,t,i,O);if(n>0)for(let e=0;e<o.length;e++)o[e]+=h.length;for(let e in a)p[e]=(p[e]??[]).concat(a[e]);h=h.concat(o)}}return[[].concat(...l),p,h]};return{search:(...e)=>Y(...e),split:B,filter:U,info:q,sort:e.sort}}let p=(()=>{let e={A:`ÁÀÃÂÄĄĂÅ`,a:`áàãâäąăå`,E:`ÉÈÊËĖĚ`,e:`éèêëęě`,I:`ÍÌÎÏĮİ`,i:`íìîïįı`,O:`ÓÒÔÕÖ`,o:`óòôõö`,U:`ÚÙÛÜŪŲŮŰ`,u:`úùûüūųůű`,C:`ÇČĆ`,c:`çčć`,D:`Ď`,d:`ď`,G:`Ğ`,g:`ğ`,L:`Ł`,l:`ł`,N:`ÑŃŇ`,n:`ñńň`,S:`ŠŚȘŞ`,s:`šśșş`,T:`ŢȚŤ`,t:`ţțť`,Y:`Ý`,y:`ý`,Z:`ŻŹŽ`,z:`żźž`},t={},n=``;for(let r in e)e[r].split(``).forEach(e=>{n+=e,t[e]=r});let r=RegExp(`[${n}]`,`g`),i=e=>t[e];return e=>{if(typeof e==`string`)return e.replace(r,i);let t=Array(e.length);for(let n=0;n<e.length;n++)t[n]=e[n].replace(r,i);return t}})();function m(e){e=e.slice();let t=e.length,n=[e.slice()],r=Array(t).fill(0),i=1,a,o;for(;i<t;)r[i]<i?(a=i%2&&r[i],o=e[i],e[i]=e[a],e[a]=o,++r[i],i=1,n.push(e.slice())):(r[i]=0,++i);return n}let h=(e,t)=>t?`<mark>${e}</mark>`:e,g=(e,t)=>e+t;function _(e,t,n=h,r=``,i=g){r=i(r,n(e.substring(0,t[0]),!1))??r;for(let a=0;a<t.length;a+=2){let o=t[a],s=t[a+1];r=i(r,n(e.substring(o,s),!0))??r,a<t.length-3&&(r=i(r,n(e.substring(t[a+1],t[a+2]),!1))??r)}return r=i(r,n(e.substring(t[t.length-1]),!1))??r,r}f.latinize=p,f.permute=e=>m([...Array(e.length).keys()]).sort((e,t)=>{for(let n=0;n<e.length;n++)if(e[n]!=t[n])return e[n]-t[n];return 0}).map(t=>t.map(t=>e[t])),f.highlight=_,t.exports=f}))(),1),w=class{documents=[];fuzzy;lastSearchInfo=new Map;constructor(){this.fuzzy=new C.default({unicode:!0})}async buildIndex(t){let r=await p(`**/*.md`,{cwd:t});if(r.length!==0){this.documents=[];for(let i of r)try{let r=s(t,i),a=e(u(r,`utf-8`)),c=a.split(`
2
+ `)[0]?.replace(/^#+\s*/,``).trim()||o(i,`.md`).replace(/[-_]/g,` `),l=n(i);this.documents.push({id:i,title:c,content:a,source:l,file_path:r,metadata:{file_name:o(i),file_path:i}})}catch(e){console.warn(`Warning: Failed to index file ${i}:`,e)}}}async search(e,t={}){let{topK:n=5,where:r}=t;if(this.documents.length===0)return[];let i=this.documents;if(r?.source&&(i=this.documents.filter(e=>e.source===r.source)),i.length===0)return[];let a=[],o=new Set,s=i.map(e=>e.title),c=i.map(e=>e.content),l=i.map(e=>e.metadata.file_name);return this.fuzzySearch(s,e).forEach(e=>{let t=i[e];t&&!o.has(t.id)&&(o.add(t.id),a.push({id:t.id,title:t.title,content:t.content,source:t.source,file_path:t.file_path,score:1,metadata:{...t.metadata,matchType:`title`}}))}),this.fuzzySearch(c,e).forEach(e=>{let t=i[e];t&&!o.has(t.id)&&(o.add(t.id),a.push({id:t.id,title:t.title,content:t.content,source:t.source,file_path:t.file_path,score:.7,metadata:{...t.metadata,matchType:`content`}}))}),this.fuzzySearch(l,e).forEach(e=>{let t=i[e];t&&!o.has(t.id)&&(o.add(t.id),a.push({id:t.id,title:t.title,content:t.content,source:t.source,file_path:t.file_path,score:.5,metadata:{...t.metadata,matchType:`filename`}}))}),a.sort((e,t)=>t.score-e.score).slice(0,n)}fuzzySearch(e,t){return this.fuzzySearchWithInfo(e,t).indices}fuzzySearchWithInfo(e,t){if(!t||t.length<1||!e||e.length===0)return{indices:[],info:null};let n=this.fuzzy.filter(e,t);if(!n||n.length===0)return{indices:[],info:null};let r=this.fuzzy.info(n,e,t),i=this.fuzzy.sort(r,e,t),a=`${t}-${JSON.stringify(e.slice(0,3))}`;return this.lastSearchInfo.set(a,r),{indices:i.map(e=>r.idx[e]),info:r}}isBuilt(){return this.documents.length>0}async getStats(){return{totalDocuments:this.documents.length}}getBackendInfo(){return{backendId:`ufuzzy`,mode:`fuzzy`,supportsPersistence:!1,supportsEmbeddings:!1}}async getIndexState(){return{backendId:`ufuzzy`,documentCount:this.documents.length,builtAt:this.documents.length>0?new Date().toISOString():void 0}}async searchByPriority(e,t=5){return this.search(e,{topK:t})}clearIndex(){this.documents=[],this.lastSearchInfo.clear()}};const T=new Set([`a`,`an`,`and`,`for`,`how`,`in`,`of`,`on`,`or`,`the`,`to`,`with`]);var E=class{options;index=null;documents=[];indexState=null;constructor(e){this.options=e}async initialize(){this.index||(this.index=this.createIndex(),this.loadPersistedIndexIfAvailable())}async buildIndex(e){await this.initialize();let t=await this.loadDocuments(e),n=this.calculateReferencesHash(t);this.indexState?.referencesHash===n&&this.index||(this.index=this.createIndex(),this.index.addAll(t),this.documents=t,this.indexState={backendId:`minisearch`,referencesHash:n,builtAt:new Date().toISOString(),documentCount:t.length,rulesetVersion:r.minisearch},this.persistIndex())}async search(e,t={}){if(await this.initialize(),!this.index)return[];let{topK:n=5,where:r}=t,i=this.index.search(e,{boost:{title:3},prefix:!0,fuzzy:.15,filter:e=>r?.source?e.source===r.source:!0});return this.rerankResults(i,e).slice(0,n).map(e=>({id:String(e.id),title:String(e.title),content:this.documents.find(t=>t.id===String(e.id))?.content||``,source:e.source,file_path:String(e.file_path),score:Number(e.score),metadata:{match:e.match,queryTerms:e.queryTerms,matchedTerms:e.terms,backendId:`minisearch`,rawScore:e.score,rerankScore:e.rerankScore,titleCoverage:e.titleCoverage,contentCoverage:e.contentCoverage,pathCoverage:e.pathCoverage,termDensity:e.termDensity,phraseCoverage:e.phraseCoverage,proximityScore:e.proximityScore}}))}getBackendInfo(){return{backendId:`minisearch`,mode:`fulltext`,supportsPersistence:!0,supportsEmbeddings:!1}}isBuilt(){return this.index!==null&&this.documents.length>0}async getStats(){return{totalDocuments:this.documents.length}}async getIndexState(){return this.indexState??{backendId:`minisearch`}}clearIndex(){this.index=this.createIndex(),this.documents=[],this.indexState=null;let e=this.getSearchDir();d(s(e,`minisearch-index.json`),{force:!0}),d(s(e,`index-state.json`),{force:!0})}createIndex(){return new m({fields:[`title`,`content`],storeFields:[`id`,`title`,`source`,`file_path`],searchOptions:{boost:{title:3},prefix:!0,fuzzy:.15}})}getSearchDir(){return s(this.options.skillDir,`search`)}ensureSearchDir(){l(this.getSearchDir(),{recursive:!0})}getIndexFilePath(){return s(this.getSearchDir(),`minisearch-index.json`)}getStateFilePath(){return s(this.getSearchDir(),`index-state.json`)}loadPersistedIndexIfAvailable(){let e=this.getIndexFilePath(),t=this.getStateFilePath();if(!(!c(e)||!c(t)))try{let n=JSON.parse(u(e,`utf-8`));this.documents=n.documents,this.index=m.loadJSON(n.index,{fields:[`title`,`content`],storeFields:[`id`,`title`,`source`,`file_path`],searchOptions:{boost:{title:3},prefix:!0,fuzzy:.15}}),this.indexState=JSON.parse(u(t,`utf-8`)),this.indexState.rulesetVersion!==r.minisearch&&(this.index=this.createIndex(),this.documents=[],this.indexState=null)}catch{this.index=this.createIndex(),this.documents=[],this.indexState=null}}persistIndex(){!this.index||!this.indexState||(this.ensureSearchDir(),f(this.getIndexFilePath(),JSON.stringify({index:JSON.stringify(this.index),documents:this.documents},null,2)),f(this.getStateFilePath(),JSON.stringify(this.indexState,null,2)))}async loadDocuments(t){return(await p(`**/*.md`,{cwd:t})).map(r=>{let i=s(t,r),a=e(u(i,`utf-8`));return{id:r,title:a.split(`
3
+ `)[0]?.replace(/^#+\s*/,``).trim()||o(r,`.md`).replace(/[-_]/g,` `),content:a,source:n(r),file_path:i}})}calculateReferencesHash(e){return t(e.flatMap(e=>[e.id,e.title,e.content,e.source,r.minisearch]))}rerankResults(e,t){let n=this.normalizeQueryTokens(t),r=this.extractQueryPhrases(t,n);return e.map(e=>{let t=this.documents.find(t=>t.id===String(e.id)),i=t?.content??``,a=this.calculatePathCoverage(t?.id??``,n),o=this.calculateCoverage(e.title,n),s=this.calculateCoverage(i,n),c=this.calculateTermDensity(i,n),l=this.calculatePhraseCoverage(i,e.title,r),u=this.calculateProximityScore(i,n),d=this.calibrateRawScore(e.score)*.17+o*.18+s*.1+a*.17+Math.min(c*1.5,1)*.08+l*.18+u*.12;return{...e,rerankScore:d,titleCoverage:o,contentCoverage:s,pathCoverage:a,termDensity:c,phraseCoverage:l,proximityScore:u}}).sort((e,t)=>t.rerankScore===e.rerankScore?t.score-e.score:t.rerankScore-e.rerankScore)}normalizeQueryTokens(e){return(e.toLowerCase().match(/[\p{L}\p{N}]+/gu)??[]).filter(e=>!T.has(e))}calculateCoverage(e,t){if(t.length===0)return 0;let n=e.toLowerCase().match(/[\p{L}\p{N}]+/gu)??[];if(n.length===0)return 0;let r=0;for(let e of t)n.some(t=>this.tokensLooselyMatch(t,e))&&(r+=1);return r/t.length}calculatePathCoverage(e,t){if(t.length===0)return 0;let n=e.toLowerCase().replace(/\.md$/g,``).split(/[^a-z0-9]+/g).filter(Boolean);if(n.length===0)return 0;let r=0;for(let e of t)n.some(t=>this.tokensLooselyMatch(t,e))&&(r+=1);return r/t.length}calculateTermDensity(e,t){if(t.length===0)return 0;let n=e.toLowerCase().match(/[\p{L}\p{N}]+/gu)??[];if(n.length===0)return 0;let r=0;for(let e of t)n.some(t=>this.tokensLooselyMatch(t,e))&&(r+=1);return r/Math.sqrt(n.length)}extractQueryPhrases(e,t){let n=(e.toLowerCase().match(/[a-z0-9_/-]+/g)??[]).filter(e=>/[/_-]/.test(e)),r=t.length>=2?t.slice(0,-1).map((e,n)=>[`${e} ${t[n+1]}`,`${e}/${t[n+1]}`,`${e}_${t[n+1]}`,`${e}-${t[n+1]}`,`${e}${t[n+1]}`]):[],i=t.length>=2?[t.join(` `)]:[];return Array.from(new Set([...n,...i,...r.flat()].filter(e=>e.length>=8&&!T.has(e)&&e.split(/[\s/_-]+/).filter(Boolean).length>=2)))}calculatePhraseCoverage(e,t,n){if(n.length===0)return 0;let r=t.toLowerCase(),i=e.toLowerCase(),a=0;for(let e of n){if(r.includes(e)){a+=1;continue}i.includes(e)&&(a+=.8)}return Math.min(a/n.length,1)}calculateProximityScore(e,t){if(t.length<2)return 0;let n=e.toLowerCase();if(n.length===0)return 0;let r=t.map(e=>this.findSubstringPositions(n,e)).filter(e=>e.length>0);if(r.length<2)return 0;let i=1/0;for(let e=0;e<r.length-1;e++)for(let t of r[e])for(let n of r[e+1]){let e=Math.abs(n-t);e<i&&(i=e)}return Number.isFinite(i)?1/(1+i/24):0}findSubstringPositions(e,t){let n=[],r=0;for(;r<e.length;){let i=e.indexOf(t,r);if(i===-1)break;n.push(i),r=i+1}return n}calibrateRawScore(e){return e<=0?0:e/(e+3)}tokensLooselyMatch(e,t){if(e===t)return!0;if(e.length>=5&&t.length>=5){let[n,r]=e.length<=t.length?[e,t]:[t,e];if(r.startsWith(n)&&r.length-n.length<=3)return!0}return e===t.slice(0,-1)||t===e.slice(0,-1)}},D=class{fuzzyAdapter;miniSearchAdapter;options;lastBackendInfo=null;lastDecisionReason=null;constructor(e,t={}){this.options=e,this.fuzzyAdapter=t.fuzzyAdapter??new w,this.miniSearchAdapter=t.fulltextAdapter??new E({skillDir:this.options.skillDir})}async buildIndex(e){await this.miniSearchAdapter.buildIndex(e),await this.fuzzyAdapter.buildIndex(e)}async search(e,t={}){let{topK:n=5,where:r}=t,i=this.options.qualityThreshold||.55,a=await this.miniSearchAdapter.search(e,{topK:n,where:r}),o=this.evaluateSearchQuality(a,e);if(o.score>=i){let e=this.miniSearchAdapter.getBackendInfo();return this.lastBackendInfo=e,this.lastDecisionReason=`Kept fulltext results (${o.reason}, score ${o.score.toFixed(2)} >= threshold ${i.toFixed(2)})`,a.map(t=>({...t,metadata:{...t.metadata,autoBackendId:e.backendId,autoRequestedMode:`auto`,autoDecisionReason:this.lastDecisionReason}}))}let s=await this.fuzzyAdapter.search(e,{topK:n,where:r}),c=this.fuzzyAdapter.getBackendInfo();return this.lastBackendInfo=c,this.lastDecisionReason=`Fell back from fulltext to fuzzy (${o.reason}, score ${o.score.toFixed(2)} < threshold ${i.toFixed(2)})`,s.map(e=>({...e,metadata:{...e.metadata,autoBackendId:c.backendId,autoRequestedMode:`auto`,autoDecisionReason:this.lastDecisionReason}}))}evaluateSearchQuality(e,t){let n=i(e,t);return{score:n.overallScore,reason:n.reason}}isBuilt(){return this.miniSearchAdapter.isBuilt()||this.fuzzyAdapter.isBuilt()}async getStats(){let[e,t]=await Promise.all([this.miniSearchAdapter.getStats(),this.fuzzyAdapter.getStats()]);return{totalDocuments:Math.max(e.totalDocuments,t.totalDocuments)}}getBackendInfo(){return this.lastBackendInfo?{...this.lastBackendInfo,mode:`auto`}:{backendId:`minisearch`,mode:`auto`,supportsPersistence:!0,supportsEmbeddings:!1}}async getIndexState(){return await this.miniSearchAdapter.getIndexState()??{backendId:`minisearch`}}async searchByPriority(e,t=5){return this.search(e,{topK:t})}clearIndex(){this.fuzzyAdapter.clearIndex(),this.miniSearchAdapter.clearIndex(),this.lastBackendInfo=null,this.lastDecisionReason=null}};async function O(e={}){let{mode:t=`auto`,skillDir:n=``,referencesDir:r=``,config:i,adapterOptions:a={}}=e;switch(t===`chroma`?`vector`:t){case`auto`:if(!i)throw Error(`Config is required for auto mode`);return new D({skillDir:n,qualityThreshold:a.qualityThreshold??.55});case`fulltext`:return new E({skillDir:n});case`fuzzy`:return new w;case`vector`:return new(await(import(`./sqliteVectorSearchAdapter-DNkyKuZs.mjs`))).SqliteVectorSearchAdapter({skillDir:n,embedderMode:a.vectorEmbedder});default:throw Error(`Invalid search mode: ${t}. Use 'auto', 'fulltext', 'fuzzy', or 'vector'.`)}}var k=class{searchEngine=null;formatter=null;options;constructor(e){this.options=e}async initialize(){let{skillDir:e=``,referencesDir:t=``,config:n,mode:r=`auto`,adapterOptions:i={}}=this.options;this.searchEngine=await O({mode:r,skillDir:e,referencesDir:t,config:n,adapterOptions:i}),this.options.format&&(this.formatter=a(this.options.format))}async buildIndex(e){await this.ensureInitialized(),this.searchEngine&&await this.searchEngine.buildIndex(e)}async search(e,t=5,n){return await this.ensureInitialized(),this.searchEngine?this.searchEngine.search(e,{topK:t,where:n}):[]}async searchAndFormat(e,t=5,n){let r=await this.search(e,t,n);if(!this.formatter)throw Error(`No formatter configured. Use format option in constructor.`);let i={skillPath:this.options.skillPath||process.cwd(),query:e,maxPreviewLength:this.options.formatting?.maxPreviewLength||200,showFullContentThreshold:this.options.formatting?.showFullContentThreshold||.15,minScoreForPreview:this.options.formatting?.minScoreForPreview||.1,showLineNumbers:this.options.formatting?.showLineNumbers??!0};return this.formatter.format(r,i)}async isBuilt(){return await this.ensureInitialized(),this.searchEngine?.isBuilt()||!1}async getStats(){return await this.ensureInitialized(),this.searchEngine?.getStats()||{totalDocuments:0}}async getBackendInfo(){return await this.ensureInitialized(),this.searchEngine?.getBackendInfo()||null}async getIndexState(){return await this.ensureInitialized(),this.searchEngine?.getIndexState?this.searchEngine.getIndexState():null}async clearIndex(){await this.ensureInitialized(),this.searchEngine&&`clearIndex`in this.searchEngine&&await this.searchEngine.clearIndex()}async ensureInitialized(){this.searchEngine||await this.initialize()}};export{E as n,w as r,k as t};
4
+ //# sourceMappingURL=unifiedSearch-CLdazMYU.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unifiedSearch-CLdazMYU.mjs","names":["uFuzzy","reTpl","p","intraIns","intraSub","intraTrn","intraDel","intraSlice","info","lftCharIdx","m","idxAcc","i","len","terms","needle","uFuzzy","results: SearchResult[]","path","positions: number[]","backendInfo","formattingOptions: FormattingOptions"],"sources":["../node_modules/.pnpm/@leeoniya+ufuzzy@1.0.19/node_modules/@leeoniya/ufuzzy/dist/uFuzzy.cjs","../src/core/fuzzySearchAdapter.ts","../src/core/miniSearchAdapter.ts","../src/core/autoSearchAdapter.ts","../src/core/searchEngineFactory.ts","../src/core/unifiedSearch.ts"],"sourcesContent":["/**\n* Copyright (c) 2025, Leon Sorokin\n* All rights reserved. (MIT Licensed)\n*\n* uFuzzy.js (μFuzzy)\n* A tiny, efficient fuzzy matcher that doesn't suck\n* https://github.com/leeoniya/uFuzzy (v1.0.19)\n*/\n\n'use strict';\n\nconst cmp = (a, b) => a > b ? 1 : a < b ? -1 : 0;\n\nconst inf = Infinity;\n\n// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_expressions#escaping\nconst escapeRegExp = str => str.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n\n// meh, magic tmp placeholder, must be tolerant to toLocaleLowerCase(), interSplit, and intraSplit\nconst EXACT_HERE = 'eexxaacctt';\n\nconst PUNCT_RE = /\\p{P}/gu;\n\nconst LATIN_UPPER = 'A-Z';\nconst LATIN_LOWER = 'a-z';\n\nconst COLLATE_ARGS = ['en', { numeric: true, sensitivity: 'base' }];\n\nconst swapAlpha = (str, upper, lower) => str.replace(LATIN_UPPER, upper).replace(LATIN_LOWER, lower);\n\nconst OPTS = {\n\t// whether regexps use a /u unicode flag\n\tunicode: false,\n\n\talpha: null,\n\n\t// term segmentation & punct/whitespace merging\n\tinterSplit: \"[^A-Za-z\\\\d']+\",\n\tintraSplit: \"[a-z][A-Z]\",\n\n\t// inter bounds that will be used to increase lft2/rgt2 info counters\n\tinterBound: \"[^A-Za-z\\\\d]\",\n\t// intra bounds that will be used to increase lft1/rgt1 info counters\n\tintraBound: \"[A-Za-z]\\\\d|\\\\d[A-Za-z]|[a-z][A-Z]\",\n\n\t// inter-bounds mode\n\t// 2 = strict (will only match 'man' on whitepace and punct boundaries: Mega Man, Mega_Man, mega.man)\n\t// 1 = loose (plus allowance for alpha-num and case-change boundaries: MegaMan, 0007man)\n\t// 0 = any (will match 'man' as any substring: megamaniac)\n\tinterLft: 0,\n\tinterRgt: 0,\n\n\t// allowance between terms\n\tinterChars: '.',\n\tinterIns: inf,\n\n\t// allowance between chars in terms\n\tintraChars: \"[a-z\\\\d']\", // internally case-insensitive\n\tintraIns: null,\n\n\tintraContr: \"'[a-z]{1,2}\\\\b\",\n\n\t// multi-insert or single-error mode\n\tintraMode: 0,\n\n\t// single-error bounds for errors within terms, default requires exact first char\n\tintraSlice: [1, inf],\n\n\t// single-error tolerance toggles\n\tintraSub: null,\n\tintraTrn: null,\n\tintraDel: null,\n\n\t// can post-filter matches that are too far apart in distance or length\n\t// (since intraIns is between each char, it can accum to nonsense matches)\n\tintraFilt: (term, match, index) => true, // should this also accept WIP info?\n\n\ttoUpper: str => str.toLocaleUpperCase(),\n\ttoLower: str => str.toLocaleLowerCase(),\n\tcompare: null,\n\n\t// final sorting fn\n\tsort: (info, haystack, needle, compare = cmp) => {\n\t\tlet {\n\t\t\tidx,\n\t\t\tchars,\n\t\t\tterms,\n\t\t\tinterLft2,\n\t\t\tinterLft1,\n\t\t//\tinterRgt2,\n\t\t//\tinterRgt1,\n\t\t\tstart,\n\t\t\tintraIns,\n\t\t\tinterIns,\n\t\t\tcases,\n\t\t} = info;\n\n\t\treturn idx.map((v, i) => i).sort((ia, ib) => (\n\t\t\t// most contig chars matched\n\t\t\tchars[ib] - chars[ia] ||\n\t\t\t// least char intra-fuzz (most contiguous)\n\t\t\tintraIns[ia] - intraIns[ib] ||\n\t\t\t// most prefix bounds, boosted by full term matches\n\t\t\t(\n\t\t\t\t(terms[ib] + interLft2[ib] + 0.5 * interLft1[ib]) -\n\t\t\t\t(terms[ia] + interLft2[ia] + 0.5 * interLft1[ia])\n\t\t\t) ||\n\t\t\t// highest density of match (least span)\n\t\t//\tspan[ia] - span[ib] ||\n\t\t\t// highest density of match (least term inter-fuzz)\n\t\t\tinterIns[ia] - interIns[ib] ||\n\t\t\t// earliest start of match\n\t\t\tstart[ia] - start[ib] ||\n\t\t\t// case match\n\t\t\tcases[ib] - cases[ia] ||\n\t\t\t// alphabetic\n\t\t\tcompare(haystack[idx[ia]], haystack[idx[ib]])\n\t\t));\n\t},\n};\n\nconst lazyRepeat = (chars, limit) => (\n\tlimit == 0 ? '' :\n\tlimit == 1 ? chars + '??' :\n\tlimit == inf ? chars + '*?' :\n\t chars + `{0,${limit}}?`\n);\n\nconst mode2Tpl = '(?:\\\\b|_)';\n\nfunction uFuzzy(opts) {\n\topts = Object.assign({}, OPTS, opts);\n\n\tlet {\n\t\tunicode,\n\t\tinterLft,\n\t\tinterRgt,\n\t\tintraMode,\n\t\tintraSlice,\n\t\tintraIns,\n\t\tintraSub,\n\t\tintraTrn,\n\t\tintraDel,\n\t\tintraContr,\n\t\tintraSplit: _intraSplit,\n\t\tinterSplit: _interSplit,\n\t\tintraBound: _intraBound,\n\t\tinterBound: _interBound,\n\t\tintraChars,\n\t\ttoUpper,\n\t\ttoLower,\n\t\tcompare,\n\t} = opts;\n\n\tintraIns ??= intraMode;\n\tintraSub ??= intraMode;\n\tintraTrn ??= intraMode;\n\tintraDel ??= intraMode;\n\n\tcompare ??= typeof Intl == \"undefined\" ? cmp : new Intl.Collator(...COLLATE_ARGS).compare;\n\n\tlet alpha = opts.letters ?? opts.alpha;\n\n\tif (alpha != null) {\n\t\tlet upper = toUpper(alpha);\n\t\tlet lower = toLower(alpha);\n\n\t\t_interSplit = swapAlpha(_interSplit, upper, lower);\n\t\t_intraSplit = swapAlpha(_intraSplit, upper, lower);\n\t\t_interBound = swapAlpha(_interBound, upper, lower);\n\t\t_intraBound = swapAlpha(_intraBound, upper, lower);\n\t\tintraChars = swapAlpha(intraChars, upper, lower);\n\t\tintraContr = swapAlpha(intraContr, upper, lower);\n\t}\n\n\tlet uFlag = unicode ? 'u' : '';\n\n\tconst quotedAny = '\".+?\"';\n\tconst EXACTS_RE = new RegExp(quotedAny, 'gi' + uFlag);\n\tconst NEGS_RE = new RegExp(`(?:\\\\s+|^)-(?:${intraChars}+|${quotedAny})`, 'gi' + uFlag);\n\n\tlet { intraRules } = opts;\n\n\tif (intraRules == null) {\n\t\tintraRules = p => {\n\t\t\t// default is exact term matches only\n\t\t\tlet _intraSlice = OPTS.intraSlice, // requires first char\n\t\t\t\t_intraIns = 0,\n\t\t\t\t_intraSub = 0,\n\t\t\t\t_intraTrn = 0,\n\t\t\t\t_intraDel = 0;\n\n\t\t\t// only-digits strings should match exactly, else special rules for short strings\n\t\t\tif (/[^\\d]/.test(p)) {\n\t\t\t\tlet plen = p.length;\n\n\t\t\t\t// prevent junk matches by requiring stricter rules for short terms\n\t\t\t\tif (plen <= 4) {\n\t\t\t\t\tif (plen >= 3) {\n\t\t\t\t\t\t// one swap in non-first char when 3-4 chars\n\t\t\t\t\t\t_intraTrn = Math.min(intraTrn, 1);\n\n\t\t\t\t\t\t// or one insertion when 4 chars\n\t\t\t\t\t\tif (plen == 4)\n\t\t\t\t\t\t\t_intraIns = Math.min(intraIns, 1);\n\t\t\t\t\t}\n\t\t\t\t\t// else exact match when 1-2 chars\n\t\t\t\t}\n\t\t\t\t// use supplied opts\n\t\t\t\telse {\n\t\t\t\t\t_intraSlice = intraSlice;\n\t\t\t\t\t_intraIns = intraIns,\n\t\t\t\t\t_intraSub = intraSub,\n\t\t\t\t\t_intraTrn = intraTrn,\n\t\t\t\t\t_intraDel = intraDel;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tintraSlice: _intraSlice,\n\t\t\t\tintraIns: _intraIns,\n\t\t\t\tintraSub: _intraSub,\n\t\t\t\tintraTrn: _intraTrn,\n\t\t\t\tintraDel: _intraDel,\n\t\t\t};\n\t\t};\n\t}\n\n\tlet withIntraSplit = !!_intraSplit;\n\n\tlet intraSplit = new RegExp(_intraSplit, 'g' + uFlag);\n\tlet interSplit = new RegExp(_interSplit, 'g' + uFlag);\n\n\tlet trimRe = new RegExp('^' + _interSplit + '|' + _interSplit + '$', 'g' + uFlag);\n\tlet contrsRe = new RegExp(intraContr, 'gi' + uFlag);\n\n\tconst split = (needle, keepCase = false) => {\n\t\tlet exacts = [];\n\n\t\tneedle = needle.replace(EXACTS_RE, m => {\n\t\t\texacts.push(m);\n\t\t\treturn EXACT_HERE;\n\t\t});\n\n\t\tneedle = needle.replace(trimRe, '');\n\n\t\tif (!keepCase)\n\t\t\tneedle = toLower(needle);\n\n\t\tif (withIntraSplit)\n\t\t\tneedle = needle.replace(intraSplit, m => m[0] + ' ' + m[1]);\n\n\t\tlet j = 0;\n\t\treturn needle.split(interSplit).filter(t => t != '').map(v => v === EXACT_HERE ? exacts[j++] : v);\n\t};\n\n\tconst NUM_OR_ALPHA_RE = /[^\\d]+|\\d+/g;\n\n\tconst prepQuery = (needle, capt = 0, interOR = false) => {\n\t\t// split on punct, whitespace, num-alpha, and upper-lower boundaries\n\t\tlet parts = split(needle);\n\n\t\tif (parts.length == 0)\n\t\t\treturn [];\n\n\t\t// split out any detected contractions for each term that become required suffixes\n\t\tlet contrs = Array(parts.length).fill('');\n\t\tparts = parts.map((p, pi) => p.replace(contrsRe, m => {\n\t\t\tcontrs[pi] = m;\n\t\t\treturn '';\n\t\t}));\n\n\t\t// array of regexp tpls for each term\n\t\tlet reTpl;\n\n\t\t// allows single mutations within each term\n\t\tif (intraMode == 1) {\n\t\t\treTpl = parts.map((p, pi) => {\n\t\t\t\tif (p[0] === '\"')\n\t\t\t\t\treturn escapeRegExp(p.slice(1, -1));\n\n\t\t\t\tlet reTpl = '';\n\n\t\t\t\t// split into numeric and alpha parts, so numbers are only matched as following punct or alpha boundaries, without swaps or insertions\n\t\t\t\tfor (let m of p.matchAll(NUM_OR_ALPHA_RE)) {\n\t\t\t\t\tlet p = m[0];\n\n\t\t\t\t\tlet {\n\t\t\t\t\t\tintraSlice,\n\t\t\t\t\t\tintraIns,\n\t\t\t\t\t\tintraSub,\n\t\t\t\t\t\tintraTrn,\n\t\t\t\t\t\tintraDel,\n\t\t\t\t\t} = intraRules(p);\n\n\t\t\t\t\tif (intraIns + intraSub + intraTrn + intraDel == 0)\n\t\t\t\t\t\treTpl += p + contrs[pi];\n\t\t\t\t\telse {\n\t\t\t\t\t\tlet [lftIdx, rgtIdx] = intraSlice;\n\t\t\t\t\t\tlet lftChar = p.slice(0, lftIdx); // prefix\n\t\t\t\t\t\tlet rgtChar = p.slice(rgtIdx); // suffix\n\n\t\t\t\t\t\tlet chars = p.slice(lftIdx, rgtIdx);\n\n\t\t\t\t\t\t// neg lookahead to prefer matching 'Test' instead of 'tTest' in ManifestTest or fittest\n\t\t\t\t\t\t// but skip when search term contains leading repetition (aardvark, aaa)\n\t\t\t\t\t\tif (intraIns == 1 && lftChar.length == 1 && lftChar != chars[0])\n\t\t\t\t\t\t\tlftChar += '(?!' + lftChar + ')';\n\n\t\t\t\t\t\tlet numChars = chars.length;\n\n\t\t\t\t\t\tlet variants = [p];\n\n\t\t\t\t\t\t// variants with single char substitutions\n\t\t\t\t\t\tif (intraSub) {\n\t\t\t\t\t\t\tfor (let i = 0; i < numChars; i++)\n\t\t\t\t\t\t\t\tvariants.push(lftChar + chars.slice(0, i) + intraChars + chars.slice(i + 1) + rgtChar);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// variants with single transpositions\n\t\t\t\t\t\tif (intraTrn) {\n\t\t\t\t\t\t\tfor (let i = 0; i < numChars - 1; i++) {\n\t\t\t\t\t\t\t\tif (chars[i] != chars[i+1])\n\t\t\t\t\t\t\t\t\tvariants.push(lftChar + chars.slice(0, i) + chars[i+1] + chars[i] + chars.slice(i + 2) + rgtChar);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// variants with single char omissions\n\t\t\t\t\t\tif (intraDel) {\n\t\t\t\t\t\t\tfor (let i = 0; i < numChars; i++)\n\t\t\t\t\t\t\t\tvariants.push(lftChar + chars.slice(0, i + 1) + '?' + chars.slice(i + 1) + rgtChar);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// variants with single char insertions\n\t\t\t\t\t\tif (intraIns) {\n\t\t\t\t\t\t\tlet intraInsTpl = lazyRepeat(intraChars, 1);\n\n\t\t\t\t\t\t\tfor (let i = 0; i < numChars; i++)\n\t\t\t\t\t\t\t\tvariants.push(lftChar + chars.slice(0, i) + intraInsTpl + chars.slice(i) + rgtChar);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treTpl += '(?:' + variants.join('|') + ')' + contrs[pi];\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t//\tconsole.log(reTpl);\n\n\t\t\t\treturn reTpl;\n\t\t\t});\n\t\t}\n\t\telse {\n\t\t\tlet intraInsTpl = lazyRepeat(intraChars, intraIns);\n\n\t\t\t// capture at char level\n\t\t\tif (capt == 2 && intraIns > 0) {\n\t\t\t\t// sadly, we also have to capture the inter-term junk via parenth-wrapping .*?\n\t\t\t\t// to accum other capture groups' indices for \\b boosting during scoring\n\t\t\t\tintraInsTpl = ')(' + intraInsTpl + ')(';\n\t\t\t}\n\n\t\t\treTpl = parts.map((p, pi) => p[0] === '\"' ? escapeRegExp(p.slice(1, -1)) : p.split('').map((c, i, chars) => {\n\t\t\t\t// neg lookahead to prefer matching 'Test' instead of 'tTest' in ManifestTest or fittest\n\t\t\t\t// but skip when search term contains leading repetition (aardvark, aaa)\n\t\t\t\tif (intraIns == 1 && i == 0 && chars.length > 1 && c != chars[i+1])\n\t\t\t\t\tc += '(?!' + c + ')';\n\n\t\t\t\treturn c;\n\t\t\t}).join(intraInsTpl) + contrs[pi]);\n\t\t}\n\n\t//\tconsole.log(reTpl);\n\n\t\t// this only helps to reduce initial matches early when they can be detected\n\t\t// TODO: might want a mode 3 that excludes _\n\t\tlet preTpl = interLft == 2 ? mode2Tpl : '';\n\t\tlet sufTpl = interRgt == 2 ? mode2Tpl : '';\n\n\t\tlet interCharsTpl = sufTpl + lazyRepeat(opts.interChars, opts.interIns) + preTpl;\n\n\t\t// capture at word level\n\t\tif (capt > 0) {\n\t\t\tif (interOR) {\n\t\t\t\t// this is basically for doing .matchAll() occurence counting and highlighting without needing permuted ooo needles\n\t\t\t\treTpl = preTpl + '(' + reTpl.join(')' + sufTpl + '|' + preTpl + '(') + ')' + sufTpl;\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// sadly, we also have to capture the inter-term junk via parenth-wrapping .*?\n\t\t\t\t// to accum other capture groups' indices for \\b boosting during scoring\n\t\t\t\treTpl = '(' + reTpl.join(')(' + interCharsTpl + ')(') + ')';\n\t\t\t\treTpl = '(.??' + preTpl + ')' + reTpl + '(' + sufTpl + '.*)'; // nit: trailing capture here assumes interIns = Inf\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\treTpl = reTpl.join(interCharsTpl);\n\t\t\treTpl = preTpl + reTpl + sufTpl;\n\t\t}\n\n\t//\tconsole.log(reTpl);\n\n\t\treturn [new RegExp(reTpl, 'i' + uFlag), parts, contrs];\n\t};\n\n\tconst filter = (haystack, needle, idxs) => {\n\n\t\tlet [query] = prepQuery(needle);\n\n\t\tif (query == null)\n\t\t\treturn null;\n\n\t\tlet out = [];\n\n\t\tif (idxs != null) {\n\t\t\tfor (let i = 0; i < idxs.length; i++) {\n\t\t\t\tlet idx = idxs[i];\n\t\t\t\tquery.test(haystack[idx]) && out.push(idx);\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tfor (let i = 0; i < haystack.length; i++)\n\t\t\t\tquery.test(haystack[i]) && out.push(i);\n\t\t}\n\n\t\treturn out;\n\t};\n\n\tlet withIntraBound = !!_intraBound;\n\n\tlet interBound = new RegExp(_interBound, uFlag);\n\tlet intraBound = new RegExp(_intraBound, uFlag);\n\n\tconst info = (idxs, haystack, needle) => {\n\n\t\tlet [query, parts, contrs] = prepQuery(needle, 1);\n\t\tlet partsCased = split(needle, true);\n\t\tlet [queryR] = prepQuery(needle, 2);\n\t\tlet partsLen = parts.length;\n\n\t\tlet _terms = Array(partsLen);\n\t\tlet _termsCased = Array(partsLen);\n\n\t\tfor (let j = 0; j < partsLen; j++) {\n\t\t\tlet part = parts[j];\n\t\t\tlet partCased = partsCased[j];\n\n\t\t\tlet term = part[0] == '\"' ? part.slice(1, -1) : part + contrs[j];\n\t\t\tlet termCased = partCased[0] == '\"' ? partCased.slice(1, -1) : partCased + contrs[j];\n\n\t\t\t_terms[j] = term;\n\t\t\t_termsCased[j] = termCased;\n\t\t}\n\n\t\tlet len = idxs.length;\n\n\t\tlet field = Array(len).fill(0);\n\n\t\tlet info = {\n\t\t\t// idx in haystack\n\t\t\tidx: Array(len),\n\n\t\t\t// start of match\n\t\t\tstart: field.slice(),\n\t\t\t// length of match\n\t\t//\tspan: field.slice(),\n\n\t\t\t// contiguous chars matched\n\t\t\tchars: field.slice(),\n\n\t\t\t// case matched in term (via term.includes(match))\n\t\t\tcases: field.slice(),\n\n\t\t\t// contiguous (no fuzz) and bounded terms (intra=0, lft2/1, rgt2/1)\n\t\t\t// excludes terms that are contiguous but have < 2 bounds (substrings)\n\t\t\tterms: field.slice(),\n\n\t\t\t// cumulative length of unmatched chars (fuzz) within span\n\t\t\tinterIns: field.slice(), // between terms\n\t\t\tintraIns: field.slice(), // within terms\n\n\t\t\t// interLft/interRgt counters\n\t\t\tinterLft2: field.slice(),\n\t\t\tinterRgt2: field.slice(),\n\t\t\tinterLft1: field.slice(),\n\t\t\tinterRgt1: field.slice(),\n\n\t\t\tranges: Array(len),\n\t\t};\n\n\t\t// might discard idxs based on bounds checks\n\t\tlet mayDiscard = interLft == 1 || interRgt == 1;\n\n\t\tlet ii = 0;\n\n\t\tfor (let i = 0; i < idxs.length; i++) {\n\t\t\tlet mhstr = haystack[idxs[i]];\n\n\t\t\t// the matched parts are [full, junk, term, junk, term, junk]\n\t\t\tlet m = mhstr.match(query);\n\n\t\t\t// leading junk\n\t\t\tlet start = m.index + m[1].length;\n\n\t\t\tlet idxAcc = start;\n\t\t//\tlet span = m[0].length;\n\n\t\t\tlet disc = false;\n\t\t\tlet lft2 = 0;\n\t\t\tlet lft1 = 0;\n\t\t\tlet rgt2 = 0;\n\t\t\tlet rgt1 = 0;\n\t\t\tlet chars = 0;\n\t\t\tlet terms = 0;\n\t\t\tlet cases = 0;\n\t\t\tlet inter = 0;\n\t\t\tlet intra = 0;\n\n\t\t\tlet refine = [];\n\n\t\t\tfor (let j = 0, k = 2; j < partsLen; j++, k+=2) {\n\t\t\t\tlet group = toLower(m[k]);\n\t\t\t\tlet term = _terms[j];\n\t\t\t\tlet termCased = _termsCased[j];\n\t\t\t\tlet termLen = term.length;\n\t\t\t\tlet groupLen = group.length;\n\t\t\t\tlet fullMatch = group == term;\n\n\t\t\t\tif (m[k] == termCased)\n\t\t\t\t\tcases++;\n\n\t\t\t\t// this won't handle the case when an exact match exists across the boundary of the current group and the next junk\n\t\t\t\t// e.g. blob,ob when searching for 'bob' but finding the earlier `blob` (with extra insertion)\n\t\t\t\tif (!fullMatch && m[k+1].length >= termLen) {\n\t\t\t\t\t// probe for exact match in inter junk (TODO: maybe even in this matched part?)\n\t\t\t\t\tlet idxOf = toLower(m[k+1]).indexOf(term);\n\n\t\t\t\t\tif (idxOf > -1) {\n\t\t\t\t\t\trefine.push(idxAcc, groupLen, idxOf, termLen);\n\t\t\t\t\t\tidxAcc += refineMatch(m, k, idxOf, termLen);\n\t\t\t\t\t\tgroup = term;\n\t\t\t\t\t\tgroupLen = termLen;\n\t\t\t\t\t\tfullMatch = true;\n\n\t\t\t\t\t\tif (j == 0)\n\t\t\t\t\t\t\tstart = idxAcc;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (mayDiscard || fullMatch) {\n\t\t\t\t\t// does group's left and/or right land on \\b\n\t\t\t\t\tlet lftCharIdx = idxAcc - 1;\n\t\t\t\t\tlet rgtCharIdx = idxAcc + groupLen;\n\n\t\t\t\t\tlet isPre = false;\n\t\t\t\t\tlet isSuf = false;\n\n\t\t\t\t\t// prefix info\n\t\t\t\t\tif (lftCharIdx == -1 || interBound.test(mhstr[lftCharIdx])) {\n\t\t\t\t\t\tfullMatch && lft2++;\n\t\t\t\t\t\tisPre = true;\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tif (interLft == 2) {\n\t\t\t\t\t\t\tdisc = true;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (withIntraBound && intraBound.test(mhstr[lftCharIdx] + mhstr[lftCharIdx + 1])) {\n\t\t\t\t\t\t\tfullMatch && lft1++;\n\t\t\t\t\t\t\tisPre = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\tif (interLft == 1) {\n\t\t\t\t\t\t\t\t// regexps are eager, so try to improve the match by probing forward inter junk for exact match at a boundary\n\t\t\t\t\t\t\t\tlet junk = m[k+1];\n\t\t\t\t\t\t\t\tlet junkIdx = idxAcc + groupLen;\n\n\t\t\t\t\t\t\t\tif (junk.length >= termLen) {\n\t\t\t\t\t\t\t\t\tlet idxOf = 0;\n\t\t\t\t\t\t\t\t\tlet found = false;\n\t\t\t\t\t\t\t\t\tlet re = new RegExp(term, 'ig' + uFlag);\n\n\t\t\t\t\t\t\t\t\tlet m2;\n\t\t\t\t\t\t\t\t\twhile (m2 = re.exec(junk)) {\n\t\t\t\t\t\t\t\t\t\tidxOf = m2.index;\n\n\t\t\t\t\t\t\t\t\t\tlet charIdx = junkIdx + idxOf;\n\t\t\t\t\t\t\t\t\t\tlet lftCharIdx = charIdx - 1;\n\n\t\t\t\t\t\t\t\t\t\tif (lftCharIdx == -1 || interBound.test(mhstr[lftCharIdx])) {\n\t\t\t\t\t\t\t\t\t\t\tlft2++;\n\t\t\t\t\t\t\t\t\t\t\tfound = true;\n\t\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\telse if (intraBound.test(mhstr[lftCharIdx] + mhstr[charIdx])) {\n\t\t\t\t\t\t\t\t\t\t\tlft1++;\n\t\t\t\t\t\t\t\t\t\t\tfound = true;\n\t\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\tif (found) {\n\t\t\t\t\t\t\t\t\t\tisPre = true;\n\n\t\t\t\t\t\t\t\t\t\t// identical to exact term refinement pass above\n\t\t\t\t\t\t\t\t\t\trefine.push(idxAcc, groupLen, idxOf, termLen);\n\t\t\t\t\t\t\t\t\t\tidxAcc += refineMatch(m, k, idxOf, termLen);\n\t\t\t\t\t\t\t\t\t\tgroup = term;\n\t\t\t\t\t\t\t\t\t\tgroupLen = termLen;\n\t\t\t\t\t\t\t\t\t\tfullMatch = true;\n\n\t\t\t\t\t\t\t\t\t\tif (j == 0)\n\t\t\t\t\t\t\t\t\t\t\tstart = idxAcc;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tif (!isPre) {\n\t\t\t\t\t\t\t\t\tdisc = true;\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// suffix info\n\t\t\t\t\tif (rgtCharIdx == mhstr.length || interBound.test(mhstr[rgtCharIdx])) {\n\t\t\t\t\t\tfullMatch && rgt2++;\n\t\t\t\t\t\tisSuf = true;\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tif (interRgt == 2) {\n\t\t\t\t\t\t\tdisc = true;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (withIntraBound && intraBound.test(mhstr[rgtCharIdx - 1] + mhstr[rgtCharIdx])) {\n\t\t\t\t\t\t\tfullMatch && rgt1++;\n\t\t\t\t\t\t\tisSuf = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\tif (interRgt == 1) {\n\t\t\t\t\t\t\t\tdisc = true;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (fullMatch) {\n\t\t\t\t\t\tchars += termLen;\n\n\t\t\t\t\t\tif (isPre && isSuf)\n\t\t\t\t\t\t\tterms++;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (groupLen > termLen)\n\t\t\t\t\tintra += groupLen - termLen; // intraFuzz\n\n\t\t\t\tif (j > 0)\n\t\t\t\t\tinter += m[k-1].length; // interFuzz\n\n\t\t\t\t// TODO: group here is lowercased, which is okay for length cmp, but not more case-sensitive filts\n\t\t\t\tif (!opts.intraFilt(term, group, idxAcc)) {\n\t\t\t\t\tdisc = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tif (j < partsLen - 1)\n\t\t\t\t\tidxAcc += groupLen + m[k+1].length;\n\t\t\t}\n\n\t\t\tif (!disc) {\n\t\t\t\tinfo.idx[ii] = idxs[i];\n\t\t\t\tinfo.interLft2[ii] = lft2;\n\t\t\t\tinfo.interLft1[ii] = lft1;\n\t\t\t\tinfo.interRgt2[ii] = rgt2;\n\t\t\t\tinfo.interRgt1[ii] = rgt1;\n\t\t\t\tinfo.chars[ii] = chars;\n\t\t\t\tinfo.terms[ii] = terms;\n\t\t\t\tinfo.cases[ii] = cases;\n\t\t\t\tinfo.interIns[ii] = inter;\n\t\t\t\tinfo.intraIns[ii] = intra;\n\n\t\t\t\tinfo.start[ii] = start;\n\t\t\t//\tinfo.span[ii] = span;\n\n\t\t\t\t// ranges\n\t\t\t\tlet m = mhstr.match(queryR);\n\n\t\t\t\tlet idxAcc = m.index + m[1].length;\n\n\t\t\t\tlet refLen = refine.length;\n\t\t\t\tlet ri = refLen > 0 ? 0 : Infinity;\n\t\t\t\tlet lastRi = refLen - 4;\n\n\t\t\t\tfor (let i = 2; i < m.length;) {\n\t\t\t\t\tlet len = m[i].length;\n\n\t\t\t\t\tif (ri <= lastRi && refine[ri] == idxAcc) {\n\t\t\t\t\t\tlet groupLen = refine[ri+1];\n\t\t\t\t\t\tlet idxOf = refine[ri+2];\n\t\t\t\t\t\tlet termLen = refine[ri+3];\n\n\t\t\t\t\t\t// advance to end of original (full) group match that includes intra-junk\n\t\t\t\t\t\tlet j = i;\n\t\t\t\t\t\tlet v = '';\n\t\t\t\t\t\tfor (let _len = 0; _len < groupLen; j++) {\n\t\t\t\t\t\t\tv += m[j];\n\t\t\t\t\t\t\t_len += m[j].length;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tm.splice(i, j - i, v);\n\n\t\t\t\t\t\tidxAcc += refineMatch(m, i, idxOf, termLen);\n\n\t\t\t\t\t\tri += 4;\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tidxAcc += len;\n\t\t\t\t\t\ti++;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tidxAcc = m.index + m[1].length;\n\n\t\t\t\tlet ranges = info.ranges[ii] = [];\n\t\t\t\tlet from = idxAcc;\n\t\t\t\tlet to = idxAcc;\n\n\t\t\t\tfor (let i = 2; i < m.length; i++) {\n\t\t\t\t\tlet len = m[i].length;\n\n\t\t\t\t\tidxAcc += len;\n\n\t\t\t\t\tif (i % 2 == 0)\n\t\t\t\t\t\tto = idxAcc;\n\t\t\t\t\telse if (len > 0) {\n\t\t\t\t\t\tranges.push(from, to);\n\t\t\t\t\t\tfrom = to = idxAcc;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (to > from)\n\t\t\t\t\tranges.push(from, to);\n\n\t\t\t\tii++;\n\t\t\t}\n\t\t}\n\n\t\t// trim arrays\n\t\tif (ii < idxs.length) {\n\t\t\tfor (let k in info)\n\t\t\t\tinfo[k] = info[k].slice(0, ii);\n\t\t}\n\n\t\treturn info;\n\t};\n\n\tconst refineMatch = (m, k, idxInNext, termLen) => {\n\t\t// shift the current group into the prior junk\n\t\tlet prepend = m[k] + m[k+1].slice(0, idxInNext);\n\t\tm[k-1] += prepend;\n\t\tm[k] = m[k+1].slice(idxInNext, idxInNext + termLen);\n\t\tm[k+1] = m[k+1].slice(idxInNext + termLen);\n\t\treturn prepend.length;\n\t};\n\n\tconst OOO_TERMS_LIMIT = 5;\n\n\t// returns [idxs, info, order]\n\tconst _search = (haystack, needle, outOfOrder, infoThresh = 1e3, preFiltered) => {\n\t\toutOfOrder = !outOfOrder ? 0 : outOfOrder === true ? OOO_TERMS_LIMIT : outOfOrder;\n\n\t\tlet needles = null;\n\t\tlet matches = null;\n\n\t\tlet negs = [];\n\n\t\tneedle = needle.replace(NEGS_RE, m => {\n\t\t\tlet neg = m.trim().slice(1);\n\n\t\t\tneg = neg[0] === '\"' ? escapeRegExp(neg.slice(1,-1)) : neg.replace(PUNCT_RE, '');\n\n\t\t\tif (neg != '')\n\t\t\t\tnegs.push(neg);\n\n\t\t\treturn '';\n\t\t});\n\n\t\tlet terms = split(needle);\n\n\t\tlet negsRe;\n\n\t\tif (negs.length > 0) {\n\t\t\tnegsRe = new RegExp(negs.join('|'), 'i' + uFlag);\n\n\t\t\tif (terms.length == 0) {\n\t\t\t\tlet idxs = [];\n\n\t\t\t\tfor (let i = 0; i < haystack.length; i++) {\n\t\t\t\t\tif (!negsRe.test(haystack[i]))\n\t\t\t\t\t\tidxs.push(i);\n\t\t\t\t}\n\n\t\t\t\treturn [idxs, null, null];\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\t// abort search (needle is empty after pre-processing, e.g. no alpha-numeric chars)\n\t\t\tif (terms.length == 0)\n\t\t\t\treturn [null, null, null];\n\t\t}\n\n\t//\tconsole.log(negs);\n\t//\tconsole.log(needle);\n\n\t\tif (outOfOrder > 0) {\n\t\t\t// since uFuzzy is an AND-based search, we can iteratively pre-reduce the haystack by searching\n\t\t\t// for each term in isolation before running permutations on what's left.\n\t\t\t// this is a major perf win. e.g. searching \"test man ger pp a\" goes from 570ms -> 14ms\n\t\t\tlet terms = split(needle);\n\n\t\t\tif (terms.length > 1) {\n\t\t\t\t// longest -> shortest\n\t\t\t\tlet terms2 = terms.slice().sort((a, b) => b.length - a.length);\n\n\t\t\t\tfor (let ti = 0; ti < terms2.length; ti++) {\n\t\t\t\t\t// no haystack item contained all terms\n\t\t\t\t\tif (preFiltered?.length == 0)\n\t\t\t\t\t\treturn [[], null, null];\n\n\t\t\t\t\tpreFiltered = filter(haystack, terms2[ti], preFiltered);\n\t\t\t\t}\n\n\t\t\t\t// avoid combinatorial explosion by limiting outOfOrder to 5 terms (120 max searches)\n\t\t\t\t// fall back to just filter() otherwise\n\t\t\t\tif (terms.length > outOfOrder)\n\t\t\t\t\treturn [preFiltered, null, null];\n\n\t\t\t\tneedles = permute(terms).map(perm => perm.join(' '));\n\n\t\t\t\t// filtered matches for each needle excluding same matches for prior needles\n\t\t\t\tmatches = [];\n\n\t\t\t\t// keeps track of already-matched idxs to skip in follow-up permutations\n\t\t\t\tlet matchedIdxs = new Set();\n\n\t\t\t\tfor (let ni = 0; ni < needles.length; ni++) {\n\t\t\t\t\tif (matchedIdxs.size < preFiltered.length) {\n\t\t\t\t\t\t// filter further for this needle, exclude already-matched\n\t\t\t\t\t\tlet preFiltered2 = preFiltered.filter(idx => !matchedIdxs.has(idx));\n\n\t\t\t\t\t\tlet matched = filter(haystack, needles[ni], preFiltered2);\n\n\t\t\t\t\t\tfor (let j = 0; j < matched.length; j++)\n\t\t\t\t\t\t\tmatchedIdxs.add(matched[j]);\n\n\t\t\t\t\t\tmatches.push(matched);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tmatches.push([]);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// interOR\n\t//\tconsole.log(prepQuery(needle, 1, null, true));\n\n\t\t// non-ooo or ooo w/single term\n\t\tif (needles == null) {\n\t\t\tneedles = [needle];\n\t\t\tmatches = [preFiltered?.length > 0 ? preFiltered : filter(haystack, needle)];\n\t\t}\n\n\t\tlet retInfo = null;\n\t\tlet retOrder = null;\n\n\t\tif (negs.length > 0)\n\t\t\tmatches = matches.map(idxs => idxs.filter(idx => !negsRe.test(haystack[idx])));\n\n\t\tlet matchCount = matches.reduce((acc, idxs) => acc + idxs.length, 0);\n\n\t\t// rank, sort, concat\n\t\tif (matchCount <= infoThresh) {\n\t\t\tretInfo = {};\n\t\t\tretOrder = [];\n\n\t\t\tfor (let ni = 0; ni < matches.length; ni++) {\n\t\t\t\tlet idxs = matches[ni];\n\n\t\t\t\tif (idxs == null || idxs.length == 0)\n\t\t\t\t\tcontinue;\n\n\t\t\t\tlet needle = needles[ni];\n\t\t\t\tlet _info = info(idxs, haystack, needle);\n\t\t\t\tlet order = opts.sort(_info, haystack, needle, compare);\n\n\t\t\t\t// offset idxs for concat'ing infos\n\t\t\t\tif (ni > 0) {\n\t\t\t\t\tfor (let i = 0; i < order.length; i++)\n\t\t\t\t\t\torder[i] += retOrder.length;\n\t\t\t\t}\n\n\t\t\t\tfor (let k in _info)\n\t\t\t\t\tretInfo[k] = (retInfo[k] ?? []).concat(_info[k]);\n\n\t\t\t\tretOrder = retOrder.concat(order);\n\t\t\t}\n\t\t}\n\n\t\treturn [\n\t\t\t[].concat(...matches),\n\t\t\tretInfo,\n\t\t\tretOrder,\n\t\t];\n\t};\n\n\treturn {\n\t\tsearch: (...args) => {\n\t\t\tlet out = _search(...args);\n\t\t\treturn out;\n\t\t},\n\t\tsplit,\n\t\tfilter,\n\t\tinfo,\n\t\tsort: opts.sort,\n\t};\n}\n\nconst latinize = (() => {\n\tlet accents = {\n\t\tA: 'ÁÀÃÂÄĄĂÅ',\n\t\ta: 'áàãâäąăå',\n\t\tE: 'ÉÈÊËĖĚ',\n\t\te: 'éèêëęě',\n\t\tI: 'ÍÌÎÏĮİ',\n\t\ti: 'íìîïįı',\n\t\tO: 'ÓÒÔÕÖ',\n\t\to: 'óòôõö',\n\t\tU: 'ÚÙÛÜŪŲŮŰ',\n\t\tu: 'úùûüūųůű',\n\t\tC: 'ÇČĆ',\n\t\tc: 'çčć',\n\t\tD: 'Ď',\n\t\td: 'ď',\n\t\tG: 'Ğ',\n\t\tg: 'ğ',\n\t\tL: 'Ł',\n\t\tl: 'ł',\n\t\tN: 'ÑŃŇ',\n\t\tn: 'ñńň',\n\t\tS: 'ŠŚȘŞ',\n\t\ts: 'šśșş',\n\t\tT: 'ŢȚŤ',\n\t\tt: 'ţțť',\n\t\tY: 'Ý',\n\t\ty: 'ý',\n\t\tZ: 'ŻŹŽ',\n\t\tz: 'żźž'\n\t};\n\n\t// str.normalize(\"NFD\").replace(/\\p{Diacritic}/gu, \"\")\n\n\tlet accentsMap = {};\n\tlet accentsTpl = '';\n\n\tfor (let r in accents) {\n\t\taccents[r].split('').forEach(a => {\n\t\t\taccentsTpl += a;\n\t\t\taccentsMap[a] = r;\n\t\t});\n\t}\n\n\tlet accentsRe = new RegExp(`[${accentsTpl}]`, 'g');\n\tlet replacer = m => accentsMap[m];\n\n\treturn strings => {\n\t\tif (typeof strings == 'string')\n\t\t\treturn strings.replace(accentsRe, replacer);\n\n\t\tlet out = Array(strings.length);\n\t\tfor (let i = 0; i < strings.length; i++)\n\t\t\tout[i] = strings[i].replace(accentsRe, replacer);\n\t\treturn out;\n\t};\n})();\n\n// https://stackoverflow.com/questions/9960908/permutations-in-javascript/37580979#37580979\nfunction permute(arr) {\n\tarr = arr.slice();\n\n\tlet length = arr.length,\n\t\tresult = [arr.slice()],\n\t\tc = new Array(length).fill(0),\n\t\ti = 1, k, p;\n\n\twhile (i < length) {\n\t\tif (c[i] < i) {\n\t\t\tk = i % 2 && c[i];\n\t\t\tp = arr[i];\n\t\t\tarr[i] = arr[k];\n\t\t\tarr[k] = p;\n\t\t\t++c[i];\n\t\t\ti = 1;\n\t\t\tresult.push(arr.slice());\n\t\t} else {\n\t\t\tc[i] = 0;\n\t\t\t++i;\n\t\t}\n\t}\n\n\treturn result;\n}\n\nconst _mark = (part, matched) => matched ? `<mark>${part}</mark>` : part;\nconst _append = (acc, part) => acc + part;\n\nfunction highlight(str, ranges, mark = _mark, accum = '', append = _append) {\n\taccum = append(accum, mark(str.substring(0, ranges[0]), false)) ?? accum;\n\n\tfor (let i = 0; i < ranges.length; i+=2) {\n\t\tlet fr = ranges[i];\n\t\tlet to = ranges[i+1];\n\n\t\taccum = append(accum, mark(str.substring(fr, to), true)) ?? accum;\n\n\t\tif (i < ranges.length - 3)\n\t\t\taccum = append(accum, mark(str.substring(ranges[i+1], ranges[i+2]), false)) ?? accum;\n\t}\n\n\taccum = append(accum, mark(str.substring(ranges[ranges.length - 1]), false)) ?? accum;\n\n\treturn accum;\n}\n\nuFuzzy.latinize = latinize;\nuFuzzy.permute = arr => {\n\tlet idxs = permute([...Array(arr.length).keys()]).sort((a,b) => {\n\t\tfor (let i = 0; i < a.length; i++) {\n\t\t\tif (a[i] != b[i])\n\t\t\t\treturn a[i] - b[i];\n\t\t}\n\t\treturn 0;\n\t});\n\n\treturn idxs.map(pi => pi.map(i => arr[i]));\n};\nuFuzzy.highlight = highlight;\n\nmodule.exports = uFuzzy;\n","/**\n * Fuzzy Search Engine Adapter\n * Implements the SearchEngine interface using uFuzzy library\n * Clean, focused implementation - formatting handled by separate formatters\n */\n\nimport uFuzzy from '@leeoniya/ufuzzy'\nimport type {\n SearchEngine,\n SearchResult,\n SearchOptions,\n SearchBackendInfo,\n SearchIndexState,\n} from './searchAdapter'\nimport { glob } from 'glob'\nimport { readFileSync } from 'node:fs'\nimport { join, basename } from 'node:path'\nimport { detectReferenceSource, normalizeReferenceContent } from './referenceSearchUtils.js'\n\n/**\n * uFuzzy-based Search Engine Adapter\n * Clean implementation focused only on search functionality\n */\nexport class FuzzySearchAdapter implements SearchEngine {\n private documents: Array<{\n id: string\n title: string\n content: string\n source: 'user' | 'context7'\n file_path: string\n metadata: any\n }> = []\n\n private fuzzy: uFuzzy\n private lastSearchInfo: Map<string, any> = new Map()\n\n constructor() {\n // Initialize uFuzzy with sensible defaults\n this.fuzzy = new uFuzzy({\n unicode: true,\n })\n }\n\n async buildIndex(referencesDir: string): Promise<void> {\n const files = await glob('**/*.md', { cwd: referencesDir })\n\n if (files.length === 0) {\n return\n }\n\n this.documents = []\n\n for (const filePath of files) {\n try {\n const fullPath = join(referencesDir, filePath)\n const content = normalizeReferenceContent(readFileSync(fullPath, 'utf-8'))\n\n // Extract title from first line or filename\n const lines = content.split('\\n')\n const title =\n lines[0]?.replace(/^#+\\s*/, '').trim() || basename(filePath, '.md').replace(/[-_]/g, ' ')\n\n const source = detectReferenceSource(filePath)\n\n this.documents.push({\n id: filePath,\n title,\n content,\n source,\n file_path: fullPath,\n metadata: {\n file_name: basename(filePath),\n file_path: filePath,\n },\n })\n } catch (error) {\n console.warn(`Warning: Failed to index file ${filePath}:`, error)\n }\n }\n\n }\n\n async search(query: string, options: SearchOptions = {}): Promise<SearchResult[]> {\n const { topK = 5, where } = options\n\n if (this.documents.length === 0) {\n return []\n }\n\n // Filter documents by source if specified\n let searchableDocs = this.documents\n if (where?.source) {\n searchableDocs = this.documents.filter((doc) => doc.source === where.source)\n }\n\n if (searchableDocs.length === 0) {\n return []\n }\n\n const results: SearchResult[] = []\n const seenIds = new Set<string>()\n\n // Prepare search arrays\n const titles = searchableDocs.map((doc) => doc.title)\n const contents = searchableDocs.map((doc) => doc.content)\n const fileNames = searchableDocs.map((doc) => doc.metadata.file_name)\n\n // Search in titles (highest priority - score 1.0)\n const titleMatches = this.fuzzySearch(titles, query)\n titleMatches.forEach((idx) => {\n const doc = searchableDocs[idx]\n if (doc && !seenIds.has(doc.id)) {\n seenIds.add(doc.id)\n results.push({\n id: doc.id,\n title: doc.title,\n content: doc.content,\n source: doc.source,\n file_path: doc.file_path,\n score: 1.0,\n metadata: {\n ...doc.metadata,\n matchType: 'title',\n },\n })\n }\n })\n\n // Search in content (medium priority - score 0.7)\n const contentMatches = this.fuzzySearch(contents, query)\n contentMatches.forEach((idx) => {\n const doc = searchableDocs[idx]\n if (doc && !seenIds.has(doc.id)) {\n seenIds.add(doc.id)\n results.push({\n id: doc.id,\n title: doc.title,\n content: doc.content,\n source: doc.source,\n file_path: doc.file_path,\n score: 0.7,\n metadata: {\n ...doc.metadata,\n matchType: 'content',\n },\n })\n }\n })\n\n // Search in file names (lowest priority - score 0.5)\n const fileNameMatches = this.fuzzySearch(fileNames, query)\n fileNameMatches.forEach((idx) => {\n const doc = searchableDocs[idx]\n if (doc && !seenIds.has(doc.id)) {\n seenIds.add(doc.id)\n results.push({\n id: doc.id,\n title: doc.title,\n content: doc.content,\n source: doc.source,\n file_path: doc.file_path,\n score: 0.5,\n metadata: {\n ...doc.metadata,\n matchType: 'filename',\n },\n })\n }\n })\n\n // Sort by score and limit to topK\n return results.sort((a, b) => b.score - a.score).slice(0, topK)\n }\n\n /**\n * Basic fuzzy search using uFuzzy\n */\n private fuzzySearch(haystack: string[], needle: string): number[] {\n const result = this.fuzzySearchWithInfo(haystack, needle)\n return result.indices\n }\n\n /**\n * Enhanced fuzzy search that returns both indices and detailed info\n */\n private fuzzySearchWithInfo(\n haystack: string[],\n needle: string\n ): {\n indices: number[]\n info: any\n } {\n if (!needle || needle.length < 1) return { indices: [], info: null }\n if (!haystack || haystack.length === 0) return { indices: [], info: null }\n\n const idxs = this.fuzzy.filter(haystack, needle)\n\n if (!idxs || idxs.length === 0) {\n return { indices: [], info: null }\n }\n\n const info = this.fuzzy.info(idxs, haystack, needle)\n const order = this.fuzzy.sort(info, haystack, needle)\n\n // Store info for later use\n const searchKey = `${needle}-${JSON.stringify(haystack.slice(0, 3))}`\n this.lastSearchInfo.set(searchKey, info)\n\n const indices = order.map((sortedIdx: number) => {\n return info.idx[sortedIdx]\n })\n\n return { indices, info }\n }\n\n isBuilt(): boolean {\n return this.documents.length > 0\n }\n\n async getStats(): Promise<{ totalDocuments: number }> {\n return { totalDocuments: this.documents.length }\n }\n\n getBackendInfo(): SearchBackendInfo {\n return {\n backendId: 'ufuzzy',\n mode: 'fuzzy',\n supportsPersistence: false,\n supportsEmbeddings: false,\n }\n }\n\n async getIndexState(): Promise<SearchIndexState> {\n return {\n backendId: 'ufuzzy',\n documentCount: this.documents.length,\n builtAt: this.documents.length > 0 ? new Date().toISOString() : undefined,\n }\n }\n\n async searchByPriority(query: string, topK: number = 5): Promise<SearchResult[]> {\n // For fuzzy search, this is the same as regular search\n return this.search(query, { topK })\n }\n\n clearIndex(): void {\n this.documents = []\n this.lastSearchInfo.clear()\n }\n}\n","import MiniSearch from 'minisearch'\nimport { glob } from 'glob'\nimport { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs'\nimport { basename, join } from 'node:path'\nimport type {\n SearchBackendInfo,\n SearchEngine,\n SearchIndexState,\n SearchOptions,\n SearchResult,\n} from './searchAdapter.js'\nimport {\n SEARCH_RULESET_VERSIONS,\n createSearchRulesetHash,\n detectReferenceSource,\n normalizeReferenceContent,\n} from './referenceSearchUtils.js'\n\ninterface MiniSearchDocument {\n id: string\n title: string\n content: string\n source: 'user' | 'context7'\n file_path: string\n}\n\ninterface MiniSearchHit {\n id: string | number\n title: string\n source: 'user' | 'context7'\n file_path: string\n score: number\n match: Record<string, string[]>\n terms?: string[]\n queryTerms?: string[]\n}\n\nconst QUERY_STOPWORDS = new Set([\n 'a',\n 'an',\n 'and',\n 'for',\n 'how',\n 'in',\n 'of',\n 'on',\n 'or',\n 'the',\n 'to',\n 'with',\n])\n\nexport interface MiniSearchAdapterOptions {\n skillDir: string\n}\n\nexport class MiniSearchAdapter implements SearchEngine {\n private readonly options: MiniSearchAdapterOptions\n private index: MiniSearch<MiniSearchDocument> | null = null\n private documents: MiniSearchDocument[] = []\n private indexState: SearchIndexState | null = null\n\n constructor(options: MiniSearchAdapterOptions) {\n this.options = options\n }\n\n async initialize(): Promise<void> {\n if (this.index) return\n this.index = this.createIndex()\n this.loadPersistedIndexIfAvailable()\n }\n\n async buildIndex(referencesDir: string): Promise<void> {\n await this.initialize()\n\n const docs = await this.loadDocuments(referencesDir)\n const referencesHash = this.calculateReferencesHash(docs)\n\n if (this.indexState?.referencesHash === referencesHash && this.index) {\n return\n }\n\n this.index = this.createIndex()\n this.index.addAll(docs)\n this.documents = docs\n\n this.indexState = {\n backendId: 'minisearch',\n referencesHash,\n builtAt: new Date().toISOString(),\n documentCount: docs.length,\n rulesetVersion: SEARCH_RULESET_VERSIONS.minisearch,\n }\n\n this.persistIndex()\n }\n\n async search(query: string, options: SearchOptions = {}): Promise<SearchResult[]> {\n await this.initialize()\n\n if (!this.index) return []\n const { topK = 5, where } = options\n\n const rawResults = this.index.search(query, {\n boost: { title: 3 },\n prefix: true,\n fuzzy: 0.15,\n filter: (result) => {\n if (!where?.source) return true\n return result.source === where.source\n },\n }) as MiniSearchHit[]\n\n const rerankedResults = this.rerankResults(rawResults, query).slice(0, topK)\n\n return rerankedResults.map((item) => ({\n id: String(item.id),\n title: String(item.title),\n content: this.documents.find((doc) => doc.id === String(item.id))?.content || '',\n source: item.source,\n file_path: String(item.file_path),\n score: Number(item.score),\n metadata: {\n match: item.match,\n queryTerms: item.queryTerms,\n matchedTerms: item.terms,\n backendId: 'minisearch',\n rawScore: item.score,\n rerankScore: item.rerankScore,\n titleCoverage: item.titleCoverage,\n contentCoverage: item.contentCoverage,\n pathCoverage: item.pathCoverage,\n termDensity: item.termDensity,\n phraseCoverage: item.phraseCoverage,\n proximityScore: item.proximityScore,\n },\n }))\n }\n\n getBackendInfo(): SearchBackendInfo {\n return {\n backendId: 'minisearch',\n mode: 'fulltext',\n supportsPersistence: true,\n supportsEmbeddings: false,\n }\n }\n\n isBuilt(): boolean {\n return this.index !== null && this.documents.length > 0\n }\n\n async getStats(): Promise<{ totalDocuments: number }> {\n return {\n totalDocuments: this.documents.length,\n }\n }\n\n async getIndexState(): Promise<SearchIndexState> {\n return this.indexState ?? { backendId: 'minisearch' }\n }\n\n clearIndex(): void {\n this.index = this.createIndex()\n this.documents = []\n this.indexState = null\n\n const searchDir = this.getSearchDir()\n rmSync(join(searchDir, 'minisearch-index.json'), { force: true })\n rmSync(join(searchDir, 'index-state.json'), { force: true })\n }\n\n private createIndex(): MiniSearch<MiniSearchDocument> {\n return new MiniSearch<MiniSearchDocument>({\n fields: ['title', 'content'],\n storeFields: ['id', 'title', 'source', 'file_path'],\n searchOptions: {\n boost: { title: 3 },\n prefix: true,\n fuzzy: 0.15,\n },\n })\n }\n\n private getSearchDir(): string {\n return join(this.options.skillDir, 'search')\n }\n\n private ensureSearchDir(): void {\n mkdirSync(this.getSearchDir(), { recursive: true })\n }\n\n private getIndexFilePath(): string {\n return join(this.getSearchDir(), 'minisearch-index.json')\n }\n\n private getStateFilePath(): string {\n return join(this.getSearchDir(), 'index-state.json')\n }\n\n private loadPersistedIndexIfAvailable(): void {\n const indexFile = this.getIndexFilePath()\n const stateFile = this.getStateFilePath()\n if (!existsSync(indexFile) || !existsSync(stateFile)) return\n\n try {\n const serialized = JSON.parse(readFileSync(indexFile, 'utf-8')) as {\n index: string\n documents: MiniSearchDocument[]\n }\n this.documents = serialized.documents\n this.index = MiniSearch.loadJSON<MiniSearchDocument>(serialized.index, {\n fields: ['title', 'content'],\n storeFields: ['id', 'title', 'source', 'file_path'],\n searchOptions: {\n boost: { title: 3 },\n prefix: true,\n fuzzy: 0.15,\n },\n })\n this.indexState = JSON.parse(readFileSync(stateFile, 'utf-8')) as SearchIndexState\n if (\n (this.indexState as SearchIndexState & { rulesetVersion?: string }).rulesetVersion !==\n SEARCH_RULESET_VERSIONS.minisearch\n ) {\n this.index = this.createIndex()\n this.documents = []\n this.indexState = null\n }\n } catch {\n this.index = this.createIndex()\n this.documents = []\n this.indexState = null\n }\n }\n\n private persistIndex(): void {\n if (!this.index || !this.indexState) return\n this.ensureSearchDir()\n writeFileSync(\n this.getIndexFilePath(),\n JSON.stringify(\n {\n index: JSON.stringify(this.index),\n documents: this.documents,\n },\n null,\n 2\n )\n )\n writeFileSync(this.getStateFilePath(), JSON.stringify(this.indexState, null, 2))\n }\n\n private async loadDocuments(referencesDir: string): Promise<MiniSearchDocument[]> {\n const files = await glob('**/*.md', { cwd: referencesDir })\n return files.map((relativePath) => {\n const fullPath = join(referencesDir, relativePath)\n const content = normalizeReferenceContent(readFileSync(fullPath, 'utf-8'))\n const title =\n content.split('\\n')[0]?.replace(/^#+\\s*/, '').trim() ||\n basename(relativePath, '.md').replace(/[-_]/g, ' ')\n return {\n id: relativePath,\n title,\n content,\n source: detectReferenceSource(relativePath),\n file_path: fullPath,\n }\n })\n }\n\n private calculateReferencesHash(documents: MiniSearchDocument[]): string {\n return createSearchRulesetHash(\n documents.flatMap((document) => [\n document.id,\n document.title,\n document.content,\n document.source,\n SEARCH_RULESET_VERSIONS.minisearch,\n ])\n )\n }\n\n private rerankResults(\n results: MiniSearchHit[],\n query: string\n ): Array<\n MiniSearchHit & {\n rerankScore: number\n titleCoverage: number\n contentCoverage: number\n pathCoverage: number\n termDensity: number\n phraseCoverage: number\n proximityScore: number\n }\n > {\n const queryTokens = this.normalizeQueryTokens(query)\n const queryPhrases = this.extractQueryPhrases(query, queryTokens)\n\n return results\n .map((result) => {\n const document = this.documents.find((doc) => doc.id === String(result.id))\n const content = document?.content ?? ''\n const pathCoverage = this.calculatePathCoverage(document?.id ?? '', queryTokens)\n const titleCoverage = this.calculateCoverage(result.title, queryTokens)\n const contentCoverage = this.calculateCoverage(content, queryTokens)\n const termDensity = this.calculateTermDensity(content, queryTokens)\n const phraseCoverage = this.calculatePhraseCoverage(content, result.title, queryPhrases)\n const proximityScore = this.calculateProximityScore(content, queryTokens)\n const calibratedScore = this.calibrateRawScore(result.score)\n\n const rerankScore =\n calibratedScore * 0.17 +\n titleCoverage * 0.18 +\n contentCoverage * 0.1 +\n pathCoverage * 0.17 +\n Math.min(termDensity * 1.5, 1) * 0.08 +\n phraseCoverage * 0.18 +\n proximityScore * 0.12\n\n return {\n ...result,\n rerankScore,\n titleCoverage,\n contentCoverage,\n pathCoverage,\n termDensity,\n phraseCoverage,\n proximityScore,\n }\n })\n .sort((left, right) => {\n if (right.rerankScore !== left.rerankScore) {\n return right.rerankScore - left.rerankScore\n }\n\n return right.score - left.score\n })\n }\n\n private normalizeQueryTokens(query: string): string[] {\n const normalized = query.toLowerCase().match(/[\\p{L}\\p{N}]+/gu) ?? []\n return normalized.filter((token) => !QUERY_STOPWORDS.has(token))\n }\n\n private calculateCoverage(text: string, queryTokens: string[]): number {\n if (queryTokens.length === 0) {\n return 0\n }\n\n const words = text.toLowerCase().match(/[\\p{L}\\p{N}]+/gu) ?? []\n if (words.length === 0) {\n return 0\n }\n\n let matched = 0\n for (const token of queryTokens) {\n if (words.some((word) => this.tokensLooselyMatch(word, token))) {\n matched += 1\n }\n }\n\n return matched / queryTokens.length\n }\n\n private calculatePathCoverage(path: string, queryTokens: string[]): number {\n if (queryTokens.length === 0) {\n return 0\n }\n\n const pathTokens = path\n .toLowerCase()\n .replace(/\\.md$/g, '')\n .split(/[^a-z0-9]+/g)\n .filter(Boolean)\n\n if (pathTokens.length === 0) {\n return 0\n }\n\n let matched = 0\n for (const token of queryTokens) {\n if (pathTokens.some((pathToken) => this.tokensLooselyMatch(pathToken, token))) {\n matched += 1\n }\n }\n\n return matched / queryTokens.length\n }\n\n private calculateTermDensity(content: string, queryTokens: string[]): number {\n if (queryTokens.length === 0) {\n return 0\n }\n\n const words = content.toLowerCase().match(/[\\p{L}\\p{N}]+/gu) ?? []\n if (words.length === 0) {\n return 0\n }\n\n let matches = 0\n for (const token of queryTokens) {\n if (words.some((word) => this.tokensLooselyMatch(word, token))) {\n matches += 1\n }\n }\n\n return matches / Math.sqrt(words.length)\n }\n\n private extractQueryPhrases(query: string, queryTokens: string[]): string[] {\n const normalizedQuery = query.toLowerCase()\n const explicitPhrases = (normalizedQuery.match(/[a-z0-9_/-]+/g) ?? []).filter((phrase) =>\n /[/_-]/.test(phrase)\n )\n const tokenPairs =\n queryTokens.length >= 2\n ? queryTokens.slice(0, -1).map((token, index) => [\n `${token} ${queryTokens[index + 1]}`,\n `${token}/${queryTokens[index + 1]}`,\n `${token}_${queryTokens[index + 1]}`,\n `${token}-${queryTokens[index + 1]}`,\n `${token}${queryTokens[index + 1]}`,\n ])\n : []\n const multiWordPhrase = queryTokens.length >= 2 ? [queryTokens.join(' ')] : []\n\n return Array.from(\n new Set(\n [...explicitPhrases, ...multiWordPhrase, ...tokenPairs.flat()].filter(\n (phrase) =>\n phrase.length >= 8 &&\n !QUERY_STOPWORDS.has(phrase) &&\n phrase.split(/[\\s/_-]+/).filter(Boolean).length >= 2\n )\n )\n )\n }\n\n private calculatePhraseCoverage(content: string, title: string, phrases: string[]): number {\n if (phrases.length === 0) {\n return 0\n }\n\n const titleText = title.toLowerCase()\n const contentText = content.toLowerCase()\n let matched = 0\n for (const phrase of phrases) {\n if (titleText.includes(phrase)) {\n matched += 1\n continue\n }\n if (contentText.includes(phrase)) {\n matched += 0.8\n }\n }\n\n return Math.min(matched / phrases.length, 1)\n }\n\n private calculateProximityScore(content: string, queryTokens: string[]): number {\n if (queryTokens.length < 2) {\n return 0\n }\n\n const text = content.toLowerCase()\n if (text.length === 0) {\n return 0\n }\n\n const positions = queryTokens\n .map((token) => this.findSubstringPositions(text, token))\n .filter((hits) => hits.length > 0)\n\n if (positions.length < 2) {\n return 0\n }\n\n let bestSpan = Number.POSITIVE_INFINITY\n for (let i = 0; i < positions.length - 1; i++) {\n for (const start of positions[i]) {\n for (const end of positions[i + 1]) {\n const span = Math.abs(end - start)\n if (span < bestSpan) {\n bestSpan = span\n }\n }\n }\n }\n\n if (!Number.isFinite(bestSpan)) {\n return 0\n }\n\n return 1 / (1 + bestSpan / 24)\n }\n\n private findSubstringPositions(text: string, token: string): number[] {\n const positions: number[] = []\n let startIndex = 0\n\n while (startIndex < text.length) {\n const matchIndex = text.indexOf(token, startIndex)\n if (matchIndex === -1) {\n break\n }\n positions.push(matchIndex)\n startIndex = matchIndex + 1\n }\n\n return positions\n }\n\n private calibrateRawScore(score: number): number {\n if (score <= 0) {\n return 0\n }\n\n return score / (score + 3)\n }\n\n private tokensLooselyMatch(left: string, right: string): boolean {\n if (left === right) {\n return true\n }\n\n if (left.length >= 5 && right.length >= 5) {\n const [shorter, longer] = left.length <= right.length ? [left, right] : [right, left]\n if (longer.startsWith(shorter) && longer.length - shorter.length <= 3) {\n return true\n }\n }\n\n return left === right.slice(0, -1) || right === left.slice(0, -1)\n }\n}\n","/**\n * Auto Search Adapter\n * Intelligently selects and switches between search engines based on query characteristics\n * and search quality evaluation\n */\n\nimport type {\n SearchEngine,\n SearchResult,\n SearchOptions,\n SearchBackendInfo,\n SearchIndexState,\n} from './searchAdapter.js'\nimport { FuzzySearchAdapter } from './fuzzySearchAdapter.js'\nimport { MiniSearchAdapter } from './miniSearchAdapter.js'\nimport { analyzeSearchQuality } from './searchQuality.js'\n\nexport interface AutoSearchOptions {\n /** Skill directory */\n skillDir: string\n /** Quality threshold for switching engines */\n qualityThreshold?: number\n}\n\nexport interface AutoSearchDependencies {\n fulltextAdapter?: SearchEngine\n fuzzyAdapter?: SearchEngine\n}\n\n/**\n * Auto Search Adapter\n * Implements intelligent search engine selection and automatic switching\n */\nexport class AutoSearchAdapter implements SearchEngine {\n private fuzzyAdapter: FuzzySearchAdapter\n private miniSearchAdapter: MiniSearchAdapter\n private options: AutoSearchOptions\n private lastBackendInfo: SearchBackendInfo | null = null\n private lastDecisionReason: string | null = null\n\n constructor(options: AutoSearchOptions, dependencies: AutoSearchDependencies = {}) {\n this.options = options\n this.fuzzyAdapter = (dependencies.fuzzyAdapter as FuzzySearchAdapter | undefined) ?? new FuzzySearchAdapter()\n this.miniSearchAdapter =\n (dependencies.fulltextAdapter as MiniSearchAdapter | undefined) ??\n new MiniSearchAdapter({\n skillDir: this.options.skillDir,\n })\n }\n\n async buildIndex(referencesDir: string): Promise<void> {\n await this.miniSearchAdapter.buildIndex(referencesDir)\n await this.fuzzyAdapter.buildIndex(referencesDir)\n }\n\n async search(query: string, options: SearchOptions = {}): Promise<SearchResult[]> {\n const { topK = 5, where } = options\n const qualityThreshold = this.options.qualityThreshold || 0.55\n\n const fulltextResults = await this.miniSearchAdapter.search(query, { topK, where })\n\n const quality = this.evaluateSearchQuality(fulltextResults, query)\n\n if (quality.score >= qualityThreshold) {\n const backendInfo = this.miniSearchAdapter.getBackendInfo()\n this.lastBackendInfo = backendInfo\n this.lastDecisionReason = `Kept fulltext results (${quality.reason}, score ${quality.score.toFixed(2)} >= threshold ${qualityThreshold.toFixed(2)})`\n return fulltextResults.map((result) => ({\n ...result,\n metadata: {\n ...result.metadata,\n autoBackendId: backendInfo.backendId,\n autoRequestedMode: 'auto',\n autoDecisionReason: this.lastDecisionReason,\n },\n }))\n }\n\n const fuzzyResults = await this.fuzzyAdapter.search(query, { topK, where })\n const backendInfo = this.fuzzyAdapter.getBackendInfo()\n this.lastBackendInfo = backendInfo\n this.lastDecisionReason = `Fell back from fulltext to fuzzy (${quality.reason}, score ${quality.score.toFixed(2)} < threshold ${qualityThreshold.toFixed(2)})`\n return fuzzyResults.map((result) => ({\n ...result,\n metadata: {\n ...result.metadata,\n autoBackendId: backendInfo.backendId,\n autoRequestedMode: 'auto',\n autoDecisionReason: this.lastDecisionReason,\n },\n }))\n }\n\n /**\n * Evaluate search result quality\n */\n private evaluateSearchQuality(\n results: SearchResult[],\n query: string\n ): { score: number; reason: string } {\n const summary = analyzeSearchQuality(results, query)\n return { score: summary.overallScore, reason: summary.reason }\n }\n\n isBuilt(): boolean {\n return this.miniSearchAdapter.isBuilt() || this.fuzzyAdapter.isBuilt()\n }\n\n async getStats(): Promise<{ totalDocuments: number }> {\n const [fulltextStats, fuzzyStats] = await Promise.all([\n this.miniSearchAdapter.getStats(),\n this.fuzzyAdapter.getStats(),\n ])\n return {\n totalDocuments: Math.max(fulltextStats.totalDocuments, fuzzyStats.totalDocuments),\n }\n }\n\n getBackendInfo(): SearchBackendInfo {\n if (this.lastBackendInfo) {\n return {\n ...this.lastBackendInfo,\n mode: 'auto',\n }\n }\n\n return {\n backendId: 'minisearch',\n mode: 'auto',\n supportsPersistence: true,\n supportsEmbeddings: false,\n }\n }\n\n async getIndexState(): Promise<SearchIndexState> {\n return (\n (await this.miniSearchAdapter.getIndexState()) ?? {\n backendId: 'minisearch',\n }\n )\n }\n\n async searchByPriority(query: string, topK: number = 5): Promise<SearchResult[]> {\n return this.search(query, { topK })\n }\n\n clearIndex(): void {\n this.fuzzyAdapter.clearIndex()\n this.miniSearchAdapter.clearIndex()\n this.lastBackendInfo = null\n this.lastDecisionReason = null\n }\n}\n","/**\n * Search Engine Factory\n * Provides a simple way to create appropriate search engines based on options\n */\n\nimport type { SearchEngine } from './searchAdapter.js'\nimport type { SearchMode } from './searchAdapter.js'\nimport { AutoSearchAdapter } from './autoSearchAdapter.js'\nimport { FuzzySearchAdapter } from './fuzzySearchAdapter.js'\nimport { MiniSearchAdapter } from './miniSearchAdapter.js'\nimport type { SkillConfig } from '../types/index.js'\n\nexport interface SearchEngineOptions {\n /** Search mode */\n mode?: SearchMode\n /** Skill directory for file operations */\n skillDir?: string\n /** References directory containing documentation */\n referencesDir?: string\n /** Skill configuration */\n config?: SkillConfig\n /** Additional options for adapters */\n adapterOptions?: {\n /** Auto search quality threshold */\n qualityThreshold?: number\n /** Explicit vector embedder mode */\n vectorEmbedder?: string\n }\n}\n\n/**\n * Build and return appropriate search engine based on options\n */\nexport async function buildSearchEngine(options: SearchEngineOptions = {}): Promise<SearchEngine> {\n const { mode = 'auto', skillDir = '', referencesDir = '', config, adapterOptions = {} } = options\n const resolvedMode = mode === 'chroma' ? 'vector' : mode\n\n switch (resolvedMode) {\n case 'auto':\n if (!config) {\n throw new Error('Config is required for auto mode')\n }\n\n return new AutoSearchAdapter({\n skillDir,\n qualityThreshold: adapterOptions.qualityThreshold ?? 0.55,\n })\n\n case 'fulltext':\n return new MiniSearchAdapter({\n skillDir,\n })\n\n case 'fuzzy':\n return new FuzzySearchAdapter()\n\n case 'vector':\n return new (await import('./sqliteVectorSearchAdapter.js')).SqliteVectorSearchAdapter({\n skillDir,\n embedderMode: adapterOptions.vectorEmbedder,\n })\n\n default:\n throw new Error(`Invalid search mode: ${mode}. Use 'auto', 'fulltext', 'fuzzy', or 'vector'.`)\n }\n}\n","/**\n * Unified Search Engine\n * Acts as a coordinator between search adapters and formatters\n * Provides a clean interface for the complete search pipeline\n */\n\nimport { join } from 'node:path'\nimport type {\n SearchResult,\n SearchOptions,\n SearchMode,\n SearchBackendInfo,\n SearchIndexState,\n} from './searchAdapter.js'\nimport type { SearchFormatter, FormattingOptions, FormattedResult } from '../search_format/types.js'\nimport { createFormatter } from '../search_format/index.js'\nimport type { SearchEngineOptions } from './searchEngineFactory.js'\nimport { buildSearchEngine } from './searchEngineFactory.js'\n\n/**\n * Unified search options including formatting preferences\n */\nexport interface UnifiedSearchOptions extends SearchEngineOptions {\n /** Output format: list, enhanced, or custom */\n format?: 'list' | 'enhanced' | string\n /** Skill path for relative path calculation */\n skillPath?: string\n /** Formatting options */\n formatting?: {\n maxPreviewLength?: number\n showFullContentThreshold?: number\n minScoreForPreview?: number\n showLineNumbers?: boolean\n }\n}\n\n/**\n * Unified Search Engine\n * Coordinates search adapters with formatters for complete search experience\n */\nexport class UnifiedSearchEngine {\n private searchEngine: Awaited<ReturnType<typeof buildSearchEngine>> | null = null\n private formatter: SearchFormatter | null = null\n private options: UnifiedSearchOptions\n\n constructor(options: UnifiedSearchOptions) {\n this.options = options\n // Note: actual engine creation happens asynchronously\n }\n\n /**\n * Initialize the search engine\n */\n async initialize(): Promise<void> {\n const {\n skillDir = '',\n referencesDir = '',\n config,\n mode = 'auto',\n adapterOptions = {},\n } = this.options\n\n this.searchEngine = await buildSearchEngine({\n mode,\n skillDir,\n referencesDir,\n config,\n adapterOptions,\n })\n\n // Initialize formatter if format is specified\n if (this.options.format) {\n this.formatter = createFormatter(this.options.format as 'list' | 'enhanced')\n }\n }\n\n /**\n * Build search index\n */\n async buildIndex(referencesDir: string): Promise<void> {\n await this.ensureInitialized()\n if (this.searchEngine) {\n await this.searchEngine.buildIndex(referencesDir)\n }\n }\n\n /**\n * Search and optionally format results\n */\n async search(\n query: string,\n topK: number = 5,\n where?: Record<string, any>\n ): Promise<SearchResult[]> {\n await this.ensureInitialized()\n if (!this.searchEngine) {\n return []\n }\n return this.searchEngine.search(query, { topK, where })\n }\n\n /**\n * Search and return formatted results\n */\n async searchAndFormat(\n query: string,\n topK: number = 5,\n where?: Record<string, any>\n ): Promise<FormattedResult[]> {\n const results = await this.search(query, topK, where)\n\n if (!this.formatter) {\n throw new Error('No formatter configured. Use format option in constructor.')\n }\n\n const formattingOptions: FormattingOptions = {\n skillPath: this.options.skillPath || process.cwd(),\n query,\n maxPreviewLength: this.options.formatting?.maxPreviewLength || 200,\n showFullContentThreshold: this.options.formatting?.showFullContentThreshold || 0.15,\n minScoreForPreview: this.options.formatting?.minScoreForPreview || 0.1,\n showLineNumbers: this.options.formatting?.showLineNumbers ?? true,\n }\n\n return this.formatter.format(results, formattingOptions)\n }\n\n /**\n * Check if search index is built\n */\n async isBuilt(): Promise<boolean> {\n await this.ensureInitialized()\n return this.searchEngine?.isBuilt() || false\n }\n\n /**\n * Get search statistics\n */\n async getStats(): Promise<{ totalDocuments: number }> {\n await this.ensureInitialized()\n return this.searchEngine?.getStats() || { totalDocuments: 0 }\n }\n\n async getBackendInfo(): Promise<SearchBackendInfo | null> {\n await this.ensureInitialized()\n return this.searchEngine?.getBackendInfo() || null\n }\n\n async getIndexState(): Promise<SearchIndexState | null> {\n await this.ensureInitialized()\n if (!this.searchEngine?.getIndexState) return null\n return this.searchEngine.getIndexState()\n }\n\n /**\n * Clear search index\n */\n async clearIndex(): Promise<void> {\n await this.ensureInitialized()\n if (this.searchEngine && 'clearIndex' in this.searchEngine) {\n await this.searchEngine.clearIndex()\n }\n }\n\n /**\n * Ensure engine is initialized\n */\n private async ensureInitialized(): Promise<void> {\n if (!this.searchEngine) {\n await this.initialize()\n }\n }\n}\n"],"x_google_ignoreList":[0],"mappings":"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"}
@@ -0,0 +1 @@
1
+ import{t as e}from"./unifiedSearch-CLdazMYU.mjs";import"./referenceSearchUtils-DZgupMZp.mjs";import"./searchQuality-DVdHmamh.mjs";import"./search_format-RyjXACJa.mjs";export{e as UnifiedSearchEngine};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skill-creator",
3
- "version": "1.5.1",
3
+ "version": "1.5.2",
4
4
  "description": "Create claude-code-skills with documentation management",
5
5
  "bin": {
6
6
  "skill-creator": "dist/cli.mjs",
@@ -18,47 +18,65 @@
18
18
  "default": "./dist/cli.mjs"
19
19
  }
20
20
  },
21
+ "scripts": {
22
+ "pub": "pnpm publish --access publish --no-git-checks",
23
+ "build": "tsdown",
24
+ "sc": "pnpm tsx src/cli.ts",
25
+ "demo:search-tech-eval": "pnpm tsx demo/search-tech-eval/run.ts",
26
+ "dev": "tsdown --watch",
27
+ "cli": "node dist/cli.mjs",
28
+ "verify:workflow": "pnpm tsx scripts/verify-cli-workflow.ts",
29
+ "verify:installed": "pnpm tsx scripts/verify-installed-cli.ts",
30
+ "verify:linked": "pnpm tsx scripts/verify-linked-cli.ts",
31
+ "test": "vitest run",
32
+ "test:watch": "vitest",
33
+ "test:coverage": "vitest run --coverage",
34
+ "fmt": "prettier --write .",
35
+ "ts": "tsc --noEmit",
36
+ "pre-release": "pnpm tsx scripts/pre-release-check.ts",
37
+ "pre-release-check": "pnpm tsx scripts/pre-release-check.ts"
38
+ },
21
39
  "keywords": [
22
40
  "claude-code",
23
41
  "skills",
24
42
  "documentation",
25
- "chromadb",
26
43
  "search"
27
44
  ],
28
45
  "author": "Gaubee",
29
46
  "license": "MIT",
30
47
  "dependencies": {
31
48
  "@chroma-core/default-embed": "^0.1.8",
32
- "chromadb": "^3.0.17"
49
+ "@gaubee/nodekit": "^0.14.0",
50
+ "commander": "^14.0.1",
51
+ "glob": "^11.0.0",
52
+ "gradient-string": "^3.0.0",
53
+ "inquirer": "^12.10.0",
54
+ "markdown-it": "^14.1.0",
55
+ "minisearch": "^7.2.0",
56
+ "sqlite-vec": "^0.1.9",
57
+ "ts-pattern": "^5.9.0",
58
+ "zod": "^4.1.12"
33
59
  },
34
60
  "devDependencies": {
35
- "@gaubee/nodekit": "^0.14.0",
36
61
  "@leeoniya/ufuzzy": "^1.0.19",
37
62
  "@types/inquirer": "^9.0.9",
38
63
  "@types/markdown-it": "^14.1.2",
39
64
  "@types/node": "^24.9.1",
40
65
  "@vitest/coverage-v8": "^3.2.4",
41
66
  "@vitest/ui": "^3.2.4",
42
- "commander": "^14.0.1",
43
67
  "dedent": "^1.5.3",
44
68
  "fuse.js": "^7.1.0",
45
- "glob": "^11.0.0",
46
- "gradient-string": "^3.0.0",
47
- "inquirer": "^12.10.0",
48
69
  "jsdom": "^27.0.1",
49
- "markdown-it": "^14.1.0",
50
70
  "ora": "^9.0.0",
51
71
  "prettier": "^3.4.2",
52
- "ts-pattern": "^5.9.0",
53
72
  "tsdown": "^0.16.3",
54
73
  "tsx": "^4.19.2",
55
74
  "typescript": "^5.7.2",
56
- "vitest": "^3.2.4",
57
- "zod": "^4.1.12"
75
+ "vitest": "^3.2.4"
58
76
  },
59
77
  "repository": {
60
78
  "type": "git",
61
- "url": "https://github.com/gaubee/skill-creator"
79
+ "url": "git+https://github.com/jixoai/skill-creator.git"
62
80
  },
63
81
  "files": [
64
82
  "dist",
@@ -67,19 +85,5 @@
67
85
  ],
68
86
  "engines": {
69
87
  "node": ">=18.0.0"
70
- },
71
- "scripts": {
72
- "pub": "pnpm publish --access publish --no-git-checks",
73
- "build": "tsdown",
74
- "sc": "pnpm tsx src/cli.ts",
75
- "dev": "tsdown --watch",
76
- "cli": "node dist/cli.js",
77
- "test": "vitest run",
78
- "test:watch": "vitest",
79
- "test:coverage": "vitest run --coverage",
80
- "fmt": "prettier --write .",
81
- "ts": "tsc --noEmit",
82
- "pre-release": "node scripts/pre-release-check.js",
83
- "pre-release-check": "node scripts/pre-release-check.js"
84
88
  }
85
- }
89
+ }
@@ -9,20 +9,35 @@ description: |
9
9
 
10
10
  {{DESCRIPTION}}
11
11
 
12
+ <skill-package name="{{PACKAGE_NAME}}" version="{{PACKAGE_VERSION_HINT}}">
13
+ </skill-package>
14
+
12
15
  ## CLI Commands
13
16
 
14
17
  ```bash
15
18
  # Add user content
16
19
  skill-creator add-skill --pwd "{{SKILL_PATH}}" [--title "Title" --content "Content"]|[--file=*.md]
17
20
 
21
+ # Replace the closest matching user note with new knowledge
22
+ skill-creator add-skill --pwd "{{SKILL_PATH}}" --title "Replacement" --content "Content" --force
23
+
24
+ # Append a structured knowledge update into the closest matching user note
25
+ skill-creator add-skill --pwd "{{SKILL_PATH}}" --title "Update" --content "Content" --force-append
26
+
18
27
  # Search documentation
19
- skill-creator search-skill --pwd "{{SKILL_PATH}}" "query" [--mode=auto|chroma|fuzzy]
28
+ skill-creator search-skill --pwd "{{SKILL_PATH}}" "query" [--mode=auto|fulltext|fuzzy|vector]
29
+
30
+ # Build or refresh the persisted search index
31
+ skill-creator build-index --pwd "{{SKILL_PATH}}" [--mode=auto|fulltext|vector]
32
+
33
+ # Download Context7 docs from the package metadata stored in this skill
34
+ skill-creator download-context7 --pwd "{{SKILL_PATH}}"
20
35
 
21
- # Download Context7 docs
22
- skill-creator download-context7 --pwd "{{SKILL_PATH}}" <context7_library_id>
36
+ # Download Context7 docs with an explicit package override
37
+ skill-creator download-context7 --pwd "{{SKILL_PATH}}" --package "{{PACKAGE_NAME}}" --package-version "{{PACKAGE_VERSION_HINT}}"
23
38
 
24
39
  # Update Context7 docs
25
- skill-creator download-context7 --pwd "{{SKILL_PATH}}" --force [<context7_library_id>]
40
+ skill-creator download-context7 --pwd "{{SKILL_PATH}}" --force
26
41
 
27
42
  # List all Context7 projects
28
43
  skill-creator list-context7 --pwd "{{SKILL_PATH}}"
@@ -13,7 +13,7 @@ description: |
13
13
 
14
14
  ### 核心特性
15
15
 
16
- - **智能搜索**: 基于ChromaDB的语义搜索,快速定位相关信息
16
+ - **智能搜索**: 默认基于本地轻量全文检索,支持显式向量检索模式
17
17
  - **动态内容管理**: 支持添加自定义知识点,智能去重和更新
18
18
  - **Context7集成**: 自动获取和切片最新官方文档
19
19
  - **优先级管理**: 用户生成内容优先于官方文档
@@ -53,27 +53,38 @@ skill-creator search-skill --pwd="{{SKILL_PATH}}" "搜索关键词"
53
53
  # 自动模式(默认)- 智能选择搜索策略
54
54
  skill-creator search-skill --pwd="{{SKILL_PATH}}" --mode=auto "搜索关键词"
55
55
 
56
- # ChromaDB模式 - 语义化搜索,理解上下文含义
57
- skill-creator search-skill --pwd="{{SKILL_PATH}}" --mode=chroma "搜索关键词"
56
+ # Fulltext模式 - 默认全文检索,适合 API、标题、最佳实践检索
57
+ skill-creator search-skill --pwd="{{SKILL_PATH}}" --mode=fulltext "搜索关键词"
58
58
 
59
59
  # Fuzzy模式 - 关键字搜索,字符串模糊匹配
60
60
  skill-creator search-skill --pwd="{{SKILL_PATH}}" --mode=fuzzy "搜索关键词"
61
+
62
+ # Vector模式 - 显式向量检索,适合语义查询
63
+ skill-creator search-skill --pwd="{{SKILL_PATH}}" --mode=vector "搜索关键词"
61
64
  ```
62
65
 
63
66
  #### 搜索模式详解
64
67
 
65
68
  **🤖 Auto 模式(自动模式,默认)**
66
69
 
67
- - **工作原理**: 首先使用 Fuzzy 搜索进行快速匹配,如果没有找到满意结果,自动切换到 ChromaDB 进行语义搜索
70
+ - **工作原理**: 先执行默认全文检索;若结果为空或质量不足,再回退到 fuzzy 检索
68
71
  - **适用场景**:
69
72
  - 不确定使用哪种搜索方式时
70
73
  - 希望获得最佳搜索平衡时
71
74
  - 日常快速查询需求
72
75
  - **优势**: 智能切换,兼顾速度和准确性,用户体验最佳
73
76
 
74
- **🧠 ChromaDB 模式(语义搜索)**
77
+ **📚 Fulltext 模式(默认全文检索)**
78
+
79
+ - **工作原理**: 基于本地持久化索引进行字段加权、前缀与轻量模糊匹配
80
+ - **适用场景**:
81
+ - API 名称、配置项、标题、最佳实践定位
82
+ - 大多数日常文档检索
83
+ - **优势**: 无需外部服务,索引构建与查询都更轻量
84
+
85
+ **🧠 Vector 模式(显式语义搜索)**
75
86
 
76
- - **工作原理**: 基于向量数据库和语义模型,理解查询意图和上下文含义,进行概念匹配
87
+ - **工作原理**: 基于 SQLite vector 和本地 embedding 理解查询意图与上下文
77
88
  - **适用场景**:
78
89
  - 概念性查询("如何处理状态管理")
79
90
  - 功能性搜索("数据验证的方法")
@@ -113,14 +124,14 @@ skill-creator search-skill --pwd="{{SKILL_PATH}}" --mode=fuzzy "useQuery"
113
124
  skill-creator search-skill --pwd="{{SKILL_PATH}}" --mode=fuzzy "baseURL配置"
114
125
  ```
115
126
 
116
- **概念和最佳实践查询**(推荐 ChromaDB 模式):
127
+ **概念和最佳实践查询**(推荐 Vector 模式):
117
128
 
118
129
  ```bash
119
130
  # 概念性问题
120
- skill-creator search-skill --pwd="{{SKILL_PATH}}" --mode=chroma "如何优化React组件性能"
131
+ skill-creator search-skill --pwd="{{SKILL_PATH}}" --mode=vector "如何优化React组件性能"
121
132
 
122
133
  # 最佳实践
123
- skill-creator search-skill --pwd="{{SKILL_PATH}}" --mode=chroma "状态管理最佳实践"
134
+ skill-creator search-skill --pwd="{{SKILL_PATH}}" --mode=vector "状态管理最佳实践"
124
135
  ```
125
136
 
126
137
  **日常查询**(使用 Auto 模式):
@@ -139,6 +150,26 @@ skill-creator search-skill --pwd="{{SKILL_PATH}}" "路由配置" # --mode=auto
139
150
  skill-creator search-skill --pwd="{{SKILL_PATH}}" --list "搜索关键词"
140
151
  ```
141
152
 
153
+ ### 构建或刷新搜索索引
154
+
155
+ 当你希望提前构建本地持久化索引,或者在 `download-context7 --skip-indexing` 之后补建索引时,可以使用:
156
+
157
+ ```bash
158
+ # 自动模式(默认)- 构建默认持久化索引路径
159
+ skill-creator build-index --pwd="{{SKILL_PATH}}" --mode=auto
160
+
161
+ # Fulltext 模式 - 显式构建 MiniSearch 索引
162
+ skill-creator build-index --pwd="{{SKILL_PATH}}" --mode=fulltext
163
+
164
+ # Vector 模式 - 显式构建 SQLite vector 索引(要求当前 runtime 支持)
165
+ skill-creator build-index --pwd="{{SKILL_PATH}}" --mode=vector
166
+ ```
167
+
168
+ 说明:
169
+
170
+ - `--mode=fuzzy` 不支持单独预建索引,因此会被拒绝
171
+ - `--mode=vector` 需要当前 Node runtime 支持 `node:sqlite` 和 `sqlite-vec`
172
+
142
173
  **示例查询:**
143
174
 
144
175
  - "如何创建router"
@@ -155,6 +186,18 @@ skill-creator search-skill --pwd="{{SKILL_PATH}}" --list "搜索关键词"
155
186
  skill-creator add-skill --pwd="{{SKILL_PATH}}" --title "知识点标题" --content "详细内容"
156
187
  ```
157
188
 
189
+ 如果要直接替换最接近的用户知识点:
190
+
191
+ ```bash
192
+ skill-creator add-skill --pwd="{{SKILL_PATH}}" --title "替换标题" --content "详细内容" --force
193
+ ```
194
+
195
+ 如果要把新增内容作为结构化知识更新追加到最接近的用户知识点:
196
+
197
+ ```bash
198
+ skill-creator add-skill --pwd="{{SKILL_PATH}}" --title "更新标题" --content "详细内容" --force-append
199
+ ```
200
+
158
201
  **添加内容类型:**
159
202
 
160
203
  - 🚀 **最佳实践**: 项目中的实际经验和技巧
@@ -189,9 +232,8 @@ skill-creator list-skills --pwd="{{SKILL_PATH}}"
189
232
  │ └── references/
190
233
  │ ├── context7/ # 官方文档切片
191
234
  │ └── user/ # 用户自定义知识点
192
- ├── config.json # 技能配置
193
- ├── SKILL.md # 本文件
194
- └── package.json # 依赖管理
235
+ │ └── search/ # 本地搜索索引与状态
236
+ └── SKILL.md # 本文件
195
237
  ```
196
238
 
197
239
  ## 内容优先级