@cachly-dev/mcp-server 0.10.155 → 0.10.157
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 +4 -4
- package/glama.json +1 -1
- package/package.json +1 -1
- package/server.json +2 -2
|
@@ -11,7 +11,7 @@ ${T.join(`
|
|
|
11
11
|
|
|
12
12
|
${h.join(`
|
|
13
13
|
|
|
14
|
-
`)}`}case"smart_recall":{const{instance_id:p,query:t,threshold:n=.78,author:w="",context_files:A=[]}=Z,f=Array.isArray(A)?A.filter(c=>typeof c=="string"):[],o=await X(p);St(o,void 0,2);let b=new Set,h=!1;w&&(b=await at(o,p,w).catch(()=>new Set),h=await Ke(o,p,w).catch(()=>null)==="admin");const u=await Re(o,["cachly:ctx:*","cachly:lesson:best:*","cachly:idx:*"],t,25),d=Zt(u);let S=!1,T=null;const a=await(async()=>{if(!Ne())return ze(Pt,"kein Einbettungsdienst eingerichtet \u2014 die Suche laeuft nur ueber Woerter"),d;try{if(await me.aktualisiere(o),me.groesse===0)return ze(Ot,"keine einzige Lektion hat eine Einbettung \u2014 die Suche laeuft nur ueber Woerter. Nachruesten mit eingaenge-nachruesten, pruefen mit brain_doctor"),d;const c=await be(t);if(!c?.length)return ze(jt,"die Frage liess sich nicht einbetten \u2014 diese Suche laeuft nur ueber Woerter"),d;await Ue.aktualisiere(o),await Ye.aktualisiere(o),await Le.aktualisiere(o),await Ve.aktualisiere(o);const k=(me.groesse>0?Ve.groesse/me.groesse:0)>=xs?be(t,{modell:qe}).catch(()=>null):null,G=d.filter(L=>L.key.startsWith("cachly:lesson:best:")).map(L=>L.key.slice(19)),K=L=>Math.max(me.naehe(c,L),Ue.naehe(c,L)),F=[...Le.themen()],U=(F.length>0?F:me.aehnlichste(c,500).map(L=>L.topic)).map(L=>({t:L,n:K(L)})).sort((L,te)=>te.n-L.n).slice(0,Ns).map(L=>L.t),J=[...new Set([...G,...U])],W=U.slice(0,3).map(L=>me.rohvektor(L)).filter(Boolean),le=W.length?ps(c,W):c,$e=mt(t),we=Le.statistik,I=J.map(L=>{const te=new Set([...mt(Le.textVon(L))].map(ms));return{naeheText:me.naehe(c,L),naeheThema:Ue.naehe(c,L),naeheRueckkopplung:me.naehe(le,L),seltenheitsDeckung:we?we.deckung($e,te):0,besterZeuge:we?we.besterZeuge($e,te):0}});let P=fs(I,gs);{const L=[...P].sort((te,re)=>re-te);T=L.length>1?L[0]-L[1]:null}if(Ye.groesse>0){const L=J.map(re=>{const de=Ye.besteNaehe(c,re);return de>=Es?de:-2}),te=pt(L);P=P.map((re,de)=>re+As*te[de])}if(k){const L=await k;if(L?.length){const te=pt(J.map(re=>Ve.naehe(L,re)));P=P.map((re,de)=>re+Cs*te[de])}}const ie=J.map((L,te)=>({t:L,p:P[te]})).sort((L,te)=>te.p-L.p).map(L=>L.t),ee=new Map(d.map(L=>[L.key,L])),q=[];for(const L of ie){const te=`cachly:lesson:best:${L}`,re=ee.get(te);if(re){q.push(re),ee.delete(te);continue}const de=await o.get(te).catch(()=>null);de&&q.push({key:te,content:de,score:0,matchedWords:[],wortBelege:0,finalScore:0,qualityBoost:1})}return q.push(...ee.values()),S=!0,q}catch(c){return await Ie(o,zt,`der Bedeutungspfad brach ab (${c instanceof Error?c.message:String(c)}) \u2014 diese Suche lief nur ueber Woerter`),d}})();bt(o);{const c=a.filter(v=>v.key.startsWith("cachly:lesson:best:")).slice(0,3).map(v=>v.key.slice(19));if(c.length>0){const v={ts:new Date().toISOString(),themen:c};(async()=>{const k=ht(p);await o.lpush(k,JSON.stringify(v)),await o.ltrim(k,0,ls-1)})().catch(()=>{})}{const v={ts:new Date().toISOString(),frage:t.slice(0,200),geliefert:c};(async()=>{const k=ut(p);await o.lpush(k,JSON.stringify(v)),await o.ltrim(k,0,us-1)})().catch(()=>{})}}if(Ne()){const c=a.filter(v=>v.key.startsWith("cachly:lesson:best:")).slice(0,3).map(v=>v.key.slice(19));as(o,t,c,be).catch(()=>{})}const l=[],x=a.filter(c=>c.key.startsWith("cachly:lesson:best:"));let y=0,$=0;for(const c of x.slice(0,5)){const v=await o.get(c.key).catch(()=>null);if(v){const k=z(v,null);if(k?.state==="archived")continue;if(k){const G=new Date().toISOString(),K={...k,recall_count:(k.recall_count??0)+1,verified_at:G,last_recalled_at:G};o.set(c.key,JSON.stringify(K)).catch(()=>{});const F=nt(k)?st(k):0;if(F>0&&o.incrbyfloat(`cachly:stats:time_saved_mins:${p}`,F).catch(()=>{}),o.incr(`cachly:stats:recalls_total:${p}`).catch(()=>{}),k.author&&w&&k.author!==w){y++,o.incr(`cachly:stats:cross_author_recalls:${p}`).catch(()=>{}),o.sadd(`cachly:stats:reuse_pairs:${p}`,`${w}<-${k.author}`).catch(()=>{});const U=`person:${he(w)}`,J=`person:${he(k.author)}`;pe(o,U,"collaborates",J,!0).catch(()=>{}),pe(o,J,"collaborates",U,!0).catch(()=>{})}k.outcome==="success"&&$++,k.outcome==="success"&&(k.recall_count??0)>=1&&!Me(k)&&l.push({topic:c.key.replace("cachly:lesson:best:",""),severity:k.severity??"major",recall_count:(k.recall_count??0)+1,savedMins:F,ts:k.ts,author:k.author})}}}$>0&&o.set(`cachly:stats:first_recall_at:${p}`,new Date().toISOString(),"EX",365*86400,"NX").catch(()=>{});const R=await ue(`/api/v1/instances/${p}`).catch(()=>null),D=!R?.tier||R.tier.toLowerCase()==="free",e=[];if(!D&&R?.vector_token&&Ne())try{const c=await be(t),v=`https://api.cachly.dev/v1/sem/${R.vector_token}`,k=await fetch(`${v}/search`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({embedding:c,namespace:"cachly:ctx",threshold:n,top_k:5}),signal:AbortSignal.timeout(8e3)});if(k.ok){const G=await k.json();for(const K of G.filter(F=>F.found&&F.id)){const F=K.id.replace("ctx:","").split(":"),U=F[0],J=F.slice(1).join(":"),W=`cachly:ctx:${U}:${J}`,le=await o.get(W).catch(()=>null);e.push({key:W,similarity:K.similarity??0,content:le??"(evicted)"})}}}catch{}const H=a.map(c=>c.score),_=H.length?Math.min(...H):0,ne=H.length&&Math.max(...H)-_||1,oe=c=>(c-_)/ne,V=new Map;for(const c of a){const v=oe(c.score);V.set(c.key,{key:c.key,content:c.content,bm25Score:v,hybridScore:v*(e.length>0?.7:1),matchedWords:c.matchedWords,matchType:"keyword",subQuery:c.subQuery,wortBelege:c.wortBelege})}for(const c of e){const v=V.get(c.key);v?(v.semScore=c.similarity,v.hybridScore=(v.bm25Score??0)*.6+c.similarity*.4,v.matchType="hybrid"):V.set(c.key,{key:c.key,content:c.content,semScore:c.similarity,hybridScore:c.similarity*.5,matchType:"semantic"})}try{const c=Ge(t).filter(G=>G.length>=4).slice(0,4),v=new Set;for(const G of c){const K=o.scanStream({match:`cachly:ckg:node:*${G}*`,count:20});await new Promise((F,U)=>{K.on("data",J=>{for(const W of J.slice(0,5))v.add(W.replace("cachly:ckg:node:",""))}),K.on("end",F),K.on("error",U)})}const k=new Set;for(const G of[...v].slice(0,12)){const K=await o.smembers(`cachly:ckg:idx:to:${G}`);for(const F of K.slice(0,6)){const U=await o.get(F);if(!U)continue;const J=z(U,null);if(!J||J.edgeType!=="fixes"||J.confidence<.35||k.has(J.from))continue;k.add(J.from);const W=`cachly:lesson:best:${J.from}`,le=V.get(W);if(le)le.ckgScore=Math.max(le.ckgScore??0,J.confidence),le.hybridScore=le.hybridScore*.88+J.confidence*.12,le.matchType==="keyword"&&(le.matchType="hybrid");else{const $e=await o.get(W).catch(()=>null);if(!$e)continue;V.set(W,{key:W,content:$e,ckgScore:J.confidence,hybridScore:J.confidence*.3,matchType:"ckg"})}}}}catch{}if(f.length>0){const c=new Set(f.map(v=>v.replace(/\\/g,"/")));for(const v of V.values()){if(!v.key.startsWith("cachly:lesson:best:"))continue;const k=z(v.content,{});(Array.isArray(k.file_paths)?k.file_paths.filter(K=>typeof K=="string").map(K=>K.replace(/\\/g,"/")):[]).some(K=>c.has(K))&&(v.hybridScore*=1.15,v.contextBoost=!0)}}for(const c of V.values()){if(c.matchType!=="semantic"&&c.matchType!=="ckg")continue;const v=Qt({key:c.key,content:c.content});v&&(c.hybridScore*=Xt(v))}const E=[...V.values()].filter(c=>{if(!c.key.startsWith("cachly:lesson:best:"))return!0;const v=ve(c.content);return!(!Ce(v)||!it(v?.group,b,h))}).sort((c,v)=>v.hybridScore-c.hybridScore),O=[];for(const c of E){if(!c.key.startsWith("cachly:lesson:best:"))continue;const v=z(c.content,{});v.gilt_bis&&Date.parse(String(v.gilt_bis))<Date.now()&&O.push(`\u23F3 \`${c.key.replace("cachly:lesson:best:","")}\` galt bis ${String(v.gilt_bis).slice(0,10)} \u2014 Geschichte, keine aktuelle Tatsache.`)}{const c=new Set(E.map(v=>v.key));for(let v=E.length-1;v>=0;v--){const k=E[v];if(!k.key.startsWith("cachly:lesson:best:"))continue;const G=z(k.content,{});if(!G.ersetzt_durch)continue;const K=k.key.replace("cachly:lesson:best:","");if(c.has(`cachly:lesson:best:${G.ersetzt_durch}`))E.splice(v,1),O.push(`\u21B7 \`${K}\` verdraengt \u2014 ersetzt durch \`${G.ersetzt_durch}\`${G.ersetzt_am?` am ${G.ersetzt_am.slice(0,10)}`:""}`);else if(await o.get(`cachly:lesson:best:${G.ersetzt_durch}`)){const F=ve(await o.get(`cachly:lesson:best:${G.ersetzt_durch}`));Ce(F)?O.push(`\u26A0\uFE0F \`${K}\` ist ersetzt durch \`${G.ersetzt_durch}\` (nicht in dieser Liste) \u2014 die gueltige Antwort: \`recall_best_solution(topic="${G.ersetzt_durch}")\``):O.push(`\u21B7 \`${K}\`: von einer nicht team-sichtbaren Fassung ersetzt \u2014 diese Fassung bleibt die beste sichtbare Antwort.`)}else O.push(`\u21A9\uFE0F \`${K}\` war ersetzt durch \`${G.ersetzt_durch}\` \u2014 der Nachfolger wurde geloescht, diese Fassung gilt wieder (bei Gelegenheit neu bestaetigen).`)}}const se=[];{await me.aktualisiere(o).catch(()=>{});const c=E.slice(0,8).filter(v=>v.key.startsWith("cachly:lesson:best:"));for(let v=0;v<c.length;v++)for(let k=v+1;k<c.length;k++){const G=c[v].key.replace("cachly:lesson:best:",""),K=c[k].key.replace("cachly:lesson:best:",""),F=me.rohvektor(G),U=me.rohvektor(K);if(!F||!U)continue;let J=0,W=0,le=0;for(let I=0;I<F.length;I++)J+=F[I]*U[I],W+=F[I]*F[I],le+=U[I]*U[I];if((W>0&&le>0?J/(Math.sqrt(W)*Math.sqrt(le)):0)<Ts)continue;const we=I=>{const P=z(I??"",{});return String(P.verified_at??P.ts??"").slice(0,10)||"ohne Datum"};se.push(`\u26A0\uFE0F \`${G}\` (${we(c[v].content)}) und \`${K}\` (${we(c[k].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)}O.push(...se);const Y=Ft();let Q="",s=null;if(Y!==null){Q=Wt();const c=E[0]?.hybridScore??0,v=Kt(E.map(k=>({key:k.key,content:k.content,punktzahl:k.hybridScore})),Q,Y,c);v.weggelassenesThema!==void 0?E.splice(0,1):v.markierungGesetzt&&E[0]&&v.treffer[0]&&(E[0]={...E[0],content:v.treffer[0].content}),s={frage:t,hoechstePunktzahl:c,themen:[],zulassungsfaehig:v.zulassungsfaehig,zulassungsGrund:v.zulassungsGrund,zuteilung:v.zuteilung,weggelassenesThema:v.weggelassenesThema,markierungGesetzt:v.markierungGesetzt}}const m=await kt(p,ue),g=m.reached,i=gt({ueberLimit:g,gesamt:E.length,volleTiefe:Is}),r=process.env.CACHLY_RECALL_COMPACT==="1",N=r?Math.min(3,i.sichtbar):i.sichtbar,C=[`\u{1F9E0} **Smart Recall** for: _"${t}"_
|
|
14
|
+
`)}`}case"smart_recall":{const{instance_id:p,query:t,threshold:n=.78,author:w="",context_files:A=[]}=Z,f=Array.isArray(A)?A.filter(c=>typeof c=="string"):[],o=await X(p);St(o,void 0,2);let b=new Set,h=!1;w&&(b=await at(o,p,w).catch(()=>new Set),h=await Ke(o,p,w).catch(()=>null)==="admin");const u=await Re(o,["cachly:ctx:*","cachly:lesson:best:*","cachly:idx:*"],t,25),d=Zt(u);let S=!1,T=null;const a=await(async()=>{if(!Ne())return ze(Pt,"kein Einbettungsdienst eingerichtet \u2014 die Suche laeuft nur ueber Woerter"),d;try{if(await me.aktualisiere(o),me.groesse===0)return ze(Ot,"keine einzige Lektion hat eine Einbettung \u2014 die Suche laeuft nur ueber Woerter. Nachruesten mit eingaenge-nachruesten, pruefen mit brain_doctor"),d;const c=await be(t);if(!c?.length)return ze(jt,"die Frage liess sich nicht einbetten \u2014 diese Suche laeuft nur ueber Woerter"),d;await Ue.aktualisiere(o),await Ye.aktualisiere(o),await Le.aktualisiere(o),await Ve.aktualisiere(o);const k=(me.groesse>0?Ve.groesse/me.groesse:0)>=xs?be(t,{modell:qe}).catch(()=>null):null,G=d.filter(L=>L.key.startsWith("cachly:lesson:best:")).map(L=>L.key.slice(19)),K=L=>Math.max(me.naehe(c,L),Ue.naehe(c,L)),F=[...Le.themen()],U=(F.length>0?F:me.aehnlichste(c,500).map(L=>L.topic)).map(L=>({t:L,n:K(L)})).sort((L,te)=>te.n-L.n).slice(0,Ns).map(L=>L.t),J=[...new Set([...G,...U])],W=U.slice(0,3).map(L=>me.rohvektor(L)).filter(Boolean),le=W.length?ps(c,W):c,$e=mt(t),we=Le.statistik,I=J.map(L=>{const te=new Set([...mt(Le.textVon(L))].map(ms));return{naeheText:me.naehe(c,L),naeheThema:Ue.naehe(c,L),naeheRueckkopplung:me.naehe(le,L),seltenheitsDeckung:we?we.deckung($e,te):0,besterZeuge:we?we.besterZeuge($e,te):0}});let P=fs(I,gs);{const L=[...P].sort((te,re)=>re-te);T=L.length>1?L[0]-L[1]:null}if(Ye.groesse>0){const L=J.map(re=>{const de=Ye.besteNaehe(c,re);return de>=Es?de:-2}),te=pt(L);P=P.map((re,de)=>re+As*te[de])}if(k){const L=await k;if(L?.length){const te=pt(J.map(re=>Ve.naehe(L,re)));P=P.map((re,de)=>re+Cs*te[de])}}const ie=J.map((L,te)=>({t:L,p:P[te]})).sort((L,te)=>te.p-L.p).map(L=>L.t),ee=new Map(d.map(L=>[L.key,L])),q=[];for(const L of ie){const te=`cachly:lesson:best:${L}`,re=ee.get(te);if(re){q.push(re),ee.delete(te);continue}const de=await o.get(te).catch(()=>null);de&&q.push({key:te,content:de,score:0,matchedWords:[],wortBelege:0,finalScore:0,qualityBoost:1})}return q.push(...ee.values()),S=!0,q}catch(c){return await Ie(o,zt,`der Bedeutungspfad brach ab (${c instanceof Error?c.message:String(c)}) \u2014 diese Suche lief nur ueber Woerter`),d}})();bt(o);{const c=a.filter(v=>v.key.startsWith("cachly:lesson:best:")).slice(0,3).map(v=>v.key.slice(19));if(c.length>0){const v={ts:new Date().toISOString(),themen:c};(async()=>{const k=ht(p);await o.lpush(k,JSON.stringify(v)),await o.ltrim(k,0,ls-1)})().catch(()=>{})}{const v={ts:new Date().toISOString(),frage:t.slice(0,200),geliefert:c};(async()=>{const k=ut(p);await o.lpush(k,JSON.stringify(v)),await o.ltrim(k,0,us-1)})().catch(()=>{})}}if(Ne()){const c=a.filter(v=>v.key.startsWith("cachly:lesson:best:")).slice(0,3).map(v=>v.key.slice(19));as(o,t,c,be).catch(()=>{})}const l=[],x=a.filter(c=>c.key.startsWith("cachly:lesson:best:"));let y=0,$=0;for(const c of x.slice(0,5)){const v=await o.get(c.key).catch(()=>null);if(v){const k=z(v,null);if(k?.state==="archived")continue;if(k){const G=new Date().toISOString(),K={...k,recall_count:(k.recall_count??0)+1,verified_at:G,last_recalled_at:G};o.set(c.key,JSON.stringify(K)).catch(()=>{});const F=nt(k)?st(k):0;if(F>0&&o.incrbyfloat(`cachly:stats:time_saved_mins:${p}`,F).catch(()=>{}),o.incr(`cachly:stats:recalls_total:${p}`).catch(()=>{}),k.author&&w&&k.author!==w){y++,o.incr(`cachly:stats:cross_author_recalls:${p}`).catch(()=>{}),o.sadd(`cachly:stats:reuse_pairs:${p}`,`${w}<-${k.author}`).catch(()=>{});const U=`person:${he(w)}`,J=`person:${he(k.author)}`;pe(o,U,"collaborates",J,!0).catch(()=>{}),pe(o,J,"collaborates",U,!0).catch(()=>{})}k.outcome==="success"&&$++,k.outcome==="success"&&(k.recall_count??0)>=1&&!Me(k)&&l.push({topic:c.key.replace("cachly:lesson:best:",""),severity:k.severity??"major",recall_count:(k.recall_count??0)+1,savedMins:F,ts:k.ts,author:k.author})}}}$>0&&o.set(`cachly:stats:first_recall_at:${p}`,new Date().toISOString(),"EX",365*86400,"NX").catch(()=>{});const R=await ue(`/api/v1/instances/${p}`).catch(()=>null),D=!R?.tier||R.tier.toLowerCase()==="free",e=[];if(!D&&R?.vector_token&&Ne())try{const c=await be(t),v=`https://api.cachly.dev/v1/sem/${R.vector_token}`,k=await fetch(`${v}/search`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({embedding:c,namespace:"cachly:ctx",threshold:n,top_k:5}),signal:AbortSignal.timeout(8e3)});if(k.ok){const G=await k.json();for(const K of G.filter(F=>F.found&&F.id)){const F=K.id.replace("ctx:","").split(":"),U=F[0],J=F.slice(1).join(":"),W=`cachly:ctx:${U}:${J}`,le=await o.get(W).catch(()=>null);e.push({key:W,similarity:K.similarity??0,content:le??"(evicted)"})}}}catch{}const H=a.map(c=>c.score),_=H.length?Math.min(...H):0,ne=H.length&&Math.max(...H)-_||1,oe=c=>(c-_)/ne,V=new Map;for(const c of a){const v=oe(c.score);V.set(c.key,{key:c.key,content:c.content,bm25Score:v,hybridScore:v*(e.length>0?.7:1),matchedWords:c.matchedWords,matchType:"keyword",subQuery:c.subQuery,wortBelege:c.wortBelege})}for(const c of e){const v=V.get(c.key);v?(v.semScore=c.similarity,v.hybridScore=(v.bm25Score??0)*.6+c.similarity*.4,v.matchType="hybrid"):V.set(c.key,{key:c.key,content:c.content,semScore:c.similarity,hybridScore:c.similarity*.5,matchType:"semantic"})}try{const c=Ge(t).filter(G=>G.length>=4).slice(0,4),v=new Set;for(const G of c){const K=o.scanStream({match:`cachly:ckg:node:*${G}*`,count:20});await new Promise((F,U)=>{K.on("data",J=>{for(const W of J.slice(0,5))v.add(W.replace("cachly:ckg:node:",""))}),K.on("end",F),K.on("error",U)})}const k=new Set;for(const G of[...v].slice(0,12)){const K=await o.smembers(`cachly:ckg:idx:to:${G}`);for(const F of K.slice(0,6)){const U=await o.get(F);if(!U)continue;const J=z(U,null);if(!J||J.edgeType!=="fixes"||J.confidence<.35||k.has(J.from))continue;k.add(J.from);const W=`cachly:lesson:best:${J.from}`,le=V.get(W);if(le)le.ckgScore=Math.max(le.ckgScore??0,J.confidence),le.hybridScore=le.hybridScore*.88+J.confidence*.12,le.matchType==="keyword"&&(le.matchType="hybrid");else{const $e=await o.get(W).catch(()=>null);if(!$e)continue;V.set(W,{key:W,content:$e,ckgScore:J.confidence,hybridScore:J.confidence*.3,matchType:"ckg"})}}}}catch{}if(f.length>0){const c=new Set(f.map(v=>v.replace(/\\/g,"/")));for(const v of V.values()){if(!v.key.startsWith("cachly:lesson:best:"))continue;const k=z(v.content,{});(Array.isArray(k.file_paths)?k.file_paths.filter(K=>typeof K=="string").map(K=>K.replace(/\\/g,"/")):[]).some(K=>c.has(K))&&(v.hybridScore*=1.15,v.contextBoost=!0)}}for(const c of V.values()){if(c.matchType!=="semantic"&&c.matchType!=="ckg")continue;const v=Qt({key:c.key,content:c.content});v&&(c.hybridScore*=Xt(v))}const E=[...V.values()].filter(c=>{if(!c.key.startsWith("cachly:lesson:best:"))return!0;const v=ve(c.content);return!(!Ce(v)||!it(v?.group,b,h))}).sort((c,v)=>v.hybridScore-c.hybridScore),O=[];for(const c of E){if(!c.key.startsWith("cachly:lesson:best:"))continue;const v=z(c.content,{});v.gilt_bis&&Date.parse(String(v.gilt_bis))<Date.now()&&O.push(`\u23F3 \`${c.key.replace("cachly:lesson:best:","")}\` galt bis ${String(v.gilt_bis).slice(0,10)} \u2014 Geschichte, keine aktuelle Tatsache.`)}{const c=new Set(E.map(v=>v.key));for(let v=E.length-1;v>=0;v--){const k=E[v];if(!k.key.startsWith("cachly:lesson:best:"))continue;const G=z(k.content,{});if(!G.ersetzt_durch)continue;const K=k.key.replace("cachly:lesson:best:","");if(c.has(`cachly:lesson:best:${G.ersetzt_durch}`))E.splice(v,1),O.push(`\u21B7 \`${K}\` verdraengt \u2014 ersetzt durch \`${G.ersetzt_durch}\`${G.ersetzt_am?` am ${G.ersetzt_am.slice(0,10)}`:""}`);else if(await o.get(`cachly:lesson:best:${G.ersetzt_durch}`)){const F=ve(await o.get(`cachly:lesson:best:${G.ersetzt_durch}`));Ce(F)?O.push(`\u26A0\uFE0F \`${K}\` ist ersetzt durch \`${G.ersetzt_durch}\` (nicht in dieser Liste) \u2014 die gueltige Antwort: \`recall_best_solution(topic="${G.ersetzt_durch}")\``):O.push(`\u21B7 \`${K}\`: von einer nicht team-sichtbaren Fassung ersetzt \u2014 diese Fassung bleibt die beste sichtbare Antwort.`)}else O.push(`\u21A9\uFE0F \`${K}\` war ersetzt durch \`${G.ersetzt_durch}\` \u2014 der Nachfolger wurde geloescht, diese Fassung gilt wieder (bei Gelegenheit neu bestaetigen).`)}}const se=[];{await me.aktualisiere(o).catch(()=>{});const c=E.slice(0,8).filter(v=>v.key.startsWith("cachly:lesson:best:"));for(let v=0;v<c.length;v++)for(let k=v+1;k<c.length;k++){const G=c[v].key.replace("cachly:lesson:best:",""),K=c[k].key.replace("cachly:lesson:best:",""),F=me.rohvektor(G),U=me.rohvektor(K);if(!F||!U)continue;let J=0,W=0,le=0;for(let I=0;I<F.length;I++)J+=F[I]*U[I],W+=F[I]*F[I],le+=U[I]*U[I];if((W>0&&le>0?J/(Math.sqrt(W)*Math.sqrt(le)):0)<Ts)continue;const we=I=>{const P=z(I??"",{});return String(P.verified_at??P.ts??"").slice(0,10)||"ohne Datum"};se.push(`\u26A0\uFE0F \`${G}\` (${we(c[v].content)}) und \`${K}\` (${we(c[k].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)}O.push(...se);const Y=Ft();let Q="",s=null;if(Y!==null){Q=Wt();const c=E[0]?.hybridScore??0,v=Kt(E.map(k=>({key:k.key,content:k.content,punktzahl:k.hybridScore})),Q,Y,c);v.weggelassenesThema!==void 0?E.splice(0,1):v.markierungGesetzt&&E[0]&&v.treffer[0]&&(E[0]={...E[0],content:v.treffer[0].content}),s={frage:t,hoechstePunktzahl:c,themen:[],zulassungsfaehig:v.zulassungsfaehig,zulassungsGrund:v.zulassungsGrund,zuteilung:v.zuteilung,weggelassenesThema:v.weggelassenesThema,markierungGesetzt:v.markierungGesetzt}}const m=await kt(p,ue),g=m.reached,i=gt({ueberLimit:g,gesamt:E.length,volleTiefe:Is}),r=process.env.CACHLY_RECALL_COMPACT==="1",N=r?Math.min(5,i.sichtbar):i.sichtbar,C=[`\u{1F9E0} **Smart Recall** for: _"${t}"_
|
|
15
15
|
`];m.goodwillMessage&&C.push(`${m.goodwillMessage}
|
|
16
16
|
`);const j={critical:0,major:1,minor:2},M=l.sort((c,v)=>(j[c.severity]??1)-(j[v.severity]??1))[0];if(M){const c=F=>F>=60?`${F/60}h`:`${F}min`,v=(()=>{if(!M.ts)return null;const F=new Date(M.ts);if(isNaN(F.getTime()))return null;const U=new Date,J=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];return F.getFullYear()===U.getFullYear()?`${F.getDate()} ${J[F.getMonth()]}`:`${F.getDate()} ${J[F.getMonth()]} ${F.getFullYear()}`})(),k=M.severity==="critical"?120:M.severity==="major"?60:20,G=M.author&&M.author!==w?` \xB7 by @${M.author}`:"",K=v?` \xB7 learned ${v} (was a ${c(k)} debug)`:"";C.push(`> \u{1F4A1} **Brain saved you ~${c(M.savedMins)} here** \u2014 \`${M.topic}\`${K} \xB7 recalled ${M.recall_count}\xD7${G}
|
|
17
17
|
`)}y>0&&C.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.
|
|
@@ -19,10 +19,10 @@ ${h.join(`
|
|
|
19
19
|
`);const ce=Vt(t);ce.length>1&&C.push(`_Detected ${ce.length} sub-topics:_ ${ce.map((c,v)=>`${v+1}. "${c}"`).join(", ")}
|
|
20
20
|
`);const fe=Lt(E.map(c=>({wortBelege:c.wortBelege,semScore:c.semScore,ckgScore:c.ckgScore})));if(fe.schweigen)return vt(o,p,"schweigen"),C.push(Rt(fe)),C.join(`
|
|
21
21
|
`);const ye=ys(T);if(ye&&C.push(ye,""),vt(o,p,ye?"knapp":"beantwortet"),E.length>0){const c=E.some(k=>k.ckgScore!==void 0),v=e.length>0&&c?"keyword + semantic + CKG hybrid":e.length>0?"keyword + semantic hybrid":c?"keyword + CKG hybrid":"keyword";if(C.push(`### \u{1F50D} Results (${E.length} \u2014 ${v})
|
|
22
|
-
`)
|
|
22
|
+
`),fe.belegte<E.length){const k=[];for(const K of E){if(!K.key.startsWith("cachly:lesson:best:")||!Bt({wortBelege:K.wortBelege,semScore:K.semScore,ckgScore:K.ckgScore}))continue;const F=z(K.content,{}),U=Date.parse(F.verified_at??F.ts??"");Number.isFinite(U)&&k.push(Math.floor((Date.now()-U)/864e5))}const G=Dt(k);C.push(`> \u{1F4CA} ${fe.belegte} von ${E.length} belegt (mind. ein getroffenes Wort, Bedeutungsn\xE4he oder Kante). Die \xFCbrigen sind das N\xE4chstliegende, kein Beleg.`+(G?` \u23F3 ${G}`:"")+`
|
|
23
23
|
`)}if(O.length>0&&C.push(...O.map(k=>`> ${k}`),""),ce.length>1){const k=new Map,G=gt({ueberLimit:g,gesamt:E.length,volleTiefe:Rs});for(const U of E.slice(0,G.sichtbar)){const J=U.subQuery??t;k.has(J)||k.set(J,[]),k.get(J).push(U)}for(const[U,J]of k){C.push(`**Topic: "${U}"** (${J.length} results)
|
|
24
|
-
`);for(const W of J.slice(0,4)){const le=W.key.replace("cachly:ctx:","\u{1F4DD} ").replace("cachly:lesson:best:","\u{1F4A1} ").replace("cachly:idx:","\u{1F4C2} "),$e=W.matchType==="ckg"?`CKG: ${(W.ckgScore??0).toFixed(2)}, \u{1F578}\uFE0F causal graph`:W.ckgScore!==void 0&&W.bm25Score!==void 0?`BM25: ${W.bm25Score.toFixed(2)}, CKG: ${W.ckgScore.toFixed(2)}, \u{1F500} hybrid`:W.matchType==="hybrid"?`BM25: ${(W.bm25Score??0).toFixed(2)}, sem: ${((W.semScore??0)*100).toFixed(0)}%, \u{1F500} hybrid`:W.matchType==="semantic"?`sem: ${((W.semScore??0)*100).toFixed(0)}%, \u{1F3AF} semantic`:`BM25: ${(W.bm25Score??0).toFixed(2)}, matched: ${W.matchedWords?.join(", ")}`,we=ot(W.key,W.content,300);if(C.push(
|
|
25
|
-
\u26A0\uFE0F **No results for:** ${F.map(U=>`"${U}"`).join(", ")}`)}else for(const k of E.slice(0,N)){const G=k.key.replace("cachly:ctx:","\u{1F4DD} ").replace("cachly:lesson:best:","\u{1F4A1} ").replace("cachly:idx:","\u{1F4C2} ");let K="";if(k.key.startsWith("cachly:lesson:best:")){const W=z(k.content,{});W.author&&(K=` \xB7 \u{1F464} ${W.author}`)}const F=k.contextBoost?" \xB7 \u{1F4C1} context match":"",U=k.matchType==="hybrid"?`BM25: ${(k.bm25Score??0).toFixed(2)}, sem: ${((k.semScore??0)*100).toFixed(0)}%, \u{1F500} hybrid`:k.matchType==="semantic"?`sem: ${((k.semScore??0)*100).toFixed(0)}%, \u{1F3AF} semantic`:`BM25: ${(k.bm25Score??0).toFixed(2)}, matched: ${k.matchedWords?.join(", ")}`,J=ot(k.key,k.content,
|
|
24
|
+
`);for(const W of J.slice(0,r?2:4)){const le=W.key.replace("cachly:ctx:","\u{1F4DD} ").replace("cachly:lesson:best:","\u{1F4A1} ").replace("cachly:idx:","\u{1F4C2} "),$e=W.matchType==="ckg"?`CKG: ${(W.ckgScore??0).toFixed(2)}, \u{1F578}\uFE0F causal graph`:W.ckgScore!==void 0&&W.bm25Score!==void 0?`BM25: ${W.bm25Score.toFixed(2)}, CKG: ${W.ckgScore.toFixed(2)}, \u{1F500} hybrid`:W.matchType==="hybrid"?`BM25: ${(W.bm25Score??0).toFixed(2)}, sem: ${((W.semScore??0)*100).toFixed(0)}%, \u{1F500} hybrid`:W.matchType==="semantic"?`sem: ${((W.semScore??0)*100).toFixed(0)}%, \u{1F3AF} semantic`:`BM25: ${(W.bm25Score??0).toFixed(2)}, matched: ${W.matchedWords?.join(", ")}`,we=ot(W.key,W.content,300);if(C.push(r?` **${le}**`:` **${le}** _(${$e})_`),W.key.startsWith("cachly:lesson:best:")){const I=await o.get(W.key.replace("lesson:best:","lesson:human:")).catch(()=>null)??await o.get(W.key.replace("cachly:lesson:best:","lesson:human:")).catch(()=>null),P=I?z(I,{}):{};P.title&&C.push(` \u{1F4D6} **${P.title}** \u2014 ${P.summary??""}`)}for(const I of we)C.push(` > ${I}`);C.push("")}}const K=[...k.keys()],F=ce.filter(U=>!K.includes(U));F.length>0&&C.push(`
|
|
25
|
+
\u26A0\uFE0F **No results for:** ${F.map(U=>`"${U}"`).join(", ")}`)}else for(const k of E.slice(0,N)){const G=k.key.replace("cachly:ctx:","\u{1F4DD} ").replace("cachly:lesson:best:","\u{1F4A1} ").replace("cachly:idx:","\u{1F4C2} ");let K="";if(k.key.startsWith("cachly:lesson:best:")){const W=z(k.content,{});W.author&&(K=` \xB7 \u{1F464} ${W.author}`)}const F=k.contextBoost?" \xB7 \u{1F4C1} context match":"",U=k.matchType==="hybrid"?`BM25: ${(k.bm25Score??0).toFixed(2)}, sem: ${((k.semScore??0)*100).toFixed(0)}%, \u{1F500} hybrid`:k.matchType==="semantic"?`sem: ${((k.semScore??0)*100).toFixed(0)}%, \u{1F3AF} semantic`:`BM25: ${(k.bm25Score??0).toFixed(2)}, matched: ${k.matchedWords?.join(", ")}`,J=ot(k.key,k.content,400);C.push(r?`**${G}**`:`**${G}**${K}${F} _(${U})_`);{const W=z(k.content??"",{});W.entscheidung?.gegen&&C.push(ft({gegen:W.entscheidung.gegen,grund:W.entscheidung.grund??null}))}if(k.key.startsWith("cachly:lesson:best:")){const W=await o.get(k.key.replace("lesson:best:","lesson:human:")).catch(()=>null)??await o.get(k.key.replace("cachly:lesson:best:","lesson:human:")).catch(()=>null),le=W?z(W,{}):{};le.title&&C.push(`\u{1F4D6} **${le.title}** \u2014 ${le.summary??""}`)}for(const W of J)C.push(`> ${W}`);C.push("")}if(g&&i.zurueckgehalten>0){const k=i.zurueckgehalten;C.push(`\u{1F512} **Free recall limit reached** (${m.used}/${m.limit} this month). Showing your top match; ${k} further relevant lesson${k!==1?"s are":" is"} withheld. Your limit resets next month, or unlock your full Brain now \u2192 ${en}
|
|
26
26
|
`)}}else{C.push(`\u26A0\uFE0F No matches found for: "${t}"`),O.length>0&&C.push("",...O.map(k=>`> ${k}`));const c=Ge(t),v=[];if(rt.size>0&&c.length>0)for(const k of c.slice(0,3)){if(k.length<4)continue;let G=3,K="";for(const F of rt){if(F.length<3||Math.abs(F.length-k.length)>4)continue;const U=Yt(k,F);U>0&&U<G&&(G=U,K=F)}K&&v.push(`"${K}" (instead of "${k}")`)}v.length>0?C.push(`\u{1F4A1} **Did you mean:** ${v.join(", ")}?`):(C.push(`
|
|
27
27
|
\u{1F4A1} Tips:`),C.push(" \u2022 Try different keywords"),C.push(" \u2022 Use `list_remembered` to see available context"),C.push(' \u2022 Use `recall_best_solution("topic")` for exact topic lookup'))}if(!r&&E.length>0){C.push(""),C.push("\u21A9\uFE0E Hat eine davon geholfen? `recall_feedback(query=\u2026, topic=\u2026, helped=true, rank=N)` \u2014 und wenn die richtige Lektion **nicht** dabei war: dieselbe Meldung mit `rank=0`. Das ist die einzige Zahl, die die Sortierung besser macht.");const c=E.filter(v=>v.key.startsWith("cachly:lesson:best:")).map(v=>v.key.slice(19));if(c.length>0)try{await o.setex(`cachly:letzter-recall:${p}`,7200,JSON.stringify({frage:t,themen:c}))}catch{}}if(Y!==null&&s){const c=E.slice(0,N).filter(v=>v.key.startsWith("cachly:lesson:best:")).map(v=>v.key.slice(19));return Gt(o,Q,{...s,themen:c},C.join(`
|
|
28
28
|
`))}return C.join(`
|
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.157",
|
|
4
4
|
"mcpName": "io.github.cachly-dev/mcp-server",
|
|
5
5
|
"description": "Your AI forgets everything between sessions. cachly fixes that permanently: 123 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.157",
|
|
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.157",
|
|
16
16
|
"transport": {
|
|
17
17
|
"type": "stdio"
|
|
18
18
|
}
|