@cachly-dev/mcp-server 0.10.140 → 0.10.142

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/README.md CHANGED
@@ -65,8 +65,9 @@ hundreds of teams lose the same knowledge the same way, and we built the fix:
65
65
  - **It learns automatically** — from every commit, every fix, every session. No extra calls.
66
66
  - **It arrives pre-briefed** — your AI opens each session already knowing your stack.
67
67
  - **It's shared** — one engineer's solved bug becomes the whole team's reflex.
68
- - **It's provable** — quality-aware recall beats raw text search by **+33.3 % Precision@1**
69
- ([see the benchmark](./BENCH.md)). A claim without a number is marketing; this is the number.
68
+ - **It's provable** — **78.6 % Precision@1** on an external labelled corpus, with a CI gate
69
+ that fails the build below 71.0 % ([see the benchmark](./BENCH.md)). A claim without a
70
+ number is marketing; a number without a gate is a screenshot.
70
71
  - **It's neutral** — speaks [MCP](https://modelcontextprotocol.io), so it works with
71
72
  Claude, Cursor, Copilot, Windsurf, Cline, Zed. Switch models anytime — **your brain stays.**
72
73
 
@@ -215,7 +216,7 @@ using only Claude, alone.** That's not the game we're playing. Here's the honest
215
216
  | Works across **models & tools** | ✅ MCP — Claude, Cursor, Copilot, Windsurf, Zed… | ❌ Claude + Anthropic API only |
216
217
  | **Structured** knowledge | ✅ topic · outcome · severity · causal graph | ⚠️ flat text files, read linearly |
217
218
  | **Causal root-cause** (`causal_trace`) | ✅ problem → chain → proven fix | ❌ |
218
- | **Provable recall quality** | ✅ +33.3 % Precision@1 vs. BM25 ([benchmark](./BENCH.md)) | ❌ no public metric |
219
+ | **Provable recall quality** | ✅ 78.6 % Precision@1, CI gate at 71.0 % ([benchmark](./BENCH.md)) | ❌ no public metric |
219
220
  | **Governance** (review, attribution, audit) | ✅ `team_confirm`, roles, audit trail | ❌ |
220
221
  | **Self-hosting / BYOK / VPC** | ✅ data stays in your infra | ❌ Anthropic-hosted |
221
222
  | Survives a **model switch** | ✅ your brain is yours | ❌ memory is gone or fragmented |
@@ -225,7 +226,6 @@ using only Claude, alone.** That's not the game we're playing. Here's the honest
225
226
  memory is great — use it. If you work on a **team**, switch tools, care about **proof**,
226
227
  or need **governance and data residency**, that's a gap Anthropic structurally can't
227
228
  close without breaking its own lock-in. **That gap is where cachly wins.**
228
- *(Full strategic analysis: [STRATEGY.md](./STRATEGY.md).)*
229
229
 
230
230
  ---
231
231
 
@@ -334,7 +334,21 @@ MCP tool — bulk-ingests past outcomes the same way `brain_from_git` ingests co
334
334
 
335
335
  ---
336
336
 
337
- ## MCP Tools (123 total)
337
+ ## MCP Tools (123 total, 27 in the default catalogue)
338
+
339
+ **Your editor sees 27 of them, not 123 — on purpose.** The full list cost
340
+ ~27,750 tokens in *every* request, which is 14 % of a 200k window gone before
341
+ you type anything. The tools you use daily are listed individually; the other
342
+ 96 sit behind one dispatcher:
343
+
344
+ ```
345
+ cachly_tool(tool: "team_roster") run any of them by name
346
+ cachly_tool(tool: "team_roster", describe: true) get its schema first
347
+ ```
348
+
349
+ Nothing is unreachable: the server dispatches by name and never consults the
350
+ catalogue, so `team_roster` called directly still works. Set
351
+ `CACHLY_ALLE_WERKZEUGE=1` to get all 123 listed again.
338
352
 
339
353
  The full tool catalog is generated from `sdk/mcp/src/tools.ts`. Cross-surface
340
354
  coverage is tracked in [`../../docs/generated/surface-parity.md`](../../docs/generated/surface-parity.md),
