@cachly-dev/mcp-server 0.10.170 → 0.10.171
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/handlers/brain.js +1 -1
- package/dist/src/leser.js +1 -1
- package/glama.json +1 -1
- package/package.json +1 -1
- package/server.json +2 -2
|
@@ -14,7 +14,7 @@ ${h.join(`
|
|
|
14
14
|
|
|
15
15
|
${M.join(`
|
|
16
16
|
|
|
17
|
-
`)}`}case"smart_recall":{const g=Date.now(),{instance_id:t,query:s,threshold:S=.78,author:m="",context_files:f=[]}=q,$=Array.isArray(f)?f.filter(d=>typeof d=="string"):[],a=await V(t);It(a,void 0,2);let u=new Set,M=!1;m&&(u=await rt(a,t,m).catch(()=>new Set),M=await Ve(a,t,m).catch(()=>null)==="admin");const n=await Pe(a,["cachly:ctx:*","cachly:lesson:best:*","cachly:idx:*"],s,25),_=cs(n);let C=!1,h=null;const i=await(async()=>{if(!Ne())return Je(Vt,"kein Einbettungsdienst eingerichtet \u2014 die Suche laeuft nur ueber Woerter"),_;try{if(await _e.aktualisiere(a),_e.groesse===0)return Je(Ut,"keine einzige Lektion hat eine Einbettung \u2014 die Suche laeuft nur ueber Woerter. Nachruesten mit eingaenge-nachruesten, pruefen mit brain_doctor"),_;const d=await $e(s);if(!d?.length)return Je(Yt,"die Frage liess sich nicht einbetten \u2014 diese Suche laeuft nur ueber Woerter"),_;await Xe.aktualisiere(a),await Qe.aktualisiere(a),await Le.aktualisiere(a),await Fe.aktualisiere(a);const w=_e.groesse>0?Fe.groesse/_e.groesse:0;w<bt&&_e.groesse>0&&Cn(a,3);const v=w>=bt?$e(s,{modell:je}).catch(()=>null):null,U=_.filter(L=>L.key.startsWith("cachly:lesson:best:")).map(L=>L.key.slice(19)),K=L=>Math.max(_e.naehe(d,L),Xe.naehe(d,L)),j=[...Le.themen()],J=(j.length>0?j:_e.aehnlichste(d,500).map(L=>L.topic)).map(L=>({t:L,n:K(L)})).sort((L,ie)=>ie.n-L.n).slice(0,kt).map(L=>L.t),Q=[...new Set([...U,...J])],G=J.slice(0,3).map(L=>_e.rohvektor(L)).filter(Boolean),de=G.length?Ns(d,G):d,ke=yt(s),R=Le.statistik,B=Q.map(L=>{const ie=new Set([...yt(Le.textVon(L))].map(Es));return{naeheText:_e.naehe(d,L),naeheThema:Xe.naehe(d,L),naeheRueckkopplung:_e.naehe(de,L),seltenheitsDeckung:R?R.deckung(ke,ie):0,besterZeuge:R?R.besterZeuge(ke,ie):0}});let oe=Ts(B,As);{const L=[...oe].sort((ie,fe)=>fe-ie);h=L.length>1?L[0]-L[1]:null}if(Qe.groesse>0){const L=Q.map(fe=>{const we=Qe.besteNaehe(d,fe);return we>=Us?we:-2}),ie=wt(L);oe=oe.map((fe,we)=>fe+Vs*ie[we])}if(v){const L=await v;if(L?.length){const ie=wt(Q.map(fe=>Fe.naehe(L,fe)));oe=oe.map((fe,we)=>fe+Fs*ie[we])}}let Z=Q.map((L,ie)=>({t:L,p:oe[ie]})).sort((L,ie)=>ie.p-L.p).map(L=>L.t);if(hs()&&Z.length>1){const L=Z.slice(0,$t),ie=Z.slice($t),fe=new Map(Q.map((ge,Me)=>[ge,oe[Me]])),we=await ds(s,L.map(ge=>Le.leserTextVon(ge)));we&&(Z=[...fs(L.map(Me=>fe.get(Me)??0),we).map(Me=>L[Me]),...ie])}const X=new Map(_.map(L=>[L.key,L])),ee=[];for(const L of Z){const ie=`cachly:lesson:best:${L}`,fe=X.get(ie);if(fe){ee.push(fe),X.delete(ie);continue}const we=await a.get(ie).catch(()=>null);we&&ee.push({key:ie,content:we,score:0,matchedWords:[],wortBelege:0,finalScore:0,qualityBoost:1})}return ee.push(...X.values()),zs&&Bs({frage:s.slice(0,200),themen:Q,ausWortsuche:U.length,ausSinnsuche:J.length,obergrenze:kt,gezeigt:Z.slice(0,10)}),C=!0,ee}catch(d){return await Oe(a,Zt,`der Bedeutungspfad brach ab (${d instanceof Error?d.message:String(d)}) \u2014 diese Suche lief nur ueber Woerter`),_}})();Nt(a),_t(a,String(s??""),Date.now()-g);{const d=i.filter(w=>w.key.startsWith("cachly:lesson:best:")).slice(0,3).map(w=>w.key.slice(19));if(d.length>0){const w={ts:new Date().toISOString(),themen:d};(async()=>{const v=ft(t);await a.lpush(v,JSON.stringify(w)),await a.ltrim(v,0,ks-1)})().catch(()=>{})}{const w={ts:new Date().toISOString(),frage:s.slice(0,200),geliefert:d};(async()=>{const v=pt(t);await a.lpush(v,JSON.stringify(w)),await a.ltrim(v,0,Cs-1)})().catch(()=>{})}}if(Ne()){const d=i.filter(w=>w.key.startsWith("cachly:lesson:best:")).slice(0,3).map(w=>w.key.slice(19));_s(a,s,d,$e).catch(()=>{})}const x=[],b=i.filter(d=>d.key.startsWith("cachly:lesson:best:"));let y=0,A=0;for(const d of b.slice(0,5)){const w=await a.get(d.key).catch(()=>null);if(w){const v=O(w,null);if(v?.state==="archived")continue;if(v){const U=new Date().toISOString(),K={...v,recall_count:(v.recall_count??0)+1,last_recalled_at:U};a.set(d.key,JSON.stringify(K)).catch(()=>{});const j=at(v)?ot(v):0;if(j>0&&a.incrbyfloat(`cachly:stats:time_saved_mins:${t}`,j).catch(()=>{}),v.author&&m&&v.author!==m){y++,a.sadd(`cachly:stats:reuse_pairs:${t}`,`${m}<-${v.author}`).catch(()=>{});const J=`person:${me(m)}`,Q=`person:${me(v.author)}`;ye(a,J,"collaborates",Q,!0).catch(()=>{}),ye(a,Q,"collaborates",J,!0).catch(()=>{})}v.outcome==="success"&&A++,v.outcome==="success"&&(v.recall_count??0)>=1&&!ze(v)&&x.push({topic:d.key.replace("cachly:lesson:best:",""),severity:v.severity??"major",recall_count:(v.recall_count??0)+1,savedMins:j,ts:v.ts,author:v.author})}}}A>0&&a.set(`cachly:stats:first_recall_at:${t}`,new Date().toISOString(),"EX",365*86400,"NX").catch(()=>{}),b.length>0&&(a.incr(`cachly:stats:recalls_total:${t}`).catch(()=>{}),y>0&&a.incr(`cachly:stats:cross_author_recalls:${t}`).catch(()=>{}));const I=await ue(`/api/v1/instances/${t}`).catch(()=>null),e=!I?.tier||I.tier.toLowerCase()==="free",F=[];if(!e&&I?.vector_token&&Ne())try{const d=await $e(s),w=`https://api.cachly.dev/v1/sem/${I.vector_token}`,v=await fetch(`${w}/search`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({embedding:d,namespace:"cachly:ctx",threshold:S,top_k:5}),signal:AbortSignal.timeout(8e3)});if(v.ok){const U=await v.json();for(const K of U.filter(j=>j.found&&j.id)){const j=K.id.replace("ctx:","").split(":"),J=j[0],Q=j.slice(1).join(":"),G=`cachly:ctx:${J}:${Q}`,de=await a.get(G).catch(()=>null);F.push({key:G,similarity:K.similarity??0,content:de??"(evicted)"})}}}catch{}const k=i.map(d=>d.score),ae=k.length?Math.min(...k):0,ce=k.length&&Math.max(...k)-ae||1,he=d=>(d-ae)/ce,D=new Map;for(const d of i){const w=he(d.score);D.set(d.key,{key:d.key,content:d.content,bm25Score:w,hybridScore:w*(F.length>0?.7:1),matchedWords:d.matchedWords,matchType:"keyword",subQuery:d.subQuery,wortBelege:d.wortBelege})}for(const d of F){const w=D.get(d.key);w?(w.semScore=d.similarity,w.hybridScore=(w.bm25Score??0)*.6+d.similarity*.4,w.matchType="hybrid"):D.set(d.key,{key:d.key,content:d.content,semScore:d.similarity,hybridScore:d.similarity*.5,matchType:"semantic"})}try{const d=Ye(s).filter(U=>U.length>=4).slice(0,4),w=new Set;for(const U of d){const K=a.scanStream({match:`cachly:ckg:node:*${U}*`,count:20});await new Promise((j,J)=>{K.on("data",Q=>{for(const G of Q.slice(0,5))w.add(G.replace("cachly:ckg:node:",""))}),K.on("end",j),K.on("error",J)})}const v=new Set;for(const U of[...w].slice(0,12)){const K=await a.smembers(`cachly:ckg:idx:to:${U}`);for(const j of K.slice(0,6)){const J=await a.get(j);if(!J)continue;const Q=O(J,null);if(!Q||Q.edgeType!=="fixes"||Q.confidence<.35||v.has(Q.from))continue;v.add(Q.from);const G=`cachly:lesson:best:${Q.from}`,de=D.get(G);if(de)de.ckgScore=Math.max(de.ckgScore??0,Q.confidence),de.hybridScore=de.hybridScore*.88+Q.confidence*.12,de.matchType==="keyword"&&(de.matchType="hybrid");else{const ke=await a.get(G).catch(()=>null);if(!ke)continue;D.set(G,{key:G,content:ke,ckgScore:Q.confidence,hybridScore:Q.confidence*.3,matchType:"ckg"})}}}}catch{}if($.length>0){const d=new Set($.map(w=>w.replace(/\\/g,"/")));for(const w of D.values()){if(!w.key.startsWith("cachly:lesson:best:"))continue;const v=O(w.content,{});(Array.isArray(v.file_paths)?v.file_paths.filter(K=>typeof K=="string").map(K=>K.replace(/\\/g,"/")):[]).some(K=>d.has(K))&&(w.hybridScore*=1.15,w.contextBoost=!0)}}for(const d of D.values()){if(d.matchType!=="semantic"&&d.matchType!=="ckg")continue;const w=us({key:d.key,content:d.content});w&&(d.hybridScore*=ls(w))}const E=[...D.values()].filter(d=>{if(!d.key.startsWith("cachly:lesson:best:"))return!0;const w=Ce(d.content);return!(!Te(w)||!ct(w?.group,u,M))}).sort((d,w)=>w.hybridScore-d.hybridScore),H=[];for(const d of E){if(!d.key.startsWith("cachly:lesson:best:"))continue;const w=O(d.content,{});w.gilt_bis&&Date.parse(String(w.gilt_bis))<Date.now()&&H.push(`\u23F3 \`${d.key.replace("cachly:lesson:best:","")}\` galt bis ${String(w.gilt_bis).slice(0,10)} \u2014 Geschichte, keine aktuelle Tatsache.`);const v=O(d.content,{});if(v.pruefung_gefallen_am){const U=d.key.replace("cachly:lesson:best:",""),K=String(v.pruefung_gefallen_am).slice(0,10),j=String(v.pruefung_befund??"").trim().slice(0,200);H.push(`\u274C \`${U}\` \u2014 Pruefung am ${K} FEHLGESCHLAGEN`+(j?`: ${j}`:""))}}{const d=new Set(E.map(w=>w.key));for(let w=E.length-1;w>=0;w--){const v=E[w];if(!v.key.startsWith("cachly:lesson:best:"))continue;const U=O(v.content,{});if(!U.ersetzt_durch)continue;const K=String(U.ersetzt_grund??"").trim(),j=v.key.replace("cachly:lesson:best:","");if(d.has(`cachly:lesson:best:${U.ersetzt_durch}`))E.splice(w,1),H.push(`\u21B7 \`${j}\` verdraengt \u2014 ersetzt durch \`${U.ersetzt_durch}\`${U.ersetzt_am?` am ${U.ersetzt_am.slice(0,10)}`:""}${K?` \u2014 Grund: ${K}`:" \u2014 Grund nicht erfasst"}`);else if(await a.get(`cachly:lesson:best:${U.ersetzt_durch}`)){const J=Ce(await a.get(`cachly:lesson:best:${U.ersetzt_durch}`));Te(J)?H.push(`\u26A0\uFE0F \`${j}\` ist ersetzt durch \`${U.ersetzt_durch}\` (nicht in dieser Liste) \u2014 die gueltige Antwort: \`recall_best_solution(topic="${U.ersetzt_durch}")\``):H.push(`\u21B7 \`${j}\`: von einer nicht team-sichtbaren Fassung ersetzt \u2014 diese Fassung bleibt die beste sichtbare Antwort.`)}else H.push(`\u21A9\uFE0F \`${j}\` war ersetzt durch \`${U.ersetzt_durch}\` \u2014 der Nachfolger wurde geloescht, diese Fassung gilt wieder (bei Gelegenheit neu bestaetigen).`)}}const se=[];{await _e.aktualisiere(a).catch(()=>{});const d=E.slice(0,8).filter(w=>w.key.startsWith("cachly:lesson:best:"));for(let w=0;w<d.length;w++)for(let v=w+1;v<d.length;v++){const U=d[w].key.replace("cachly:lesson:best:",""),K=d[v].key.replace("cachly:lesson:best:",""),j=_e.rohvektor(U),J=_e.rohvektor(K);if(!j||!J)continue;let Q=0,G=0,de=0;for(let B=0;B<j.length;B++)Q+=j[B]*J[B],G+=j[B]*j[B],de+=J[B]*J[B];if((G>0&&de>0?Q/(Math.sqrt(G)*Math.sqrt(de)):0)<Ws)continue;const R=B=>{const oe=O(B??"",{});return String(oe.verified_at??oe.ts??"").slice(0,10)||"ohne Datum"};se.push(`\u26A0\uFE0F \`${U}\` (${R(d[w].content)}) und \`${K}\` (${R(d[v].content)}) sagen fast dasselbe \u2014 prueft, welche Fassung fuer euren Fall gilt; die neuere ist nicht automatisch die richtige.`)}se.length>3&&(se.length=3)}if(H.push(...se),E.length>=2){const d=E[0].hybridScore,w=E[Math.min(4,E.length-1)].hybridScore,v=Math.max(0,d-w);(async()=>{try{await a.lpush("cachly:recall:spread",JSON.stringify({ts:new Date().toISOString(),spread:Number(v.toFixed(4)),n:E.length})),await a.ltrim("cachly:recall:spread",0,199)}catch{}})()}const Y=Xt();let o="",r=null;if(Y!==null){o=Qt();const d=E[0]?.hybridScore??0,w=es(E.map(v=>({key:v.key,content:v.content,punktzahl:v.hybridScore})),o,Y,d);w.weggelassenesThema!==void 0?E.splice(0,1):w.markierungGesetzt&&E[0]&&w.treffer[0]&&(E[0]={...E[0],content:w.treffer[0].content}),r={frage:s,hoechstePunktzahl:d,themen:[],zulassungsfaehig:w.zulassungsfaehig,zulassungsGrund:w.zulassungsGrund,zuteilung:w.zuteilung,weggelassenesThema:w.weggelassenesThema,markierungGesetzt:w.markierungGesetzt}}const p=await Mt(t,ue),c=p.reached,l=St({ueberLimit:c,gesamt:E.length,volleTiefe:Zs}),T=process.env.CACHLY_RECALL_COMPACT==="1",z=(()=>{const d=Number.parseInt(process.env.CACHLY_RECALL_COMPACT_HITS??"",10);return Number.isFinite(d)?Math.min(20,Math.max(1,d)):5})(),P=T?Math.min(z,l.sichtbar):l.sichtbar,N=[`\u{1F9E0} **Smart Recall** for: _"${s}"_
|
|
17
|
+
`)}`}case"smart_recall":{const g=Date.now(),{instance_id:t,query:s,threshold:S=.78,author:m="",context_files:f=[]}=q,$=Array.isArray(f)?f.filter(d=>typeof d=="string"):[],a=await V(t);It(a,void 0,2);let u=new Set,M=!1;m&&(u=await rt(a,t,m).catch(()=>new Set),M=await Ve(a,t,m).catch(()=>null)==="admin");const n=await Pe(a,["cachly:ctx:*","cachly:lesson:best:*","cachly:idx:*"],s,25),_=cs(n);let C=!1,h=null;const i=await(async()=>{if(!Ne())return Je(Vt,"kein Einbettungsdienst eingerichtet \u2014 die Suche laeuft nur ueber Woerter"),_;try{if(await _e.aktualisiere(a),_e.groesse===0)return Je(Ut,"keine einzige Lektion hat eine Einbettung \u2014 die Suche laeuft nur ueber Woerter. Nachruesten mit eingaenge-nachruesten, pruefen mit brain_doctor"),_;const d=await $e(s);if(!d?.length)return Je(Yt,"die Frage liess sich nicht einbetten \u2014 diese Suche laeuft nur ueber Woerter"),_;await Xe.aktualisiere(a),await Qe.aktualisiere(a),await Le.aktualisiere(a),await Fe.aktualisiere(a);const w=_e.groesse>0?Fe.groesse/_e.groesse:0;w<bt&&_e.groesse>0&&Cn(a,3);const v=w>=bt?$e(s,{modell:je}).catch(()=>null):null,U=_.filter(L=>L.key.startsWith("cachly:lesson:best:")).map(L=>L.key.slice(19)),K=L=>Math.max(_e.naehe(d,L),Xe.naehe(d,L)),j=[...Le.themen()],J=(j.length>0?j:_e.aehnlichste(d,500).map(L=>L.topic)).map(L=>({t:L,n:K(L)})).sort((L,ie)=>ie.n-L.n).slice(0,kt).map(L=>L.t),Q=[...new Set([...U,...J])],G=J.slice(0,3).map(L=>_e.rohvektor(L)).filter(Boolean),de=G.length?Ns(d,G):d,ke=yt(s),R=Le.statistik,B=Q.map(L=>{const ie=new Set([...yt(Le.textVon(L))].map(Es));return{naeheText:_e.naehe(d,L),naeheThema:Xe.naehe(d,L),naeheRueckkopplung:_e.naehe(de,L),seltenheitsDeckung:R?R.deckung(ke,ie):0,besterZeuge:R?R.besterZeuge(ke,ie):0}});let oe=Ts(B,As);{const L=[...oe].sort((ie,fe)=>fe-ie);h=L.length>1?L[0]-L[1]:null}if(Qe.groesse>0){const L=Q.map(fe=>{const we=Qe.besteNaehe(d,fe);return we>=Us?we:-2}),ie=wt(L);oe=oe.map((fe,we)=>fe+Vs*ie[we])}if(v){const L=await v;if(L?.length){const ie=wt(Q.map(fe=>Fe.naehe(L,fe)));oe=oe.map((fe,we)=>fe+Fs*ie[we])}}let Z=Q.map((L,ie)=>({t:L,p:oe[ie]})).sort((L,ie)=>ie.p-L.p).map(L=>L.t);if(hs()&&Z.length>1){const L=Z.slice(0,$t),ie=Z.slice($t),fe=new Map(Q.map((ge,Me)=>[ge,oe[Me]])),we=await ds(s,L.map(ge=>Le.leserTextVon(ge)),{instanceId:t});we&&(Z=[...fs(L.map(Me=>fe.get(Me)??0),we).map(Me=>L[Me]),...ie])}const X=new Map(_.map(L=>[L.key,L])),ee=[];for(const L of Z){const ie=`cachly:lesson:best:${L}`,fe=X.get(ie);if(fe){ee.push(fe),X.delete(ie);continue}const we=await a.get(ie).catch(()=>null);we&&ee.push({key:ie,content:we,score:0,matchedWords:[],wortBelege:0,finalScore:0,qualityBoost:1})}return ee.push(...X.values()),zs&&Bs({frage:s.slice(0,200),themen:Q,ausWortsuche:U.length,ausSinnsuche:J.length,obergrenze:kt,gezeigt:Z.slice(0,10)}),C=!0,ee}catch(d){return await Oe(a,Zt,`der Bedeutungspfad brach ab (${d instanceof Error?d.message:String(d)}) \u2014 diese Suche lief nur ueber Woerter`),_}})();Nt(a),_t(a,String(s??""),Date.now()-g);{const d=i.filter(w=>w.key.startsWith("cachly:lesson:best:")).slice(0,3).map(w=>w.key.slice(19));if(d.length>0){const w={ts:new Date().toISOString(),themen:d};(async()=>{const v=ft(t);await a.lpush(v,JSON.stringify(w)),await a.ltrim(v,0,ks-1)})().catch(()=>{})}{const w={ts:new Date().toISOString(),frage:s.slice(0,200),geliefert:d};(async()=>{const v=pt(t);await a.lpush(v,JSON.stringify(w)),await a.ltrim(v,0,Cs-1)})().catch(()=>{})}}if(Ne()){const d=i.filter(w=>w.key.startsWith("cachly:lesson:best:")).slice(0,3).map(w=>w.key.slice(19));_s(a,s,d,$e).catch(()=>{})}const x=[],b=i.filter(d=>d.key.startsWith("cachly:lesson:best:"));let y=0,A=0;for(const d of b.slice(0,5)){const w=await a.get(d.key).catch(()=>null);if(w){const v=O(w,null);if(v?.state==="archived")continue;if(v){const U=new Date().toISOString(),K={...v,recall_count:(v.recall_count??0)+1,last_recalled_at:U};a.set(d.key,JSON.stringify(K)).catch(()=>{});const j=at(v)?ot(v):0;if(j>0&&a.incrbyfloat(`cachly:stats:time_saved_mins:${t}`,j).catch(()=>{}),v.author&&m&&v.author!==m){y++,a.sadd(`cachly:stats:reuse_pairs:${t}`,`${m}<-${v.author}`).catch(()=>{});const J=`person:${me(m)}`,Q=`person:${me(v.author)}`;ye(a,J,"collaborates",Q,!0).catch(()=>{}),ye(a,Q,"collaborates",J,!0).catch(()=>{})}v.outcome==="success"&&A++,v.outcome==="success"&&(v.recall_count??0)>=1&&!ze(v)&&x.push({topic:d.key.replace("cachly:lesson:best:",""),severity:v.severity??"major",recall_count:(v.recall_count??0)+1,savedMins:j,ts:v.ts,author:v.author})}}}A>0&&a.set(`cachly:stats:first_recall_at:${t}`,new Date().toISOString(),"EX",365*86400,"NX").catch(()=>{}),b.length>0&&(a.incr(`cachly:stats:recalls_total:${t}`).catch(()=>{}),y>0&&a.incr(`cachly:stats:cross_author_recalls:${t}`).catch(()=>{}));const I=await ue(`/api/v1/instances/${t}`).catch(()=>null),e=!I?.tier||I.tier.toLowerCase()==="free",F=[];if(!e&&I?.vector_token&&Ne())try{const d=await $e(s),w=`https://api.cachly.dev/v1/sem/${I.vector_token}`,v=await fetch(`${w}/search`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({embedding:d,namespace:"cachly:ctx",threshold:S,top_k:5}),signal:AbortSignal.timeout(8e3)});if(v.ok){const U=await v.json();for(const K of U.filter(j=>j.found&&j.id)){const j=K.id.replace("ctx:","").split(":"),J=j[0],Q=j.slice(1).join(":"),G=`cachly:ctx:${J}:${Q}`,de=await a.get(G).catch(()=>null);F.push({key:G,similarity:K.similarity??0,content:de??"(evicted)"})}}}catch{}const k=i.map(d=>d.score),ae=k.length?Math.min(...k):0,ce=k.length&&Math.max(...k)-ae||1,he=d=>(d-ae)/ce,D=new Map;for(const d of i){const w=he(d.score);D.set(d.key,{key:d.key,content:d.content,bm25Score:w,hybridScore:w*(F.length>0?.7:1),matchedWords:d.matchedWords,matchType:"keyword",subQuery:d.subQuery,wortBelege:d.wortBelege})}for(const d of F){const w=D.get(d.key);w?(w.semScore=d.similarity,w.hybridScore=(w.bm25Score??0)*.6+d.similarity*.4,w.matchType="hybrid"):D.set(d.key,{key:d.key,content:d.content,semScore:d.similarity,hybridScore:d.similarity*.5,matchType:"semantic"})}try{const d=Ye(s).filter(U=>U.length>=4).slice(0,4),w=new Set;for(const U of d){const K=a.scanStream({match:`cachly:ckg:node:*${U}*`,count:20});await new Promise((j,J)=>{K.on("data",Q=>{for(const G of Q.slice(0,5))w.add(G.replace("cachly:ckg:node:",""))}),K.on("end",j),K.on("error",J)})}const v=new Set;for(const U of[...w].slice(0,12)){const K=await a.smembers(`cachly:ckg:idx:to:${U}`);for(const j of K.slice(0,6)){const J=await a.get(j);if(!J)continue;const Q=O(J,null);if(!Q||Q.edgeType!=="fixes"||Q.confidence<.35||v.has(Q.from))continue;v.add(Q.from);const G=`cachly:lesson:best:${Q.from}`,de=D.get(G);if(de)de.ckgScore=Math.max(de.ckgScore??0,Q.confidence),de.hybridScore=de.hybridScore*.88+Q.confidence*.12,de.matchType==="keyword"&&(de.matchType="hybrid");else{const ke=await a.get(G).catch(()=>null);if(!ke)continue;D.set(G,{key:G,content:ke,ckgScore:Q.confidence,hybridScore:Q.confidence*.3,matchType:"ckg"})}}}}catch{}if($.length>0){const d=new Set($.map(w=>w.replace(/\\/g,"/")));for(const w of D.values()){if(!w.key.startsWith("cachly:lesson:best:"))continue;const v=O(w.content,{});(Array.isArray(v.file_paths)?v.file_paths.filter(K=>typeof K=="string").map(K=>K.replace(/\\/g,"/")):[]).some(K=>d.has(K))&&(w.hybridScore*=1.15,w.contextBoost=!0)}}for(const d of D.values()){if(d.matchType!=="semantic"&&d.matchType!=="ckg")continue;const w=us({key:d.key,content:d.content});w&&(d.hybridScore*=ls(w))}const E=[...D.values()].filter(d=>{if(!d.key.startsWith("cachly:lesson:best:"))return!0;const w=Ce(d.content);return!(!Te(w)||!ct(w?.group,u,M))}).sort((d,w)=>w.hybridScore-d.hybridScore),H=[];for(const d of E){if(!d.key.startsWith("cachly:lesson:best:"))continue;const w=O(d.content,{});w.gilt_bis&&Date.parse(String(w.gilt_bis))<Date.now()&&H.push(`\u23F3 \`${d.key.replace("cachly:lesson:best:","")}\` galt bis ${String(w.gilt_bis).slice(0,10)} \u2014 Geschichte, keine aktuelle Tatsache.`);const v=O(d.content,{});if(v.pruefung_gefallen_am){const U=d.key.replace("cachly:lesson:best:",""),K=String(v.pruefung_gefallen_am).slice(0,10),j=String(v.pruefung_befund??"").trim().slice(0,200);H.push(`\u274C \`${U}\` \u2014 Pruefung am ${K} FEHLGESCHLAGEN`+(j?`: ${j}`:""))}}{const d=new Set(E.map(w=>w.key));for(let w=E.length-1;w>=0;w--){const v=E[w];if(!v.key.startsWith("cachly:lesson:best:"))continue;const U=O(v.content,{});if(!U.ersetzt_durch)continue;const K=String(U.ersetzt_grund??"").trim(),j=v.key.replace("cachly:lesson:best:","");if(d.has(`cachly:lesson:best:${U.ersetzt_durch}`))E.splice(w,1),H.push(`\u21B7 \`${j}\` verdraengt \u2014 ersetzt durch \`${U.ersetzt_durch}\`${U.ersetzt_am?` am ${U.ersetzt_am.slice(0,10)}`:""}${K?` \u2014 Grund: ${K}`:" \u2014 Grund nicht erfasst"}`);else if(await a.get(`cachly:lesson:best:${U.ersetzt_durch}`)){const J=Ce(await a.get(`cachly:lesson:best:${U.ersetzt_durch}`));Te(J)?H.push(`\u26A0\uFE0F \`${j}\` ist ersetzt durch \`${U.ersetzt_durch}\` (nicht in dieser Liste) \u2014 die gueltige Antwort: \`recall_best_solution(topic="${U.ersetzt_durch}")\``):H.push(`\u21B7 \`${j}\`: von einer nicht team-sichtbaren Fassung ersetzt \u2014 diese Fassung bleibt die beste sichtbare Antwort.`)}else H.push(`\u21A9\uFE0F \`${j}\` war ersetzt durch \`${U.ersetzt_durch}\` \u2014 der Nachfolger wurde geloescht, diese Fassung gilt wieder (bei Gelegenheit neu bestaetigen).`)}}const se=[];{await _e.aktualisiere(a).catch(()=>{});const d=E.slice(0,8).filter(w=>w.key.startsWith("cachly:lesson:best:"));for(let w=0;w<d.length;w++)for(let v=w+1;v<d.length;v++){const U=d[w].key.replace("cachly:lesson:best:",""),K=d[v].key.replace("cachly:lesson:best:",""),j=_e.rohvektor(U),J=_e.rohvektor(K);if(!j||!J)continue;let Q=0,G=0,de=0;for(let B=0;B<j.length;B++)Q+=j[B]*J[B],G+=j[B]*j[B],de+=J[B]*J[B];if((G>0&&de>0?Q/(Math.sqrt(G)*Math.sqrt(de)):0)<Ws)continue;const R=B=>{const oe=O(B??"",{});return String(oe.verified_at??oe.ts??"").slice(0,10)||"ohne Datum"};se.push(`\u26A0\uFE0F \`${U}\` (${R(d[w].content)}) und \`${K}\` (${R(d[v].content)}) sagen fast dasselbe \u2014 prueft, welche Fassung fuer euren Fall gilt; die neuere ist nicht automatisch die richtige.`)}se.length>3&&(se.length=3)}if(H.push(...se),E.length>=2){const d=E[0].hybridScore,w=E[Math.min(4,E.length-1)].hybridScore,v=Math.max(0,d-w);(async()=>{try{await a.lpush("cachly:recall:spread",JSON.stringify({ts:new Date().toISOString(),spread:Number(v.toFixed(4)),n:E.length})),await a.ltrim("cachly:recall:spread",0,199)}catch{}})()}const Y=Xt();let o="",r=null;if(Y!==null){o=Qt();const d=E[0]?.hybridScore??0,w=es(E.map(v=>({key:v.key,content:v.content,punktzahl:v.hybridScore})),o,Y,d);w.weggelassenesThema!==void 0?E.splice(0,1):w.markierungGesetzt&&E[0]&&w.treffer[0]&&(E[0]={...E[0],content:w.treffer[0].content}),r={frage:s,hoechstePunktzahl:d,themen:[],zulassungsfaehig:w.zulassungsfaehig,zulassungsGrund:w.zulassungsGrund,zuteilung:w.zuteilung,weggelassenesThema:w.weggelassenesThema,markierungGesetzt:w.markierungGesetzt}}const p=await Mt(t,ue),c=p.reached,l=St({ueberLimit:c,gesamt:E.length,volleTiefe:Zs}),T=process.env.CACHLY_RECALL_COMPACT==="1",z=(()=>{const d=Number.parseInt(process.env.CACHLY_RECALL_COMPACT_HITS??"",10);return Number.isFinite(d)?Math.min(20,Math.max(1,d)):5})(),P=T?Math.min(z,l.sichtbar):l.sichtbar,N=[`\u{1F9E0} **Smart Recall** for: _"${s}"_
|
|
18
18
|
`];p.goodwillMessage&&N.push(`${p.goodwillMessage}
|
|
19
19
|
`);const W={critical:0,major:1,minor:2},te=x.sort((d,w)=>(W[d.severity]??1)-(W[w.severity]??1))[0];if(te){const d=j=>j>=60?`${j/60}h`:`${j}min`,w=(()=>{if(!te.ts)return null;const j=new Date(te.ts);if(isNaN(j.getTime()))return null;const J=new Date,Q=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];return j.getFullYear()===J.getFullYear()?`${j.getDate()} ${Q[j.getMonth()]}`:`${j.getDate()} ${Q[j.getMonth()]} ${j.getFullYear()}`})(),v=te.severity==="critical"?120:te.severity==="major"?60:20,U=te.author&&te.author!==m?` \xB7 by @${te.author}`:"",K=w?` \xB7 learned ${w} (was a ${d(v)} debug)`:"";N.push(`> \u{1F4A1} **Brain saved you ~${d(te.savedMins)} here** \u2014 \`${te.topic}\`${K} \xB7 recalled ${te.recall_count}\xD7${U}
|
|
20
20
|
`)}y>0&&N.push(`> \u{1F465} **Team knowledge reuse** \u2014 ${y} of these lesson${y!==1?"s were":" was"} written by a teammate. This is the value only a shared brain delivers.
|
package/dist/src/leser.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{embedConfig as
|
|
1
|
+
import{embedConfig as c}from"./embeddings.js";import{LESER_GEWICHT as f,LESER_ZEITLIMIT_MS as m}from"./rangfolge-stellschrauben.js";const h=10*6e4;let u=0;function y(){return f<=0||(process.env.CACHLY_LESER??"").toLowerCase()in{0:1,off:1,aus:1,false:1}||!c.jwt?!1:Date.now()>=u}function g(){u=0}async function L(o,n,r){if(!y()||n.length===0)return null;const a=r?.zeitlimitMs??m,l=r?.fetchFn??fetch;try{const i=await l(`${c.apiUrl}/api/v1/rerank`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${c.jwt}`},body:JSON.stringify({query:o,texts:n,instance_id:r?.instanceId}),signal:AbortSignal.timeout(a)});if(i.status===503)return u=Date.now()+h,null;if(!i.ok)return null;const t=await i.json(),e=Array.isArray(t.scores)?t.scores:null;return!e||e.length!==n.length||!e.every(s=>typeof s=="number"&&Number.isFinite(s))?null:e}catch{return null}}function _(o,n,r=f){const a=t=>{const e=Math.min(...t),s=Math.max(...t);return s>e?t.map(p=>(p-e)/(s-e)):t.map(()=>0)},l=a(o),i=a(n);return o.map((t,e)=>({i:e,p:l[e]+r*i[e]})).sort((t,e)=>e.p-t.p||t.i-e.i).map(t=>t.i)}export{y as leserAktiv,L as leserPunkte,g as leserSperreLoeschen,_ as mischeMitLeser};
|
package/glama.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cachly-dev/mcp-server",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.171",
|
|
4
4
|
"mcpName": "io.github.cachly-dev/mcp-server",
|
|
5
5
|
"description": "Your AI forgets everything between sessions. cachly fixes that permanently: 124 MCP tools for Claude Code, Cursor, Copilot & Windsurf. Learns from every bug fix, commit and CI run. Arrives pre-briefed. Free tier forever, EU servers, GDPR.",
|
|
6
6
|
"type": "module",
|
package/server.json
CHANGED
|
@@ -6,13 +6,13 @@
|
|
|
6
6
|
"url": "https://github.com/cachly-dev/cachly-mcp",
|
|
7
7
|
"source": "github"
|
|
8
8
|
},
|
|
9
|
-
"version": "0.10.
|
|
9
|
+
"version": "0.10.171",
|
|
10
10
|
"packages": [
|
|
11
11
|
{
|
|
12
12
|
"registryType": "npm",
|
|
13
13
|
"name": "@cachly-dev/mcp-server",
|
|
14
14
|
"identifier": "@cachly-dev/mcp-server",
|
|
15
|
-
"version": "0.10.
|
|
15
|
+
"version": "0.10.171",
|
|
16
16
|
"transport": {
|
|
17
17
|
"type": "stdio"
|
|
18
18
|
}
|