@@ -1,4 +1,4 @@
1
- import{EventEmitter as F}from"node:events";import{keywordSearch as $}from"../search.js";import{rerankByQuality as q}from"../rerank.js";import{BENCH_LESSONS as B,BENCH_QUERIES as k}from"./fixtures.js";const m="cachly:lesson:best:";class v{store=new Map;set(e,n){this.store.set(e,n)}async get(e){return this.store.get(e)??null}scanStream(e){const n=new F,s="^"+e.match.replace(/[.+?^${}()|[\]\\]/g,"\\$&").replace(/\*/g,".*")+"$",i=new RegExp(s),c=[...this.store.keys()].filter(a=>i.test(a));return setImmediate(()=>{n.emit("data",c),n.emit("end")}),n}pipeline(){const e=[],n=this.store;return{get(s){return e.push(s),this},async exec(){return e.map(s=>[null,n.get(s)??null])}}}}function C(t){const e=new v;for(const n of t)e.set(`${m}${n.topic}`,JSON.stringify(n));return e}function w(t,e,n){const s=t.slice(0,n);return s.length===0?0:s.filter(c=>e.has(c)).length/Math.min(n,s.length)}function x(t,e,n){return e.size===0?0:t.slice(0,n).filter(c=>e.has(c)).length/e.size}function V(t,e){for(let n=0;n<t.length;n++)if(e.has(t[n]))return 1/(n+1);return 0}function z(t,e,n){let s=0;for(let c=0;c<Math.min(n,t.length);c++)e.has(t[c])&&(s+=1/Math.log2(c+2));let i=0;for(let c=0;c<Math.min(n,e.size);c++)i+=1/Math.log2(c+2);return i===0?0:s/i}function L(t,e){const n=new Set(e.toLowerCase().replace(/[^a-z0-9\s]/g," ").split(/\s+/).filter(i=>i.length>=3));return t.map(i=>{const c=[i.topic,i.what_worked,i.what_failed??"",i.context??"",(i.tags??[]).join(" ")].join(" ").toLowerCase();let a=0;for(const o of n)c.includes(o)&&a++;const u=i.ts?new Date(i.ts).getTime():0;return{topic:i.topic,overlap:a,recency:u}}).filter(i=>i.overlap>0).sort((i,c)=>c.overlap-i.overlap||c.recency-i.recency).map(i=>i.topic)}const S=t=>t.key.replace(m,"");function f(t){return t.length===0?0:t.reduce((e,n)=>e+n,0)/t.length}function g(t){return{precisionAt1:f(t.map(e=>e.precisionAt1)),precisionAt3:f(t.map(e=>e.precisionAt3)),recallAt3:f(t.map(e=>e.recallAt3)),mrr:f(t.map(e=>e.mrr)),ndcgAt5:f(t.map(e=>e.ndcgAt5))}}function d(t,e){return{precisionAt1:w(t,e,1),precisionAt3:w(t,e,3),recallAt3:x(t,e,3),mrr:V(t,e),ndcgAt5:z(t,e,5)}}async function N(t,e){const n=C(t),s=[],i=[],c=[];for(const r of e){const l=new Set(r.relevant),R=(await $(n,[`${m}*`],r.query,10)).map(S),M=await $(n,[`${m}*`],r.query,25),b=q(M).map(S),E=L(t,r.query);s.push(d(E,l)),i.push(d(R,l)),c.push(d(b,l))}const a=g(s),u=g(i),o=g(c),p=(r,l)=>r===0?l>0?1:0:(l-r)/r,y=r=>({precisionAt1:p(r.precisionAt1,o.precisionAt1),precisionAt3:p(r.precisionAt3,o.precisionAt3),recallAt3:p(r.recallAt3,o.recallAt3),mrr:p(r.mrr,o.mrr),ndcgAt5:p(r.ndcgAt5,o.ndcgAt5)});return{flatfile:a,baseline:u,cachly:o,lift:y(u),liftVsFlatfile:y(a),queryCount:e.length,corpusSize:t.length}}async function O(){return N(B,k)}const A=t=>`${(t*100).toFixed(1)}%`,h=t=>`${t>=0?"+":""}${(t*100).toFixed(1)}%`;function P(t){const e=(n,s,i,c,a)=>` ${n.padEnd(12)} ${A(s).padStart(8)} ${A(i).padStart(8)} ${A(c).padStart(8)} ${h(a).padStart(8)}`;return["","\u{1F9E0} Cachly-Bench \u2014 recall quality head-to-head","\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",` corpus: ${t.corpusSize} lessons \xB7 queries: ${t.queryCount}`," flat-file = naive term-overlap, no quality signal (\u2248 LLM reading memory files)"," baseline = raw BM25+ keyword ranking"," cachly = BM25+ followed by quality-aware reranking","",` ${"metric".padEnd(12)} ${"flatfile".padStart(8)} ${"baseline".padStart(8)} ${"cachly".padStart(8)} ${"vs flat".padStart(8)}`,e("Precision@1",t.flatfile.precisionAt1,t.baseline.precisionAt1,t.cachly.precisionAt1,t.liftVsFlatfile.precisionAt1),e("Precision@3",t.flatfile.precisionAt3,t.baseline.precisionAt3,t.cachly.precisionAt3,t.liftVsFlatfile.precisionAt3),e("Recall@3",t.flatfile.recallAt3,t.baseline.recallAt3,t.cachly.recallAt3,t.liftVsFlatfile.recallAt3),e("MRR",t.flatfile.mrr,t.baseline.mrr,t.cachly.mrr,t.liftVsFlatfile.mrr),e("nDCG@5",t.flatfile.ndcgAt5,t.baseline.ndcgAt5,t.cachly.ndcgAt5,t.liftVsFlatfile.ndcgAt5),"\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",` vs BM25 baseline : MRR ${h(t.lift.mrr)} \xB7 Precision@1 ${h(t.lift.precisionAt1)}`,` vs flat-file mem : MRR ${h(t.liftVsFlatfile.mrr)} \xB7 Precision@1 ${h(t.liftVsFlatfile.precisionAt1)}`,""].join(`
1
+ import{EventEmitter as F}from"node:events";import{keywordSearch as $}from"../search.js";import{rerankByQuality as x}from"../rerank.js";import{BENCH_LESSONS as q,BENCH_QUERIES as B}from"./fixtures.js";const m="cachly:lesson:best:";class k{store=new Map;set(e,n){this.store.set(e,n)}async get(e){return this.store.get(e)??null}scanStream(e){const n=new F,s="^"+e.match.replace(/[.+?^${}()|[\]\\]/g,"\\$&").replace(/\*/g,".*")+"$",i=new RegExp(s),c=[...this.store.keys()].filter(a=>i.test(a));return setImmediate(()=>{n.emit("data",c),n.emit("end")}),n}pipeline(){const e=[],n=this.store;return{get(s){return e.push(s),this},async exec(){return e.map(s=>[null,n.get(s)??null])}}}}function v(t){const e=new k;for(const n of t)e.set(`${m}${n.topic}`,JSON.stringify(n));return e}function w(t,e,n){const s=t.slice(0,n);return s.length===0?0:s.filter(c=>e.has(c)).length/Math.min(n,s.length)}function C(t,e,n){return e.size===0?0:t.slice(0,n).filter(c=>e.has(c)).length/e.size}function V(t,e){for(let n=0;n<t.length;n++)if(e.has(t[n]))return 1/(n+1);return 0}function z(t,e,n){let s=0;for(let c=0;c<Math.min(n,t.length);c++)e.has(t[c])&&(s+=1/Math.log2(c+2));let i=0;for(let c=0;c<Math.min(n,e.size);c++)i+=1/Math.log2(c+2);return i===0?0:s/i}function L(t,e){const n=new Set(e.toLowerCase().replace(/[^a-z0-9\s]/g," ").split(/\s+/).filter(i=>i.length>=3));return t.map(i=>{const c=[i.topic,i.what_worked,i.what_failed??"",i.context??"",(i.tags??[]).join(" ")].join(" ").toLowerCase();let a=0;for(const o of n)c.includes(o)&&a++;const u=i.ts?new Date(i.ts).getTime():0;return{topic:i.topic,overlap:a,recency:u}}).filter(i=>i.overlap>0).sort((i,c)=>c.overlap-i.overlap||c.recency-i.recency).map(i=>i.topic)}const S=t=>t.key.replace(m,"");function f(t){return t.length===0?0:t.reduce((e,n)=>e+n,0)/t.length}function g(t){return{precisionAt1:f(t.map(e=>e.precisionAt1)),precisionAt3:f(t.map(e=>e.precisionAt3)),recallAt3:f(t.map(e=>e.recallAt3)),mrr:f(t.map(e=>e.mrr)),ndcgAt5:f(t.map(e=>e.ndcgAt5))}}function d(t,e){return{precisionAt1:w(t,e,1),precisionAt3:w(t,e,3),recallAt3:C(t,e,3),mrr:V(t,e),ndcgAt5:z(t,e,5)}}async function N(t,e){const n=v(t),s=[],i=[],c=[];for(const r of e){const l=new Set(r.relevant),R=(await $(n,[`${m}*`],r.query,10)).map(S),M=await $(n,[`${m}*`],r.query,25),b=x(M).map(S),E=L(t,r.query);s.push(d(E,l)),i.push(d(R,l)),c.push(d(b,l))}const a=g(s),u=g(i),o=g(c),p=(r,l)=>r===0?l>0?1:0:(l-r)/r,y=r=>({precisionAt1:p(r.precisionAt1,o.precisionAt1),precisionAt3:p(r.precisionAt3,o.precisionAt3),recallAt3:p(r.recallAt3,o.recallAt3),mrr:p(r.mrr,o.mrr),ndcgAt5:p(r.ndcgAt5,o.ndcgAt5)});return{flatfile:a,baseline:u,cachly:o,lift:y(u),liftVsFlatfile:y(a),queryCount:e.length,corpusSize:t.length}}async function O(){return N(q,B)}const A=t=>`${(t*100).toFixed(1)}%`,h=t=>`${t>=0?"+":""}${(t*100).toFixed(1)}%`;function P(t){const e=(n,s,i,c,a)=>` ${n.padEnd(12)} ${A(s).padStart(8)} ${A(i).padStart(8)} ${A(c).padStart(8)} ${h(a).padStart(8)}`;return["","\u{1F9E0} Cachly-Bench \u2014 recall quality head-to-head","\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",` corpus: ${t.corpusSize} lessons \xB7 queries: ${t.queryCount}`," flat-file = naive term-overlap, no quality signal (\u2248 LLM reading memory files)"," baseline = raw BM25+ keyword ranking"," cachly = BM25+ followed by quality-aware reranking","",` ${"metric".padEnd(12)} ${"flatfile".padStart(8)} ${"baseline".padStart(8)} ${"cachly".padStart(8)} ${"vs flat".padStart(8)}`,e("Precision@1",t.flatfile.precisionAt1,t.baseline.precisionAt1,t.cachly.precisionAt1,t.liftVsFlatfile.precisionAt1),e("Precision@3",t.flatfile.precisionAt3,t.baseline.precisionAt3,t.cachly.precisionAt3,t.liftVsFlatfile.precisionAt3),e("Recall@3",t.flatfile.recallAt3,t.baseline.recallAt3,t.cachly.recallAt3,t.liftVsFlatfile.recallAt3),e("MRR",t.flatfile.mrr,t.baseline.mrr,t.cachly.mrr,t.liftVsFlatfile.mrr),e("nDCG@5",t.flatfile.ndcgAt5,t.baseline.ndcgAt5,t.cachly.ndcgAt5,t.liftVsFlatfile.ndcgAt5),"\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",` vs BM25 baseline : MRR ${h(t.lift.mrr)} \xB7 Precision@1 ${h(t.lift.precisionAt1)}`,` vs flat-file mem : MRR ${h(t.liftVsFlatfile.mrr)} \xB7 Precision@1 ${h(t.liftVsFlatfile.precisionAt1)}`,""].join(`
2
2
  `)}const j=(()=>{try{return import.meta.url===`file://${process.argv[1]}`||process.argv[1]?.endsWith("cachly-bench.ts")||process.argv[1]?.endsWith("cachly-bench.js")}catch{return!1}})();j&&O().then(t=>{process.argv.includes("--json")?process.stdout.write(JSON.stringify(t,null,2)+`
3
3
  `):process.stdout.write(P(t))}).catch(t=>{process.stderr.write(`bench failed: ${t instanceof Error?t.stack:String(t)}
4
- `),process.exit(1)});export{P as formatReport,O as runBenchmark,N as runBenchmarkOn};
4
+ `),process.exit(1)});export{m as LESSON_PREFIX,P as formatReport,v as indexCorpus,S as matchTopic,O as runBenchmark,N as runBenchmarkOn};
@@ -0,0 +1,2 @@
1
+ let u=Number.NEGATIVE_INFINITY;function m(e=Date.now){u=e()}function w(){u=Number.NEGATIVE_INFINITY}function I(e={}){const t=e.fensterMs??3e5,l=e.schwelle??.8,a=e.cpu??(()=>process.cpuUsage()),d=e.jetzt??(()=>Date.now()),f=e.plane??((n,s)=>{setInterval(n,s).unref?.()}),o=e.warne??(n=>process.stderr.write(n+`
2
+ `)),i=e.beende??(()=>process.exit(0));let c=a(),r=0;f(()=>{const n=a(),s=(n.user+n.system-c.user-c.system)/1e3;c=n;const h=d()-u>=t;s>=l*t&&h?(r++,o(`[cachly-mcp] Brand-Wachhund: ${Math.round(s/1e3)} CPU-s in ${Math.round(t/1e3)} s ohne Werkzeugaufruf (Fenster ${r}/2)`)):r=0,r>=2&&(o("[cachly-mcp] Brand-Wachhund: zweites Fenster in Folge \u2014 Server beendet sich (Karte xm54lkjujmyi)"),i())},t)}export{w as _wachhundZuruecksetzen,m as merkeWerkzeugAufruf,I as starteBrandWachhund};
package/dist/src/ckg.js CHANGED
@@ -1 +1 @@
1
- const g=new Set(["that","this","with","from","when","then","also","have","been","will","were","they","them","than","more","some","into","over","only","just","where","while","which","there","their","would","could","should","after","before","about"]);function l(t){return t.toLowerCase().replace(/[^a-z0-9\-:]/g,"-").replace(/-+/g,"-").replace(/^-|-$/g,"").slice(0,80)}function S(t){const c=t.toLowerCase().replace(/[^a-z0-9\s\-_]/g," ").split(/\s+/).filter(n=>n.length>3&&!g.has(n)).slice(0,5);return c.length===0?null:c.slice(0,2).join("-")}async function y(t,c,n,o){const a=`cachly:ckg:node:${c}`,s=await t.get(a),e=s?JSON.parse(s):{id:c,domain:n,type:o,count:0,ts:new Date().toISOString()};e.count=(e.count||0)+1,e.ts=new Date().toISOString(),await t.set(a,JSON.stringify(e))}async function p(t,c,n){const o=`person:${l(c)}`,a=`cachly:ckg:node:${o}`,s=await t.get(a),e=s?JSON.parse(s):{id:o,handle:c,domain:n,type:"person",count:0,last_active:new Date().toISOString()};return e.count=(e.count||0)+1,e.last_active=new Date().toISOString(),e.domain||(e.domain=n),await t.set(a,JSON.stringify(e)),o}async function f(t,c){const n=c.split("/")[0]??"root",o=`file:${l(c)}`,a=`cachly:ckg:node:${o}`,s=await t.get(a),e=s?JSON.parse(s):{id:o,path:c,domain:n,type:"file",count:0,ts:new Date().toISOString()};return e.count=(e.count||0)+1,e.ts=new Date().toISOString(),await t.set(a,JSON.stringify(e)),o}async function h(t,c,n,o="service"){const a=`service:${l(c)}`,s=`cachly:ckg:node:${a}`,e=await t.get(s),r=e?JSON.parse(e):{id:a,name:c,kind:o,domain:n,type:"service",count:0,last_active:new Date().toISOString()};return r.count=(r.count||0)+1,r.last_active=new Date().toISOString(),r.domain||(r.domain=n),o==="system"&&(r.kind="system"),await t.set(s,JSON.stringify(r)),a}async function u(t,c,n,o,a,s=!1){const e=`cachly:ckg:edge:${c}:${n}:${o}`,r=await t.get(e),i=r?JSON.parse(r):{from:c,to:o,edgeType:n,successes:0,trials:0,confidence:0,last_updated:""};i.trials=(i.trials||0)+1,a?i.successes=(i.successes||0)+1:s&&(i.successes=(i.successes||0)+.5),i.confidence=(i.successes+1)/(i.trials+2),i.last_updated=new Date().toISOString(),await t.set(e,JSON.stringify(i)),await t.sadd(`cachly:ckg:idx:from:${c}`,e),await t.sadd(`cachly:ckg:idx:to:${o}`,e)}const w=25;async function d(t,c,n){const o=`cachly:ckg:file:touchers:${c}`,a=await t.smembers(o);for(const s of a.slice(0,w))s!==n&&(await u(t,n,"collaborates",s,!0),await u(t,s,"collaborates",n,!0));await t.sadd(o,n)}export{d as ckgRecordCollaboration,l as ckgSlug,u as ckgUpdateEdge,f as ckgUpsertFileNode,y as ckgUpsertNode,p as ckgUpsertPersonNode,h as ckgUpsertServiceNode,S as extractProblemConcept};
1
+ const y=new Set(["that","this","with","from","when","then","also","have","been","will","were","they","them","than","more","some","into","over","only","just","where","while","which","there","their","would","could","should","after","before","about"]);function f(e){return e.toLowerCase().replace(/[^a-z0-9\-:]/g,"-").replace(/-+/g,"-").replace(/^-|-$/g,"").slice(0,80)}function k(e){const c=e.toLowerCase().replace(/[^a-z0-9\s\-_]/g," ").split(/\s+/).filter(o=>o.length>3&&!y.has(o)).slice(0,5);return c.length===0?null:c.slice(0,2).join("-")}async function O(e,c,o,n){const i=`cachly:ckg:node:${c}`,s=await e.get(i),t=s?JSON.parse(s):{id:c,domain:o,type:n,count:0,ts:new Date().toISOString()};t.count=(t.count||0)+1,t.ts=new Date().toISOString(),await e.set(i,JSON.stringify(t))}async function m(e,c,o){const n=`person:${f(c)}`,i=`cachly:ckg:node:${n}`,s=await e.get(i),t=s?JSON.parse(s):{id:n,handle:c,domain:o,type:"person",count:0,last_active:new Date().toISOString()};return t.count=(t.count||0)+1,t.last_active=new Date().toISOString(),t.domain||(t.domain=o),await e.set(i,JSON.stringify(t)),n}async function $(e,c){const o=c.split("/")[0]??"root",n=`file:${f(c)}`,i=`cachly:ckg:node:${n}`,s=await e.get(i),t=s?JSON.parse(s):{id:n,path:c,domain:o,type:"file",count:0,ts:new Date().toISOString()};return t.count=(t.count||0)+1,t.ts=new Date().toISOString(),await e.set(i,JSON.stringify(t)),n}async function x(e,c,o,n="service"){const i=`service:${f(c)}`,s=`cachly:ckg:node:${i}`,t=await e.get(s),r=t?JSON.parse(t):{id:i,name:c,kind:n,domain:o,type:"service",count:0,last_active:new Date().toISOString()};return r.count=(r.count||0)+1,r.last_active=new Date().toISOString(),r.domain||(r.domain=o),n==="system"&&(r.kind="system"),await e.set(s,JSON.stringify(r)),i}function p(e,c,o,n,i,s=!1){const t=e?JSON.parse(e):{from:c,to:n,edgeType:o,successes:0,trials:0,confidence:0,last_updated:""};return t.trials=(t.trials||0)+1,i?t.successes=(t.successes||0)+1:s&&(t.successes=(t.successes||0)+.5),t.confidence=(t.successes+1)/(t.trials+2),t.last_updated=new Date().toISOString(),t}async function w(e,c,o,n,i,s=!1){const t=`cachly:ckg:edge:${c}:${o}:${n}`,r=await e.get(t),u=p(r,c,o,n,i,s);await e.set(t,JSON.stringify(u)),await e.sadd(`cachly:ckg:idx:from:${c}`,t),await e.sadd(`cachly:ckg:idx:to:${n}`,t)}async function h(e,c){if(c.length===0)return;const o=e,n=typeof o.pipeline=="function"?o.pipeline():null;if(!(n!==null&&typeof n.set=="function"&&typeof n.sadd=="function"&&typeof o.mget=="function")){for(const a of c)await w(e,a.from,a.edgeType,a.to,a.success,a.partial??!1);return}const s=a=>`cachly:ckg:edge:${a.from}:${a.edgeType}:${a.to}`,t=[...new Set(c.map(s))],r=await e.mget(...t),u=new Map(t.map((a,l)=>[a,r[l]??null]));for(const a of c){const l=s(a),d=p(u.get(l)??null,a.from,a.edgeType,a.to,a.success,a.partial??!1);u.set(l,JSON.stringify(d))}const g=n;for(const a of c){const l=s(a);g.set(l,u.get(l)),g.sadd(`cachly:ckg:idx:from:${a.from}`,l),g.sadd(`cachly:ckg:idx:to:${a.to}`,l)}await g.exec()}const S=25;async function N(e,c,o){const n=`cachly:ckg:file:touchers:${c}`,i=await e.smembers(n),s=[];for(const t of i.slice(0,S))t!==o&&(s.push({from:o,edgeType:"collaborates",to:t,success:!0}),s.push({from:t,edgeType:"collaborates",to:o,success:!0}));await h(e,s),await e.sadd(n,o)}export{N as ckgRecordCollaboration,f as ckgSlug,w as ckgUpdateEdge,h as ckgUpdateEdgesGebuendelt,$ as ckgUpsertFileNode,O as ckgUpsertNode,m as ckgUpsertPersonNode,x as ckgUpsertServiceNode,k as extractProblemConcept,p as schreibeKanteFort};
@@ -1,2 +1,2 @@
1
- import{existsSync as m,readFileSync as p}from"node:fs";import{resolve as y}from"node:path";import{AMBIENT_HOOK_VERSION as c}from"./ambient-hooks.js";import{netBalance as g,shouldBackoff as A}from"./ambient-recall.js";import{cachlyUrl as $}from"./cachly-url.js";const k=18;function H(t=process.version){const e=Number(t.replace(/^v/,"").split(".")[0]);return Number.isFinite(e)&&e>=k?{name:"Node.js",status:"ok",detail:`${t}`}:{name:"Node.js",status:"fail",detail:`${t} \u2014 too old`,hint:`cachly needs Node ${k}+ (built-in fetch). Install a current LTS from nodejs.org.`}}function P(t){if(!t)return{name:"Credential",status:"fail",detail:"CACHLY_JWT is not set",hint:"Run `npx @cachly-dev/mcp-server@latest autopilot` (browser sign-in) or export CACHLY_JWT=<cky_\u2026>."};const e=t.startsWith("cky_"),s=t.split(".").length===3;return!e&&!s?{name:"Credential",status:"warn",detail:"CACHLY_JWT is set but looks neither like a cky_\u2026 key nor a JWT",hint:"Re-copy the key from the dashboard (API Keys) \u2014 it may be truncated."}:{name:"Credential",status:"ok",detail:e?"API key (cky_\u2026)":"JWT"}}async function R(t,e=fetch){try{const s=await e(`${t}/health`,{signal:AbortSignal.timeout(5e3)});return s.ok?{name:"API",status:"ok",detail:`${t} reachable`}:{name:"API",status:"fail",detail:`${t}/health returned HTTP ${s.status}`,hint:`The API answered but is unhealthy \u2014 check ${$("/status","doctor")} or your CACHLY_API_URL.`}}catch{return{name:"API",status:"fail",detail:`${t} not reachable`,hint:"Check your network/proxy. Self-hosted: is CACHLY_API_URL correct and the API running?"}}}async function L(t,e,s=fetch){if(!e)return{name:"Auth",status:"fail",detail:"skipped \u2014 no credential",hint:"Fix the Credential check first."};try{const a=await s(`${t}/api/v1/instances`,{headers:{Authorization:`Bearer ${e}`},signal:AbortSignal.timeout(5e3)});return a.ok?{name:"Auth",status:"ok",detail:"credential accepted"}:a.status===401||a.status===403?{name:"Auth",status:"fail",detail:`API rejected the credential (HTTP ${a.status})`,hint:"The key is expired or revoked. Create a new one in the dashboard, or re-run autopilot."}:{name:"Auth",status:"warn",detail:`unexpected HTTP ${a.status} from /api/v1/instances`}}catch{return{name:"Auth",status:"warn",detail:"could not verify (network error)"}}}function S(t){return t?/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(t)?{name:"Instance",status:"ok",detail:t.slice(0,8)+"\u2026"}:{name:"Instance",status:"warn",detail:`"${t.slice(0,24)}" does not look like an instance UUID`,hint:"Copy the instance ID from the dashboard (Instances \u2192 your brain)."}:{name:"Instance",status:"warn",detail:"no CACHLY_BRAIN_INSTANCE_ID set and no default instance resolved",hint:"Run `npx @cachly-dev/mcp-server@latest init` in your project, or export CACHLY_BRAIN_INSTANCE_ID."}}function j(t){const e={settingsExists:!1,wiredEvents:[],scriptFiles:[]};try{const s=y(t,".claude/settings.json");if(e.settingsExists=m(s),e.settingsExists){const a=JSON.parse(p(s,"utf-8"));for(const[n,l]of Object.entries(a.hooks??{})){const u=o=>(o??"").replace(/\\/g,"/");if((l??[]).some(o=>(o.hooks??[]).some(r=>u(r.command).includes(".claude/hooks/cachly-ambient-")))){e.wiredEvents.push(n);for(const o of l??[])for(const r of o.hooks??[]){const h=u(r.command).match(/["']?([^"']*\.claude\/hooks\/cachly-ambient-[^"']+)["']?/);if(!h)continue;const i=h[1].replace("$CLAUDE_PROJECT_DIR",t),d=m(i);let f="unknown";if(d)try{f=p(i,"utf-8").includes(c)?"current":"stale"}catch{}e.scriptFiles.push({path:i,exists:d,version:f})}}}}}catch{}return e}const v=["SessionStart","UserPromptSubmit","PreToolUse","Stop"];function J(t){if(!t.settingsExists||t.wiredEvents.length===0)return{name:"Hooks",status:"warn",detail:"Ambient Recall hooks are not installed in this project",hint:"Run `npx @cachly-dev/mcp-server@latest init` here \u2014 memory then injects itself (no hooks = pull-only)."};const e=v.filter(n=>!t.wiredEvents.includes(n)),s=t.scriptFiles.filter(n=>!n.exists),a=t.scriptFiles.filter(n=>n.exists&&n.version==="stale");return s.length>0?{name:"Hooks",status:"fail",detail:`settings.json wires hooks whose script files are missing (${s.length})`,hint:"Re-run `npx @cachly-dev/mcp-server@latest init` to rewrite the scripts."}:a.length>0?{name:"Hooks",status:"warn",detail:`installed hooks are an older version (current: ${c}); v1/v2 shell hooks never ran on native Windows`,hint:"Re-run `npx @cachly-dev/mcp-server@latest init` to upgrade in place."}:e.length>0?{name:"Hooks",status:"warn",detail:`partial install \u2014 missing events: ${e.join(", ")}`,hint:"Re-run `npx @cachly-dev/mcp-server@latest init` to add the missing hooks."}:{name:"Hooks",status:"ok",detail:`all 4 events wired, scripts ${c}`}}function O(t,e){const s=g(t),a=A(t);return t.length===0?{name:"Ledger",status:"ok",detail:`empty (${e}) \u2014 fills up once ambient hooks inject`}:a?{name:"Ledger",status:"warn",detail:`${t.length} entries, net ${s.net} tokens \u2014 auto-backoff ACTIVE, injection paused`,hint:"The agent has not reported prevented tokens lately. This self-heals; `ambient-stats` shows details."}:{name:"Ledger",status:"ok",detail:`${t.length} entries, net ${s.net>=0?"+":""}${s.net} tokens`}}const b={ok:"\u2705",warn:"\u26A0\uFE0F ",fail:"\u274C"};function F(t){const e=["","\u{1FA7A} cachly doctor",""];for(const n of t)e.push(` ${b[n.status]} ${n.name.padEnd(11)} ${n.detail}`),n.hint&&n.status!=="ok"&&e.push(` \u21B3 ${n.hint}`);const s=t.filter(n=>n.status==="fail").length,a=t.filter(n=>n.status==="warn").length;return e.push(""),e.push(s>0?` ${s} problem${s>1?"s":""} to fix${a?`, ${a} warning${a>1?"s":""}`:""}.`:a>0?` No blockers \u2014 ${a} warning${a>1?"s":""}.`:" Everything looks healthy. Your brain is wired."),e.push(""),e.join(`
2
- `)}function Y(t){return t.some(e=>e.status==="fail")?1:0}export{R as checkApiReachable,L as checkAuthAccepted,P as checkCredential,J as checkHooks,S as checkInstance,O as checkLedger,H as checkNodeVersion,Y as doctorExitCode,j as inspectAmbientHooks,F as renderDoctorReport};
1
+ import{existsSync as f,readFileSync as p}from"node:fs";import{resolve as g}from"node:path";import{AMBIENT_HOOK_VERSION as l}from"./ambient-hooks.js";import{netBalance as y,shouldBackoff as v}from"./ambient-recall.js";import{cachlyUrl as $}from"./cachly-url.js";const k=18;function H(t=process.version){const e=Number(t.replace(/^v/,"").split(".")[0]);return Number.isFinite(e)&&e>=k?{name:"Node.js",status:"ok",detail:`${t}`}:{name:"Node.js",status:"fail",detail:`${t} \u2014 too old`,hint:`cachly needs Node ${k}+ (built-in fetch). Install a current LTS from nodejs.org.`}}function P(t){if(!t)return{name:"Credential",status:"fail",detail:"CACHLY_JWT is not set",hint:"Run `npx @cachly-dev/mcp-server@latest autopilot` (browser sign-in) or export CACHLY_JWT=<cky_\u2026>."};const e=t.startsWith("cky_"),s=t.split(".").length===3;return!e&&!s?{name:"Credential",status:"warn",detail:"CACHLY_JWT is set but looks neither like a cky_\u2026 key nor a JWT",hint:"Re-copy the key from the dashboard (API Keys) \u2014 it may be truncated."}:{name:"Credential",status:"ok",detail:e?"API key (cky_\u2026)":"JWT"}}async function R(t,e=fetch){try{const s=await e(`${t}/health`,{signal:AbortSignal.timeout(5e3)});return s.ok?{name:"API",status:"ok",detail:`${t} reachable`}:{name:"API",status:"fail",detail:`${t}/health returned HTTP ${s.status}`,hint:`The API answered but is unhealthy \u2014 check ${$("/status","doctor")} or your CACHLY_API_URL.`}}catch{return{name:"API",status:"fail",detail:`${t} not reachable`,hint:"Check your network/proxy. Self-hosted: is CACHLY_API_URL correct and the API running?"}}}async function S(t,e,s=fetch){if(!e)return{name:"Auth",status:"fail",detail:"skipped \u2014 no credential",hint:"Fix the Credential check first."};try{const r=await s(`${t}/api/v1/instances`,{headers:{Authorization:`Bearer ${e}`},signal:AbortSignal.timeout(5e3)});return r.ok?{name:"Auth",status:"ok",detail:"credential accepted"}:r.status===401||r.status===403?{name:"Auth",status:"fail",detail:`API rejected the credential (HTTP ${r.status})`,hint:"The key is expired or revoked. Create a new one in the dashboard, or re-run autopilot."}:{name:"Auth",status:"warn",detail:`unexpected HTTP ${r.status} from /api/v1/instances`}}catch{return{name:"Auth",status:"warn",detail:"could not verify (network error)"}}}async function L(t,e,s,r=fetch){if(!e||!s)return{name:"Vektoren",status:"warn",detail:"skipped \u2014 no credential or instance"};try{const n=await r(`${t}/api/v1/instances/${s}/vector-coverage`,{headers:{Authorization:`Bearer ${e}`},signal:AbortSignal.timeout(12e3)});if(!n.ok)return{name:"Vektoren",status:"warn",detail:`coverage not measured (HTTP ${n.status})`};const a=await n.json();return a.status==="leer"?{name:"Vektoren",status:"ok",detail:"no lessons yet"}:a.status==="nicht-gemessen"?{name:"Vektoren",status:"warn",detail:"instance not running \u2014 not measured"}:a.status==="aus"?{name:"Vektoren",status:"fail",detail:`semantic search is OFF: ${a.lessons} lessons, 0 vectors`,hint:"Embeddings never ran for this instance. Check the embed provider; recall runs word-only until fixed."}:a.status==="luecke"?{name:"Vektoren",status:"warn",detail:`${a.vectors}/${a.lessons} lessons have vectors (${a.percent.toFixed(0)}%)`,hint:"Some lessons are invisible to semantic search. Large writes under the rate limit can skip embedding."}:{name:"Vektoren",status:"ok",detail:`${a.vectors}/${a.lessons} vectors (${a.percent.toFixed(0)}%)`}}catch{return{name:"Vektoren",status:"warn",detail:"could not verify (network error)"}}}function V(t){return t?/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(t)?{name:"Instance",status:"ok",detail:t.slice(0,8)+"\u2026"}:{name:"Instance",status:"warn",detail:`"${t.slice(0,24)}" does not look like an instance UUID`,hint:"Copy the instance ID from the dashboard (Instances \u2192 your brain)."}:{name:"Instance",status:"warn",detail:"no CACHLY_BRAIN_INSTANCE_ID set and no default instance resolved",hint:"Run `npx @cachly-dev/mcp-server@latest init` in your project, or export CACHLY_BRAIN_INSTANCE_ID."}}function j(t){const e={settingsExists:!1,wiredEvents:[],scriptFiles:[]};try{const s=g(t,".claude/settings.json");if(e.settingsExists=f(s),e.settingsExists){const r=JSON.parse(p(s,"utf-8"));for(const[n,a]of Object.entries(r.hooks??{})){const u=o=>(o??"").replace(/\\/g,"/");if((a??[]).some(o=>(o.hooks??[]).some(i=>u(i.command).includes(".claude/hooks/cachly-ambient-")))){e.wiredEvents.push(n);for(const o of a??[])for(const i of o.hooks??[]){const h=u(i.command).match(/["']?([^"']*\.claude\/hooks\/cachly-ambient-[^"']+)["']?/);if(!h)continue;const c=h[1].replace("$CLAUDE_PROJECT_DIR",t),d=f(c);let m="unknown";if(d)try{m=p(c,"utf-8").includes(l)?"current":"stale"}catch{}e.scriptFiles.push({path:c,exists:d,version:m})}}}}}catch{}return e}const A=["SessionStart","UserPromptSubmit","PreToolUse","Stop"];function F(t){if(!t.settingsExists||t.wiredEvents.length===0)return{name:"Hooks",status:"warn",detail:"Ambient Recall hooks are not installed in this project",hint:"Run `npx @cachly-dev/mcp-server@latest init` here \u2014 memory then injects itself (no hooks = pull-only)."};const e=A.filter(n=>!t.wiredEvents.includes(n)),s=t.scriptFiles.filter(n=>!n.exists),r=t.scriptFiles.filter(n=>n.exists&&n.version==="stale");return s.length>0?{name:"Hooks",status:"fail",detail:`settings.json wires hooks whose script files are missing (${s.length})`,hint:"Re-run `npx @cachly-dev/mcp-server@latest init` to rewrite the scripts."}:r.length>0?{name:"Hooks",status:"warn",detail:`installed hooks are an older version (current: ${l}); v1/v2 shell hooks never ran on native Windows`,hint:"Re-run `npx @cachly-dev/mcp-server@latest init` to upgrade in place."}:e.length>0?{name:"Hooks",status:"warn",detail:`partial install \u2014 missing events: ${e.join(", ")}`,hint:"Re-run `npx @cachly-dev/mcp-server@latest init` to add the missing hooks."}:{name:"Hooks",status:"ok",detail:`all 4 events wired, scripts ${l}`}}function O(t,e){const s=y(t),r=v(t);return t.length===0?{name:"Ledger",status:"ok",detail:`empty (${e}) \u2014 fills up once ambient hooks inject`}:r?{name:"Ledger",status:"warn",detail:`${t.length} entries, net ${s.net} tokens \u2014 auto-backoff ACTIVE, injection paused`,hint:"The agent has not reported prevented tokens lately. This self-heals; `ambient-stats` shows details."}:{name:"Ledger",status:"ok",detail:`${t.length} entries, net ${s.net>=0?"+":""}${s.net} tokens`}}const w={ok:"\u2705",warn:"\u26A0\uFE0F ",fail:"\u274C"};function J(t){const e=["","\u{1FA7A} cachly doctor",""];for(const n of t)e.push(` ${w[n.status]} ${n.name.padEnd(11)} ${n.detail}`),n.hint&&n.status!=="ok"&&e.push(` \u21B3 ${n.hint}`);const s=t.filter(n=>n.status==="fail").length,r=t.filter(n=>n.status==="warn").length;return e.push(""),e.push(s>0?` ${s} problem${s>1?"s":""} to fix${r?`, ${r} warning${r>1?"s":""}`:""}.`:r>0?` No blockers \u2014 ${r} warning${r>1?"s":""}.`:" Everything looks healthy. Your brain is wired."),e.push(""),e.join(`
2
+ `)}function B(t){return t.some(e=>e.status==="fail")?1:0}export{R as checkApiReachable,S as checkAuthAccepted,P as checkCredential,F as checkHooks,V as checkInstance,O as checkLedger,H as checkNodeVersion,L as checkVectorCoverage,B as doctorExitCode,j as inspectAmbientHooks,J as renderDoctorReport};
@@ -0,0 +1 @@
1
+ const t=[/(?:bewusst|ausdruecklich|explizit)?\s*(?:haben wir uns|wir haben uns|ich habe mich|hat sich)?\s*entschieden gegen\s+([^,.;—\n]{3,80})(?:\s*[,—]\s*(?:weil|da|grund:?)\s+([^.;\n]{3,200}))?/i,/(?:bewusst|ausdruecklich|explizit)\s+gegen\s+([^,.;—\n]{3,80}?)\s+entschieden(?:\s*[,—]\s*(?:weil|da|grund:?)\s+([^.;\n]{3,200}))?/i,/decided against\s+(.{3,80}?)\s+because\s+([^.;\n]{3,200})/i,/decided against\s+([^,.;\n]{3,80})/i];function u(e){for(const i of t){const n=i.exec(e);if(!n)continue;const s=n[1].trim().replace(/\s+/g," ");if(!/^\d|^(mittag|abend|morgen|ende|anfang)\b/i.test(s))return{gegen:s,grund:n[2]?n[2].trim().replace(/\s+/g," "):null}}return null}function c(e){return`\u2696\uFE0F **Bewusst entschieden GEGEN ${e.gegen}**${e.grund?` \u2014 Grund: ${e.grund}`:""}. Nicht ohne NEUEN Grund aufweichen.`}export{c as entscheidungsZeile,u as leiteEntscheidungAb};
@@ -0,0 +1 @@
1
+ import{inhaltsWoerter as l,grobStamm as f}from"./rangfolge.js";const h=/\b(nicht mehr|doch nicht|stattdessen|statt dessen|in wahrheit|widerlegt|war falsch|stimmt nicht|korrektur|korrigiert|entgegen|no longer|instead|actually|turned out|wrong|superseded)\b/i;function u(t){return new Set([...l(t)].map(f))}function d(t,r){if(t.size===0||r.size===0)return 0;let n=0;for(const i of t)r.has(i)&&n++;return n/(t.size+r.size-n)}function g(t,r){const n=u(t.replace(/[:\-_/.]+/g," ")),i=u(r.replace(/[:\-_/.]+/g," "));if(n.size===0||i.size===0)return 0;let s=0;for(const e of n)i.has(e)&&s++;return s/Math.min(n.size,i.size)}function p(t,r,n){const i=r.match(h);if(!i)return null;const s=u(r);let e=null;for(const o of n){if(o.topic===t)continue;const c=g(t,o.topic),a=d(s,u(o.what_worked??""));c>=.5?(!e||c>e.wert)&&(e={topic:o.topic,grund:`Themen-Naehe ${(100*c).toFixed(0)} %`,wert:c}):a>=.3&&(!e||a>e.wert)&&(e={topic:o.topic,grund:`Text-Ueberlappung ${(100*a).toFixed(0)} %`,wert:a})}return e?`\u{1F501} Das klingt wie eine Korrektur von \`${e.topic}\` ("${i[0]}" + ${e.grund}). Wenn ja: dieselbe Lektion noch einmal mit \`ersetzt="${e.topic}"\` schreiben \u2014 dann wird die alte Fassung im Abruf verdraengt statt weiter als aktuell geliefert. Wenn nein: nichts zu tun.`:null}export{p as schlageErsetzungVor,g as themenNaehe};
@@ -0,0 +1 @@
1
+ const c=14;function N(i,r){if(!r)return[];const e=Date.parse(i);if(Number.isNaN(e))return[];const t=[],s=r.ersetzt_am?Date.parse(r.ersetzt_am):NaN;Number.isNaN(s)||(s>e&&t.push("widersprochen"),s<e&&t.push("veraltet"));const n=e+14*864e5;return(r.audit_trail??[]).some(u=>{const o=u.ts?Date.parse(u.ts):NaN;return!Number.isNaN(o)&&o>e&&o<=n})&&t.push("korrigiert"),t}function f(i){return`cachly:liefer:journal:${i}`}const p=5e3;export{p as JOURNAL_DECKEL,c as KORREKTUR_TAGE,N as etikettenFuer,f as lieferJournalSchluessel};
@@ -1,10 +1,10 @@
1
- import{ckgSlug as R}from"../ckg.js";import{safeJsonParse as v}from"../utils.js";import{ersparteMinuten as E,zaehltAlsErsparnis as j}from"../wertbeitrag.js";const F=new Set(["memory_consolidate","brain_diff","causal_trace","knowledge_decay","autopilot"]);async function U(B,A,M,K){switch(B){case"memory_consolidate":{const{instance_id:u,dry_run:h=!1,stale_days:w=90}=A,m=await M(u),l=Date.now(),y=w*86400*1e3;let $=0;const p=[];do{const[e,s]=await m.scan($,"MATCH","cachly:lesson:best:*","COUNT",200);$=parseInt(e),p.push(...s)}while($!==0);if(p.length===0)return"\u{1F9E0} **Brain is empty** \u2014 no lessons to consolidate yet. Use `learn_from_attempts` after your next bug fix.";const a=new Map;for(const e of p){const s=await m.get(e);if(!s)continue;const o=v(s,null);o&&a.set(e,o)}const g=new Map;for(const[e,s]of a){const o=s.topic.split(":")[0],n=g.get(o)??[];n.push(e),g.set(o,n)}const _=[],x=new Map;for(const[e,s]of a){const o=x.get(s.topic)??[];o.push(e),x.set(s.topic,o)}for(const[e,s]of x)new Set(s.map(n=>a.get(n)?.outcome)).size>1&&_.push({topic:e,keys:s});const b=[];for(const[e,s]of a){const o=l-new Date(s.ts).getTime(),n=s.recall_count??0;o>y&&n===0&&b.push(e)}const f=0;if(!h)for(const e of b)await m.expire(e,86400*30);return[`\u{1F52C} **Memory Consolidation Report** ${h?"(dry run \u2014 no changes made)":"\u2705 Applied"}`,"",`\u{1F4CA} **Before:** ${p.length} lessons`,"",`\u{1F501} **Contradictions detected:** ${_.length}`,..._.slice(0,5).map(e=>` \u2192 \`${e.topic}\`: ${e.keys.length} conflicting entries (kept: success)`),_.length>5?` \u2026 and ${_.length-5} more`:"","",`\u267B\uFE0F **Duplicate clusters (counted, never deleted):** ${Array.from(g.values()).filter(e=>e.length>1).length}`,"",`\u{1F570}\uFE0F **Stale entries (${w}d, 0 recalls):** ${b.length}`+(b.length>0&&!h?" \u2192 set to expire in 30 days":""),"",`\u{1F4CA} **After:** ${p.length-f} lessons`,"",h?"\u{1F4A1} Re-run without dry_run=true to apply changes.":'\u2728 Brain consolidated. Run `brain_diff(since="1h")` to see the delta.'].filter(e=>e!=="").join(`
2
- `)}case"brain_diff":{const{instance_id:u,since:h="7d",format:w="summary"}=A,m=await M(u);let l;const y=h.match(/^(\d+)([dhm])$/);if(y){const t=parseInt(y[1]),r=y[2],d=r==="d"?864e5:r==="h"?36e5:6e4;l=Date.now()-t*d}else l=new Date(h).getTime()||Date.now()-7*864e5;let $=0;const p=[];do{const[t,r]=await m.scan($,"MATCH","cachly:lesson:best:*","COUNT",200);$=parseInt(t),p.push(...r)}while($!==0);const a=[],g=[],_=[],x=p.length;for(const t of p){const r=await m.get(t);if(r){const d=v(r,null);if(!d)continue;if(new Date(d.ts).getTime()>=l){const D=`cachly:lesson:history:${d.topic}`;await m.llen(D)<=1?a.push(d):g.push(d)}(d.recall_count??0)>0&&_.push(d)}}const f=[`\u{1F4CA} **Brain Diff** \u2014 ${y?`last ${h}`:new Date(l).toLocaleDateString("de-DE")}`,"",`Total lessons in brain: **${x}**`,"",`\u2705 **New** (${a.length}):`,...a.slice(0,w==="detailed"?20:5).map(t=>` + \`${t.topic}\` \u2014 ${t.outcome} ${t.severity==="critical"?"\u{1F534}":t.severity==="major"?"\u{1F7E0}":"\u{1F7E2}"}`),a.length>5&&w==="summary"?` \u2026 and ${a.length-5} more`:"","",`\u{1F504} **Updated** (${g.length}):`,...g.slice(0,w==="detailed"?20:5).map(t=>` ~ \`${t.topic}\` \u2014 now: ${t.outcome}`),g.length>5&&w==="summary"?` \u2026 and ${g.length-5} more`:"","",`\u{1F50D} **Active** (recalled at least once): ${_.length}`,""].filter(t=>t!==""),c=[],e=m.scanStream({match:"cachly:ckg:node:resolution-*",count:100});await new Promise((t,r)=>{e.on("data",d=>c.push(...d)),e.on("end",t),e.on("error",r)});const s=[];for(const t of c){const r=await m.get(t);if(r){const d=v(r,null);d?.ts&&new Date(d.ts).getTime()>=l&&s.push({topic:d.topic??t,resolution:d.resolution??"unknown",ts:d.ts})}}if(s.length>0){f.push(`\u{1F5F3}\uFE0F **MADC Resolutions** (${s.length} contested beliefs resolved):`);for(const t of s.slice(0,5)){const r=t.resolution==="unanimous_success"?"\u2705":t.resolution==="unanimous_failure"?"\u274C":"\u26A0\uFE0F";f.push(` ${r} \`${t.topic}\` \u2192 ${t.resolution}`)}f.push("")}const o=new Set(g.concat(_).map(t=>t.topic.split(":")[0])),n=[...new Set(a.map(t=>t.topic.split(":")[0]))].filter(t=>!o.has(t));n.length>0&&f.push(`\u{1F331} **New domains bootstrapped:** ${n.map(t=>`\`${t}\``).join(", ")}`,"");const i=(await m.lrange("cachly:fedbrain:federations",-20,-1)).map(t=>v(t,null)).filter(t=>t!==null&&new Date(t.transferred_at).getTime()>=l);if(i.length>0){f.push(`\u{1F9E0} **FedBrain transfers received (${i.length}):**`);for(const t of i.slice(0,3))f.push(` \u{1F4E5} domain \`${t.domain}\` from \`${t.source.slice(0,16)}\u2026\` \u2014 ${t.nodes} nodes, ${t.edges} edges`);f.push("")}return f.push("\u{1F4A1} Run `memory_consolidate` to merge duplicates \xB7 `knowledge_decay` to see confidence scores."),f.join(`
3
- `)}case"causal_trace":{const{instance_id:u,problem:h,max_depth:w=5,tags:m=[]}=A,l=await M(u),y=h.toLowerCase().replace(/[^a-z0-9\s\-_:]/g," ").split(/\s+/).filter(e=>e.length>2),$={critical:"\u{1F534}",major:"\u{1F7E0}",minor:"\u{1F7E1}"},p=[];try{for(const e of y.slice(0,4)){const s=await l.smembers(`cachly:ckg:idx:from:${R(e)}`),o=await l.smembers(`cachly:ckg:idx:to:${R(e)}`),n=[],k=l.scanStream({match:`cachly:ckg:node:*${e}*`,count:50});await new Promise((i,t)=>{k.on("data",r=>n.push(...r)),k.on("end",i),k.on("error",t)});for(const i of n.slice(0,10)){const t=await l.get(i);if(!t)continue;const r=v(t,null);if(!r)continue;const d=await l.smembers(`cachly:ckg:idx:from:${r.id}`);for(const T of d.slice(0,20)){const D=await l.get(T);if(!D)continue;const C=v(D,null);if(!C||C.edgeType!=="fixes"&&C.edgeType!=="requires")continue;const S=await l.get(`cachly:lesson:best:${C.from.replace(/-/g,":").replace(/^fix:/,"fix:")}`),I=S?v(S,null)??void 0:void 0;p.push({conceptId:r.id,edge:C,lesson:I??void 0})}}for(const i of[...s,...o].slice(0,20)){const t=await l.get(i);if(!t)continue;const r=v(t,null);if(!r)continue;const d=await l.get(`cachly:lesson:best:${r.from}`),T=d?v(d,null)??void 0:void 0;p.push({conceptId:r.from,edge:r,lesson:T??void 0})}}}catch{}const a=new Set,g=p.filter(e=>{const s=`${e.edge.from}:${e.edge.edgeType}:${e.edge.to}`;return a.has(s)?!1:(a.add(s),!0)}).sort((e,s)=>s.edge.confidence-e.edge.confidence).slice(0,w);let _=0;const x=[];do{const[e,s]=await l.scan(_,"MATCH","cachly:lesson:*","COUNT",200);_=parseInt(e),x.push(...s)}while(_!==0);const b=[];for(const e of x){const s=await l.get(e);if(s){const o=v(s,null);if(!o||m.length>0&&!(o.tags??[]).some(i=>m.includes(i)))continue;const n=[o.topic,o.what_failed??"",o.what_worked??"",o.context??""].join(" ").toLowerCase(),k=y.reduce((i,t)=>i+(n.includes(t)?1:0),0);k>0&&b.push({score:k,lesson:o,key:e})}}b.sort((e,s)=>s.score-e.score);const f=b.slice(0,w);if(f.length===0&&g.length===0)return[`\u{1F50D} **Causal Trace: "${h}"**`,"","No matching lessons found in brain.","","\u{1F4A1} After you solve this, call:","```","learn_from_attempts(",` instance_id = "${u}",`,` topic = "fix:${y[0]??"issue"}",`,' outcome = "success",',' what_worked = "...",',` what_failed = "${h}",`,")","```"].join(`
4
- `);const c=[`\u{1F50D} **Causal Trace: "${h}"**`,""];if(g.length>0){c.push("### \u{1F578}\uFE0F CKG Graph (confidence-ranked)");for(const e of g){const s=Math.round(e.edge.confidence*100),o="\u2593".repeat(Math.round(s/10))+"\u2591".repeat(10-Math.round(s/10));c.push(` ${e.edge.from} **\u2192[${e.edge.edgeType}]\u2192** ${e.edge.to}`),c.push(` ${o} ${s}% confidence (${e.edge.successes}/${e.edge.trials} confirmed)`),e.lesson?.what_worked&&c.push(` \u2705 Fix: ${e.lesson.what_worked.slice(0,150)}`),c.push("")}}if(f.length>0){c.push(g.length>0?`### \u{1F4DA} Text Search (${f.length} related lessons)`:`Found **${f.length}** related lessons. Reconstructed causal chain:`,"");const e=f.filter(n=>n.lesson.outcome!=="success"),s=f.filter(n=>n.lesson.outcome==="success");e.length>0&&(c.push("**Root causes & failure chain:**"),e.forEach((n,k)=>{const i=n.lesson,t=$[i.severity??"minor"]??"\u{1F7E1}";c.push(`${k===0?" Root:":" \u2192 :"} ${t} \`${i.topic}\``),i.what_failed&&c.push(` \u21B3 ${i.what_failed.slice(0,120)}`)}),c.push("")),s.length>0&&(c.push("**Solutions that worked before:**"),s.forEach((n,k)=>{const i=n.lesson,t=new Date(i.ts).toLocaleDateString("de-DE");c.push(` ${k+1}. \u2705 \`${i.topic}\` \u2014 ${t} \xB7 recalled ${i.recall_count??0}\xD7`),i.what_worked&&c.push(` ${i.what_worked.slice(0,200)}`)}),c.push(""));const o=s[0]?.lesson;if(o?.what_worked){c.push("**\u26A1 Most likely fix:**"),c.push("```"),c.push(o.what_worked.slice(0,500)),c.push("```"),c.push("");const n=j(o)?E(o,"trace"):0;n>0&&l.incrbyfloat(`cachly:stats:time_saved_mins:${u}`,n).catch(()=>{})}}return c.push(`\u{1F4A1} After applying: \`learn_from_attempts(topic="fix:${y[0]??"issue"}", outcome="success", ...)\``),g.length>0&&c.push(`\u{1F578}\uFE0F Explore graph: \`ckg_inspect(concept="${y[0]??"fix"}")\``),c.join(`
5
- `)}case"knowledge_decay":{let _=function(e){const s=Math.round(e/10);return"\u2588".repeat(s)+"\u2591".repeat(10-s)+` ${e}%`};const{instance_id:u,min_age_days:h=0,show_top:w=20}=A,m=await M(u),l=Date.now(),y=h*864e5;let $=0;const p=[];do{const[e,s]=await m.scan($,"MATCH","cachly:lesson:best:*","COUNT",200);$=parseInt(e),p.push(...s)}while($!==0);const a=[];for(const e of p){const s=await m.get(e);if(s){const o=v(s,null);if(!o)continue;const n=l-new Date(o.ts).getTime();if(n<y)continue;const k=Math.floor(n/864e5),i=o.recall_count??0,t=Math.max(0,k-7),r=Math.max(5,100-t),d=Math.min(50,i*5),T=o.outcome==="failure"?-20:0,D=Math.min(100,Math.max(0,r+d+T));a.push({topic:o.topic,confidence:D,age_days:k,recalls:i,outcome:o.outcome})}}a.sort((e,s)=>e.confidence-s.confidence);const g=a.slice(0,w),x=a.length>0?Math.round(a.reduce((e,s)=>e+s.confidence,0)/a.length):0,b=a.filter(e=>e.confidence<30).length,f=a.filter(e=>e.confidence>=70).length;return[`\u{1F9EA} **Knowledge Decay Report** \u2014 ${a.length} lessons`,"",`Average confidence: **${_(x)}**`,`\u{1F534} Critical (<30%): ${b} \u{1F7E2} Healthy (\u226570%): ${f}`,"","Lowest confidence lessons (fix these first):","",...g.map((e,s)=>`${(s+1).toString().padStart(2)}. \`${e.topic}\`
6
- ${_(e.confidence)} \xB7 ${e.age_days}d old \xB7 ${e.recalls} recalls \xB7 ${e.outcome}`),"",a.length>w?`\u2026 and ${a.length-w} more lessons`:"","",b>0?`\u26A0\uFE0F ${b} lessons are critically stale. Validate them and re-run \`learn_from_attempts\` with updated findings.`:"\u2705 Brain confidence is healthy overall.","","\u{1F4A1} Run `memory_consolidate` to prune stale entries \xB7 `causal_trace` to find root causes."].filter(e=>e!=="").join(`
7
- `)}case"autopilot":{const{instance_id:u,editor:h="claude",project_name:w="this project",style:m="full"}=A,l=`## Cachly AI Brain \u2014 Autopilot
1
+ import{ckgSlug as R}from"../ckg.js";import{safeJsonParse as v}from"../utils.js";import{ersparteMinuten as E,zaehltAlsErsparnis as j}from"../wertbeitrag.js";import{findePaare as L}from"../zulassung-und-urteil.js";const H=new Set(["memory_consolidate","brain_diff","causal_trace","knowledge_decay","autopilot"]);async function W(B,S,A,K){switch(B){case"memory_consolidate":{const{instance_id:u,dry_run:h=!1,stale_days:y=90}=S,g=await A(u),l=Date.now(),$=y*86400*1e3;let _=0;const m=[];do{const[s,o]=await g.scan(_,"MATCH","cachly:lesson:best:*","COUNT",200);_=parseInt(s),m.push(...o)}while(_!==0);if(m.length===0)return"\u{1F9E0} **Brain is empty** \u2014 no lessons to consolidate yet. Use `learn_from_attempts` after your next bug fix.";const c=new Map;for(const s of m){const o=await g.get(s);if(!o)continue;const e=v(o,null);e&&c.set(s,e)}const w=new Map;for(const[s,o]of c){const e=o.topic.split(":")[0],a=w.get(e)??[];a.push(s),w.set(e,a)}const b=[...c.values()],x=L(b),k=x.filter(s=>s.beziehung==="widerspruch"),p=x.filter(s=>s.beziehung==="ersetzung"),i=[],t=new Map;for(const[s,o]of c){const e=t.get(o.topic)??[];e.push(s),t.set(o.topic,e)}for(const[s,o]of t)new Set(o.map(a=>c.get(a)?.outcome)).size>1&&i.push({topic:s,keys:o});const n=[];for(const[s,o]of c){const e=l-new Date(o.ts).getTime(),a=o.recall_count??0;e>$&&a===0&&n.push(s)}const r=0;if(!h)for(const s of n)await g.expire(s,86400*30);return[`\u{1F52C} **Memory Consolidation Report** ${h?"(dry run \u2014 no changes made)":"\u2705 Applied"}`,"",`\u{1F4CA} **Before:** ${m.length} lessons`,"",`\u{1F501} **Contradictions \u2014 exact topic match (old rule):** ${i.length}`,`\u{1F50E} **Same subject, admitted for judgement:** ${x.length} \u2192 contradiction ${k.length} \xB7 supersession ${p.length}`,...k.slice(0,5).map(s=>` \u2192 \`${s.a.topic}\` vs \`${s.b.topic}\` (${s.naehe.toFixed(2)}): ${s.grund}`),k.length>5?` \u2026 and ${k.length-5} more`:"",...p.slice(0,3).map(s=>` \u21BB \`${s.a.topic}\` \u2192 \`${s.b.topic}\`: ${s.grund}`),...i.slice(0,5).map(s=>` \u2192 \`${s.topic}\`: ${s.keys.length} conflicting entries (kept: success)`),i.length>5?` \u2026 and ${i.length-5} more`:"","",`\u267B\uFE0F **Duplicate clusters (counted, never deleted):** ${Array.from(w.values()).filter(s=>s.length>1).length}`,"",`\u{1F570}\uFE0F **Stale entries (${y}d, 0 recalls):** ${n.length}`+(n.length>0&&!h?" \u2192 set to expire in 30 days":""),"",`\u{1F4CA} **After:** ${m.length-r} lessons`,"",h?"\u{1F4A1} Re-run without dry_run=true to apply changes.":'\u2728 Brain consolidated. Run `brain_diff(since="1h")` to see the delta.'].filter(s=>s!=="").join(`
2
+ `)}case"brain_diff":{const{instance_id:u,since:h="7d",format:y="summary"}=S,g=await A(u);let l;const $=h.match(/^(\d+)([dhm])$/);if($){const e=parseInt($[1]),a=$[2],d=a==="d"?864e5:a==="h"?36e5:6e4;l=Date.now()-e*d}else l=new Date(h).getTime()||Date.now()-7*864e5;let _=0;const m=[];do{const[e,a]=await g.scan(_,"MATCH","cachly:lesson:best:*","COUNT",200);_=parseInt(e),m.push(...a)}while(_!==0);const c=[],w=[],b=[],x=m.length;for(const e of m){const a=await g.get(e);if(a){const d=v(a,null);if(!d)continue;if(new Date(d.ts).getTime()>=l){const D=`cachly:lesson:history:${d.topic}`;await g.llen(D)<=1?c.push(d):w.push(d)}(d.recall_count??0)>0&&b.push(d)}}const p=[`\u{1F4CA} **Brain Diff** \u2014 ${$?`last ${h}`:new Date(l).toLocaleDateString("de-DE")}`,"",`Total lessons in brain: **${x}**`,"",`\u2705 **New** (${c.length}):`,...c.slice(0,y==="detailed"?20:5).map(e=>` + \`${e.topic}\` \u2014 ${e.outcome} ${e.severity==="critical"?"\u{1F534}":e.severity==="major"?"\u{1F7E0}":"\u{1F7E2}"}`),c.length>5&&y==="summary"?` \u2026 and ${c.length-5} more`:"","",`\u{1F504} **Updated** (${w.length}):`,...w.slice(0,y==="detailed"?20:5).map(e=>` ~ \`${e.topic}\` \u2014 now: ${e.outcome}`),w.length>5&&y==="summary"?` \u2026 and ${w.length-5} more`:"","",`\u{1F50D} **Active** (recalled at least once): ${b.length}`,""].filter(e=>e!==""),i=[],t=g.scanStream({match:"cachly:ckg:node:resolution-*",count:100});await new Promise((e,a)=>{t.on("data",d=>i.push(...d)),t.on("end",e),t.on("error",a)});const n=[];for(const e of i){const a=await g.get(e);if(a){const d=v(a,null);d?.ts&&new Date(d.ts).getTime()>=l&&n.push({topic:d.topic??e,resolution:d.resolution??"unknown",ts:d.ts})}}if(n.length>0){p.push(`\u{1F5F3}\uFE0F **MADC Resolutions** (${n.length} contested beliefs resolved):`);for(const e of n.slice(0,5)){const a=e.resolution==="unanimous_success"?"\u2705":e.resolution==="unanimous_failure"?"\u274C":"\u26A0\uFE0F";p.push(` ${a} \`${e.topic}\` \u2192 ${e.resolution}`)}p.push("")}const r=new Set(w.concat(b).map(e=>e.topic.split(":")[0])),f=[...new Set(c.map(e=>e.topic.split(":")[0]))].filter(e=>!r.has(e));f.length>0&&p.push(`\u{1F331} **New domains bootstrapped:** ${f.map(e=>`\`${e}\``).join(", ")}`,"");const o=(await g.lrange("cachly:fedbrain:federations",-20,-1)).map(e=>v(e,null)).filter(e=>e!==null&&new Date(e.transferred_at).getTime()>=l);if(o.length>0){p.push(`\u{1F9E0} **FedBrain transfers received (${o.length}):**`);for(const e of o.slice(0,3))p.push(` \u{1F4E5} domain \`${e.domain}\` from \`${e.source.slice(0,16)}\u2026\` \u2014 ${e.nodes} nodes, ${e.edges} edges`);p.push("")}return p.push("\u{1F4A1} Run `memory_consolidate` to merge duplicates \xB7 `knowledge_decay` to see confidence scores."),p.join(`
3
+ `)}case"causal_trace":{const{instance_id:u,problem:h,max_depth:y=5,tags:g=[]}=S,l=await A(u),$=h.toLowerCase().replace(/[^a-z0-9\s\-_:]/g," ").split(/\s+/).filter(t=>t.length>2),_={critical:"\u{1F534}",major:"\u{1F7E0}",minor:"\u{1F7E1}"},m=[];try{for(const t of $.slice(0,4)){const n=await l.smembers(`cachly:ckg:idx:from:${R(t)}`),r=await l.smembers(`cachly:ckg:idx:to:${R(t)}`),f=[],s=l.scanStream({match:`cachly:ckg:node:*${t}*`,count:50});await new Promise((o,e)=>{s.on("data",a=>f.push(...a)),s.on("end",o),s.on("error",e)});for(const o of f.slice(0,10)){const e=await l.get(o);if(!e)continue;const a=v(e,null);if(!a)continue;const d=await l.smembers(`cachly:ckg:idx:from:${a.id}`);for(const T of d.slice(0,20)){const D=await l.get(T);if(!D)continue;const C=v(D,null);if(!C||C.edgeType!=="fixes"&&C.edgeType!=="requires")continue;const M=await l.get(`cachly:lesson:best:${C.from.replace(/-/g,":").replace(/^fix:/,"fix:")}`),I=M?v(M,null)??void 0:void 0;m.push({conceptId:a.id,edge:C,lesson:I??void 0})}}for(const o of[...n,...r].slice(0,20)){const e=await l.get(o);if(!e)continue;const a=v(e,null);if(!a)continue;const d=await l.get(`cachly:lesson:best:${a.from}`),T=d?v(d,null)??void 0:void 0;m.push({conceptId:a.from,edge:a,lesson:T??void 0})}}}catch{}const c=new Set,w=m.filter(t=>{const n=`${t.edge.from}:${t.edge.edgeType}:${t.edge.to}`;return c.has(n)?!1:(c.add(n),!0)}).sort((t,n)=>n.edge.confidence-t.edge.confidence).slice(0,y);let b=0;const x=[];do{const[t,n]=await l.scan(b,"MATCH","cachly:lesson:*","COUNT",200);b=parseInt(t),x.push(...n)}while(b!==0);const k=[];for(const t of x){const n=await l.get(t);if(n){const r=v(n,null);if(!r||g.length>0&&!(r.tags??[]).some(o=>g.includes(o)))continue;const f=[r.topic,r.what_failed??"",r.what_worked??"",r.context??""].join(" ").toLowerCase(),s=$.reduce((o,e)=>o+(f.includes(e)?1:0),0);s>0&&k.push({score:s,lesson:r,key:t})}}k.sort((t,n)=>n.score-t.score);const p=k.slice(0,y);if(p.length===0&&w.length===0)return[`\u{1F50D} **Causal Trace: "${h}"**`,"","No matching lessons found in brain.","","\u{1F4A1} After you solve this, call:","```","learn_from_attempts(",` instance_id = "${u}",`,` topic = "fix:${$[0]??"issue"}",`,' outcome = "success",',' what_worked = "...",',` what_failed = "${h}",`,")","```"].join(`
4
+ `);const i=[`\u{1F50D} **Causal Trace: "${h}"**`,""];if(w.length>0){i.push("### \u{1F578}\uFE0F CKG Graph (confidence-ranked)");for(const t of w){const n=Math.round(t.edge.confidence*100),r="\u2593".repeat(Math.round(n/10))+"\u2591".repeat(10-Math.round(n/10));i.push(` ${t.edge.from} **\u2192[${t.edge.edgeType}]\u2192** ${t.edge.to}`),i.push(` ${r} ${n}% confidence (${t.edge.successes}/${t.edge.trials} confirmed)`),t.lesson?.what_worked&&i.push(` \u2705 Fix: ${t.lesson.what_worked.slice(0,150)}`),i.push("")}}if(p.length>0){i.push(w.length>0?`### \u{1F4DA} Text Search (${p.length} related lessons)`:`Found **${p.length}** related lessons. Reconstructed causal chain:`,"");const t=p.filter(f=>f.lesson.outcome!=="success"),n=p.filter(f=>f.lesson.outcome==="success");t.length>0&&(i.push("**Root causes & failure chain:**"),t.forEach((f,s)=>{const o=f.lesson,e=_[o.severity??"minor"]??"\u{1F7E1}";i.push(`${s===0?" Root:":" \u2192 :"} ${e} \`${o.topic}\``),o.what_failed&&i.push(` \u21B3 ${o.what_failed.slice(0,120)}`)}),i.push("")),n.length>0&&(i.push("**Solutions that worked before:**"),n.forEach((f,s)=>{const o=f.lesson,e=new Date(o.ts).toLocaleDateString("de-DE");i.push(` ${s+1}. \u2705 \`${o.topic}\` \u2014 ${e} \xB7 recalled ${o.recall_count??0}\xD7`),o.what_worked&&i.push(` ${o.what_worked.slice(0,200)}`)}),i.push(""));const r=n[0]?.lesson;if(r?.what_worked){i.push("**\u26A1 Most likely fix:**"),i.push("```"),i.push(r.what_worked.slice(0,500)),i.push("```"),i.push("");const f=j(r)?E(r,"trace"):0;f>0&&l.incrbyfloat(`cachly:stats:time_saved_mins:${u}`,f).catch(()=>{})}}return i.push(`\u{1F4A1} After applying: \`learn_from_attempts(topic="fix:${$[0]??"issue"}", outcome="success", ...)\``),w.length>0&&i.push(`\u{1F578}\uFE0F Explore graph: \`ckg_inspect(concept="${$[0]??"fix"}")\``),i.join(`
5
+ `)}case"knowledge_decay":{let b=function(t){const n=Math.round(t/10);return"\u2588".repeat(n)+"\u2591".repeat(10-n)+` ${t}%`};const{instance_id:u,min_age_days:h=0,show_top:y=20}=S,g=await A(u),l=Date.now(),$=h*864e5;let _=0;const m=[];do{const[t,n]=await g.scan(_,"MATCH","cachly:lesson:best:*","COUNT",200);_=parseInt(t),m.push(...n)}while(_!==0);const c=[];for(const t of m){const n=await g.get(t);if(n){const r=v(n,null);if(!r)continue;const f=l-new Date(r.ts).getTime();if(f<$)continue;const s=Math.floor(f/864e5),o=r.recall_count??0,e=Math.max(0,s-7),a=Math.max(5,100-e),d=Math.min(50,o*5),T=r.outcome==="failure"?-20:0,D=Math.min(100,Math.max(0,a+d+T));c.push({topic:r.topic,confidence:D,age_days:s,recalls:o,outcome:r.outcome})}}c.sort((t,n)=>t.confidence-n.confidence);const w=c.slice(0,y),x=c.length>0?Math.round(c.reduce((t,n)=>t+n.confidence,0)/c.length):0,k=c.filter(t=>t.confidence<30).length,p=c.filter(t=>t.confidence>=70).length;return[`\u{1F9EA} **Knowledge Decay Report** \u2014 ${c.length} lessons`,"",`Average confidence: **${b(x)}**`,`\u{1F534} Critical (<30%): ${k} \u{1F7E2} Healthy (\u226570%): ${p}`,"","Lowest confidence lessons (fix these first):","",...w.map((t,n)=>`${(n+1).toString().padStart(2)}. \`${t.topic}\`
6
+ ${b(t.confidence)} \xB7 ${t.age_days}d old \xB7 ${t.recalls} recalls \xB7 ${t.outcome}`),"",c.length>y?`\u2026 and ${c.length-y} more lessons`:"","",k>0?`\u26A0\uFE0F ${k} lessons are critically stale. Validate them and re-run \`learn_from_attempts\` with updated findings.`:"\u2705 Brain confidence is healthy overall.","","\u{1F4A1} Run `memory_consolidate` to prune stale entries \xB7 `causal_trace` to find root causes."].filter(t=>t!=="").join(`
7
+ `)}case"autopilot":{const{instance_id:u,editor:h="claude",project_name:y="this project",style:g="full"}=S,l=`## Cachly AI Brain \u2014 Autopilot
8
8
 
9
9
  **Instance:** \`${u}\`
10
10
 
@@ -20,7 +20,7 @@ import{ckgSlug as R}from"../ckg.js";import{safeJsonParse as v}from"../utils.js";
20
20
  - **Window opens** \u2192 call \`session_start(instance_id="${u}", focus="<today's goal>")\`
21
21
  - **Bug fixed / deployed / discovered** \u2192 call \`learn_from_attempts(instance_id="${u}", topic="fix:<area>", outcome="success", what_worked="...")\`
22
22
  - **Window closes** \u2192 call \`session_end(instance_id="${u}", summary="...", files_changed=[...])\`
23
- `,y=`## Cachly AI Brain \u2014 Autopilot for ${w}
23
+ `,$=`## Cachly AI Brain \u2014 Autopilot for ${y}
24
24
 
25
25
  **Instance:** \`${u}\`
26
26
 
@@ -99,5 +99,5 @@ smart_recall(
99
99
  ---
100
100
 
101
101
  *Cachly v0.6 \xB7 Generated ${new Date().toISOString().slice(0,10)}*
102
- `,$=m==="minimal"?l:y,p=h==="copilot"?".github/copilot-instructions.md":h==="continue"?".continue/cachly-autopilot.md":"CLAUDE.md";return[`\u{1F916} **Autopilot instructions generated** for **${h==="all"?"all editors":h}**`,"",`**File to create:** \`${p}\``,"","```markdown",$,"```","","**How to apply:**","```bash","# Copy to your project root:",`cat > ${p} << 'EOF'`,$,"EOF","```","",`\u2728 Once this file is in place, **${h==="copilot"?"GitHub Copilot":h==="continue"?"Continue.dev":"Claude/Cursor/Windsurf"}** will manage the Brain automatically \u2014 no manual calls needed, ever.`].join(`
103
- `)}default:return null}}export{F as ADVANCED_TOOL_NAMES,U as handleAdvancedTool};
102
+ `,_=g==="minimal"?l:$,m=h==="copilot"?".github/copilot-instructions.md":h==="continue"?".continue/cachly-autopilot.md":"CLAUDE.md";return[`\u{1F916} **Autopilot instructions generated** for **${h==="all"?"all editors":h}**`,"",`**File to create:** \`${m}\``,"","```markdown",_,"```","","**How to apply:**","```bash","# Copy to your project root:",`cat > ${m} << 'EOF'`,_,"EOF","```","",`\u2728 Once this file is in place, **${h==="copilot"?"GitHub Copilot":h==="continue"?"Continue.dev":"Claude/Cursor/Windsurf"}** will manage the Brain automatically \u2014 no manual calls needed, ever.`].join(`
103
+ `)}default:return null}}export{H as ADVANCED_TOOL_NAMES,W as handleAdvancedTool};