@cachly-dev/mcp-server 0.10.227 → 0.10.229

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.
@@ -1,7 +1,7 @@
1
1
  import{existsSync as H,readFileSync as R}from"node:fs";import{readFile as j,writeFile as T,chmod as L,mkdir as x}from"node:fs/promises";import{dirname as G,resolve as l}from"node:path";import{fileURLToPath as rt}from"node:url";import{CURSOR_FLAG as nt}from"./cursor-hook.js";import{COPILOT_FLAG as st,COPILOT_HOOK_EVENTS as it}from"./copilot-hook.js";import{CODEX_FLAG as ct,CODEX_HOOK_EVENTS as at}from"./codex-hook.js";import{GEMINI_FLAG as ut,GEMINI_HOOK_EVENTS as mt}from"./gemini-hook.js";import{istCachlyEintrag as M}from"./hook-zugang.js";const v="v5",qt="ambient-recall",S="cachly-ambient-einblendung.mjs";function U(t){return t.replace(/\\/g,"\\\\").replace(/'/g,"\\'")}function C(t,o){return["#!/usr/bin/env node",`// cachly Ambient Recall \u2014 ${t} ${v}`,"// Pushes relevant memory into context automatically. Cross-platform Node hook","// (no shell script \u2014 runs identically on Windows/macOS/Linux). Never blocks","// the agent: every failure path exits 0 with no output (graceful degrade).",`// Runs the hook bundle next to this file (${S}) in this process.`,`process.env.CACHLY_BRAIN_INSTANCE_ID = '${U(o.instanceId)}';`,`process.env.CACHLY_HOOK_EVENT = '${U(t)}';`,`process.argv.splice(2, process.argv.length, '${U(t)}');`,"try {",` await import('./${S}');`,"} catch {"," process.exit(0);","}"].join(`
2
2
  `)}function K(){const t=G(rt(import.meta.url));for(const o of[l(t,"..",$,S),l(t,"..","..",$,S)])if(H(o))return o;return null}function pt(t){return C("SessionStart",t)}function lt(t){return C("UserPromptSubmit",t)}function St(t){return C("PreToolUse",t)}function ft(t){return C("Stop",t)}const ht={SessionStart:30,UserPromptSubmit:10,PreToolUse:10,Stop:60},dt="Edit|Write|MultiEdit|NotebookEdit";function J(t,o){const e=(r,i,m)=>({...m?{matcher:m}:{},hooks:[{type:"command",command:`node "${i}"${o?` ${o(r)}`:""}`,timeout:ht[r]}]}),s={SessionStart:[e("SessionStart",t.sessionStart)],UserPromptSubmit:[e("UserPromptSubmit",t.userPromptSubmit)]};return t.preToolUse&&(s.PreToolUse=[e("PreToolUse",t.preToolUse,dt)]),t.stop&&(s.Stop=[e("Stop",t.stop)]),s}const B=".claude/hooks",bt=".claude/settings.json",F={SessionStart:"cachly-ambient-session-start.mjs",UserPromptSubmit:"cachly-ambient-prompt-submit.mjs",PreToolUse:"cachly-ambient-pre-tool.mjs",Stop:"cachly-ambient-stop.mjs"},gt=".claude/hooks/cachly-ambient-";function kt(t,o){const e=t.hooks??[];return e.length>0&&e.every(s=>{const r=s.command??"";return r.replace(/\\/g,"/").includes(gt)||o.has(r)||[...o].some(i=>r.includes(`"${i}"`))})}function Ot(t,o){const e={...t,hooks:{...t.hooks??{}}},s=e.hooks,r=J(o),i=new Set([o.sessionStart,o.userPromptSubmit,o.preToolUse,o.stop].filter(c=>!!c));for(const[c,n]of Object.entries(r)){const u=(Array.isArray(s[c])?s[c]:[]).filter(f=>!kt(f,i));s[c]=[...u,...n]}const m=JSON.stringify(e)!==JSON.stringify(t);return{settings:e,changed:m}}async function to(t,o,e,s={}){const r=l(t,B),i={instanceId:o,apiKey:e},m=["SessionStart","UserPromptSubmit","PreToolUse","Stop"],c={SessionStart:pt(i),UserPromptSubmit:lt(i),PreToolUse:St(i),Stop:ft(i)},n=a=>l(r,F[a]),u=K();if(!u)throw new Error(`cachly hook bundle ${S} missing from the package`);const f=R(u,"utf-8"),d=l(r,S),O=async a=>{try{return await j(a,"utf-8")}catch{return""}},E=l(t,bt),_=H(E);let b={};if(_){const a=await O(E);try{const g=a.charCodeAt(0)===65279?a.slice(1):a,P=g.trim()?JSON.parse(g):{};if(!w(P))throw new k("is not a JSON object");if(P.hooks!==void 0&&!w(P.hooks))throw new k('has a "hooks" field that is not an object');b=P}catch(g){const P=g instanceof k?g.message:/^\s*\/\/|\/\*/m.test(a)?"has comments, and cachly only rewrites plain JSON":"is not valid JSON";return{sessionStartPath:n("SessionStart"),promptSubmitPath:n("UserPromptSubmit"),preToolUsePath:n("PreToolUse"),stopPath:n("Stop"),bundlePath:d,scripts:"unchanged",settings:"skipped",grund:P}}}let p=!1,h=await O(d)===f;for(const a of m){const g=await O(n(a));g&&(p=!0),g!==c[a]+`
3
3
  `&&(h=!1)}const D=h?"unchanged":p?"upgraded":"written";if(D!=="unchanged"){await x(r,{recursive:!0}),await T(d,f,"utf-8");for(const a of m)await T(n(a),c[a]+`
4
- `,"utf-8"),await L(n(a),493).catch(()=>{})}const I=a=>s.portable?`$CLAUDE_PROJECT_DIR/${B}/${F[a]}`:n(a),{settings:ot,changed:et}=Ot(b,{sessionStart:I("SessionStart"),userPromptSubmit:I("UserPromptSubmit"),preToolUse:I("PreToolUse"),stop:I("Stop")});let y;return _?et?y="merged":y="unchanged":y="written",y!=="unchanged"&&(await x(l(t,".claude"),{recursive:!0}),await T(E,JSON.stringify(ot,null,2)+`
4
+ `,"utf-8"),await L(n(a),493).catch(()=>{})}const I=a=>s.portable!==!1?`\${CLAUDE_PROJECT_DIR}/${B}/${F[a]}`:n(a),{settings:ot,changed:et}=Ot(b,{sessionStart:I("SessionStart"),userPromptSubmit:I("UserPromptSubmit"),preToolUse:I("PreToolUse"),stop:I("Stop")});let y;return _?et?y="merged":y="unchanged":y="written",y!=="unchanged"&&(await x(l(t,".claude"),{recursive:!0}),await T(E,JSON.stringify(ot,null,2)+`
5
5
  `,"utf-8")),{sessionStartPath:n("SessionStart"),promptSubmitPath:n("UserPromptSubmit"),preToolUsePath:n("PreToolUse"),stopPath:n("Stop"),bundlePath:d,scripts:D,settings:y}}const V=".cursor/hooks",Pt=".cursor/hooks.json",X="cachly-ambient-cursor.mjs",Tt={sessionStart:30,beforeSubmitPrompt:10,postToolUse:5};function N(t,o,e,s){return["#!/usr/bin/env node",`// cachly Ambient Recall \u2014 ${t} ${v}`,`// Einblendung fuer ${t}: ${e.join(", ")}.`,"// Blockiert nie: jeder Fehler endet mit Exit 0. Ruft das Buendel daneben",`// (${S}) in diesem Prozess.`,`process.env.CACHLY_BRAIN_INSTANCE_ID = '${U(s.instanceId)}';`,`process.argv.push('${o}');`,"try {",` await import('./${S}');`,"} catch {"," process.exit(0);","}"].join(`
6
6
  `)}function _t(t){return N("Cursor",nt,["sessionStart","beforeSubmitPrompt","postToolUse"],t)}function yt(){const t=o=>[{command:`node ${V}/${X} ${o}`,timeout:Tt[o]}];return{sessionStart:t("sessionStart"),beforeSubmitPrompt:t("beforeSubmitPrompt"),postToolUse:t("postToolUse")}}function Et(t){return z(t,yt())}function z(t,o){const e={...t.hooks??{}};for(const[r,i]of Object.entries(o)){W(e[r],r);const m=(e[r]??[]).filter(c=>!M(c));e[r]=[...m,...i]}const s={...t,version:typeof t.version=="number"?t.version:1,hooks:e};return{datei:s,changed:JSON.stringify(s)!==JSON.stringify(t)}}function Y(t,o){const e={...t.hooks??{}};for(const[r,i]of Object.entries(o)){W(e[r],r);const m=(e[r]??[]).flatMap(c=>{const n=c?.hooks;if(!Array.isArray(n))return[c];const u=n.filter(f=>!M(f));return u.length===n.length?[c]:u.length?[{...c,hooks:u}]:[]});e[r]=[...m,...i]}const s={...t,hooks:e};return{datei:s,changed:JSON.stringify(s)!==JSON.stringify(t)}}async function A(t){const o=l(t.projectDir,t.hookDir),e=K();if(!e)throw new Error(`cachly hook bundle ${S} missing from the package`);const s=R(e,"utf-8"),r=l(o,S),i=l(o,t.skriptName),m=t.skript+`
7
7
  `,c=async p=>{try{return await j(p,"utf-8")}catch{return""}},n=l(t.projectDir,t.configDatei),u=H(n)?await c(n):"",f=!u.trim();let d;try{const p=u.charCodeAt(0)===65279?u.slice(1):u,h=f?{}:JSON.parse(p);if(!w(h))throw new k("is not a JSON object");if(h.hooks!==void 0&&!w(h.hooks))throw new k('has a "hooks" field that is not an object');d=t.merge(h,i)}catch(p){const h=p instanceof k?p.message:/^\s*\/\/|\/\*/m.test(u)?"has comments, and cachly only rewrites plain JSON":"is not valid JSON";return{scriptPath:i,bundlePath:r,hooksPath:n,scripts:"unchanged",hooks:"skipped",grund:h}}await x(o,{recursive:!0});const O=await c(i),_=O===m&&await c(r)===s?"unchanged":O?"upgraded":"written";_!=="unchanged"&&(await T(r,s,"utf-8"),await T(i,m,"utf-8"),await L(i,493).catch(()=>{}));let b;if(f?b="written":d.changed?b="merged":b="unchanged",b!=="unchanged"){const p=/^([ \t]+)"/m.exec(u)?.[1]??2;await x(G(n),{recursive:!0}),await T(n,JSON.stringify(d.datei,null,p)+`
@@ -1 +1 @@
1
- const s=new Set;let u=!1;function l(){u=!0}function f(e){if(e instanceof Error)return(e.message||e.name||"Error").slice(0,200);if(typeof e=="string")return e.slice(0,200)||"leerer Text geworfen";if(e===void 0)return"undefined geworfen (kein Grund mitgegeben)";if(e===null)return"null geworfen (kein Grund mitgegeben)";try{const n=JSON.stringify(e);return n&&n!=="{}"?n.slice(0,200):`${typeof e} ohne Inhalt geworfen`}catch{return`${typeof e} geworfen, nicht darstellbar`}}function c(e,n){return s.has(e)?!1:(s.add(e),u||console.error(`[aussetzer] ${e}: ${n}`),!0)}function h(e){return s.has(e)}function g(){return[...s]}function E(){s.clear(),u=!1}const m="bedeutung-ohne-vektoren",p="bedeutung-ohne-dienst",x="bedeutung-ohne-fragevektor",d="bedeutung-abbruch",z="einbettung-beim-schreiben",k="versuch-laeuft-leer",a="cachly:aussetzer:",i=720*60*60;async function y(e,n,o){const r=c(n,o);if(!e)return r;try{const t=`${a}${n}`;await e.hincrby(t,"anzahl",1),await e.hset(t,{zuletzt:new Date().toISOString(),text:o.slice(0,500)}),await e.expire(t,i)}catch(t){c(`${n}-vermerk`,`der Aussetzer liess sich nicht vermerken (${t instanceof Error?t.message:String(t)}) \u2014 er steht nur in diesem Protokoll und ist nach einem Neustart weg`)}return r}async function S(e){if(!e)return null;try{const n=await e.keys(`${a}*`),o=[];for(const r of n){const t=await e.hgetall(r);!t||!t.anzahl||o.push({grund:r.slice(a.length),anzahl:Number(t.anzahl)||0,zuletzt:t.zuletzt??"",text:t.text??""})}return o.sort((r,t)=>t.anzahl-r.anzahl||r.grund.localeCompare(t.grund)),o}catch{return null}}export{i as AUSSETZER_TTL_SEKUNDEN,a as AUSSETZER_VORSATZ,p as OHNE_DIENST,x as OHNE_FRAGEVEKTOR,z as OHNE_SCHREIBVEKTOR,m as OHNE_VEKTOREN,d as SINNPFAD_ABBRUCH,k as VERSUCH_LEER,l as aussetzerLeise,f as fehlerText,g as gemeldeteGruende,S as leseVermerke,c as meldeEinmal,y as meldeUndVermerke,h as schonGemeldet,E as setzeAussetzerZurueck};
1
+ const o=new Set;let a=!1;function x(){a=!0}function b(e){if(e instanceof Error)return(e.message||e.name||"Error").slice(0,200);if(typeof e=="string")return e.slice(0,200)||"leerer Text geworfen";if(e===void 0)return"undefined geworfen (kein Grund mitgegeben)";if(e===null)return"null geworfen (kein Grund mitgegeben)";try{const n=JSON.stringify(e);return n&&n!=="{}"?n.slice(0,200):`${typeof e} ohne Inhalt geworfen`}catch{return`${typeof e} geworfen, nicht darstellbar`}}function c(e,n){return o.has(e)?!1:(o.add(e),a||console.error(`[aussetzer] ${e}: ${n}`),!0)}function p(e){return o.has(e)}function E(){return[...o]}function y(){o.clear(),a=!1}const i="bedeutung-ohne-vektoren",l="bedeutung-ohne-dienst",f="bedeutung-ohne-fragevektor",h="bedeutung-abbruch",d="einbettung-beim-schreiben",g="versuch-laeuft-leer",u="cachly:aussetzer:";function z(e){switch(e){case i:return"no stored vectors, so search by meaning could not run";case l:return"embedding service not reachable, so search by meaning could not run";case f:return"the question could not be embedded, so that search used words only";case h:return"search by meaning failed unexpectedly, so that search used words only";case d:return"embedding failed while saving a lesson; the lesson is saved but not findable by meaning";case g:return"an experiment is switched on but has no effect";default:return e}}function w(e){const n=String(e??""),s=n.indexOf("("),r=n.lastIndexOf(")");return s>=0&&r>s+1?n.slice(s+1,r).slice(0,140):""}const m=720*60*60;async function k(e,n,s){const r=c(n,s);if(!e)return r;try{const t=`${u}${n}`;await e.hincrby(t,"anzahl",1),await e.hset(t,{zuletzt:new Date().toISOString(),text:s.slice(0,500)}),await e.expire(t,m)}catch(t){c(`${n}-vermerk`,`der Aussetzer liess sich nicht vermerken (${t instanceof Error?t.message:String(t)}) \u2014 er steht nur in diesem Protokoll und ist nach einem Neustart weg`)}return r}async function S(e){if(!e)return null;try{const n=await e.keys(`${u}*`),s=[];for(const r of n){const t=await e.hgetall(r);!t||!t.anzahl||s.push({grund:r.slice(u.length),anzahl:Number(t.anzahl)||0,zuletzt:t.zuletzt??"",text:t.text??""})}return s.sort((r,t)=>t.anzahl-r.anzahl||r.grund.localeCompare(t.grund)),s}catch{return null}}export{m as AUSSETZER_TTL_SEKUNDEN,u as AUSSETZER_VORSATZ,l as OHNE_DIENST,f as OHNE_FRAGEVEKTOR,d as OHNE_SCHREIBVEKTOR,i as OHNE_VEKTOREN,h as SINNPFAD_ABBRUCH,g as VERSUCH_LEER,w as aussetzerEinzelheit,z as aussetzerErklaerung,x as aussetzerLeise,b as fehlerText,E as gemeldeteGruende,S as leseVermerke,c as meldeEinmal,k as meldeUndVermerke,p as schonGemeldet,y as setzeAussetzerZurueck};
@@ -1 +1 @@
1
- import{existsSync as g,readFileSync as l}from"node:fs";import{dirname as f,join as p}from"node:path";import{fileURLToPath as d}from"node:url";const o={version:"unbekannt",commit:"unbekannt",gebautAm:"unbekannt"};function k(t){if(typeof t!="object"||t===null)return!1;const e=t;return typeof e.version=="string"&&typeof e.commit=="string"&&typeof e.gebautAm=="string"}function A(t=f(d(import.meta.url))){const e=p(t,"bauabdruck-daten.generiert.json");if(!g(e))return o;try{const n=JSON.parse(l(e,"utf-8"));return k(n)?n:o}catch{return o}}const y=A(),i=14;function T(t,e=new Date){const n=new Date(t);return Number.isNaN(n.getTime())?!1:(e.getTime()-n.getTime())/864e5>i}function B(t,e=new Date){if(t.gebautAm==="unbekannt")return"\u2754 **Bauabdruck: unbekannt** \u2014 noch kein `npm run build` gelaufen, Version/Commit nicht feststellbar";const n=new Date(t.gebautAm),r=!Number.isNaN(n.getTime()),u=r&&T(t.gebautAm,e),a=r?Math.floor((e.getTime()-n.getTime())/864e5):null,m=a===null?"":` (vor ${a} Tag${a===1?"":"en"})`,s=u?"\u{1F7E1}":"\u2705",c=u?` \u2014 laeuft laenger als ${i} Tage. Kein Fehler, aber sichtbar.`:"",b=r?t.gebautAm.slice(0,10):t.gebautAm;return`${s} **Bauabdruck:** v${t.version} \xB7 commit \`${t.commit}\` \xB7 gebaut am ${b}${m}${c}`}export{i as ALTER_SCHWELLE_TAGE,o as RUECKFALL_BAUABDRUCK,y as bauabdruck,B as formatiereBauabdruck,T as istAlt,A as ladeBauabdruck};
1
+ import{existsSync as g,readFileSync as f}from"node:fs";import{dirname as l,join as d}from"node:path";import{fileURLToPath as p}from"node:url";const u={version:"unbekannt",commit:"unbekannt",gebautAm:"unbekannt"};function A(t){if(typeof t!="object"||t===null)return!1;const e=t;return typeof e.version=="string"&&typeof e.commit=="string"&&typeof e.gebautAm=="string"}function k(t=l(p(import.meta.url))){const e=d(t,"bauabdruck-daten.generiert.json");if(!g(e))return u;try{const n=JSON.parse(f(e,"utf-8"));return A(n)?n:u}catch{return u}}const w=k(),a=14;function T(t,e=new Date){const n=new Date(t);return Number.isNaN(n.getTime())?!1:(e.getTime()-n.getTime())/864e5>a}function N(t,e=new Date){if(t.gebautAm==="unbekannt")return"\u2754 **Build: unknown** \u2014 no `npm run build` has run, so version and commit cannot be told";const n=new Date(t.gebautAm),o=!Number.isNaN(n.getTime()),i=o&&T(t.gebautAm,e),r=o?Math.floor((e.getTime()-n.getTime())/864e5):null,m=r===null?"":` (${r} day${r===1?"":"s"} ago)`,s=i?"\u{1F7E1}":"\u2705",c=i?` \u2014 running for more than ${a} days. Not an error, but worth knowing.`:"",b=o?t.gebautAm.slice(0,10):t.gebautAm;return`${s} **Build:** v${t.version} \xB7 commit \`${t.commit}\` \xB7 built ${b}${m}${c}`}export{a as ALTER_SCHWELLE_TAGE,u as RUECKFALL_BAUABDRUCK,w as bauabdruck,N as formatiereBauabdruck,T as istAlt,k as ladeBauabdruck};
@@ -1,5 +1,5 @@
1
- import{existsSync as c}from"node:fs";import{readFile as u,writeFile as f}from"node:fs/promises";import{resolve as a}from"node:path";import{installClsPostCommitHook as h}from"./cls-hook.js";import{instanzIdFuerSkript as m}from"./git-ohne-shell.js";const s=/(?:#!\/bin\/sh\n)?# cachly brain_watch[\s\S]*?\nexit 0\n?/;function p(r){if(!s.test(r))return null;let t=r;for(let n=s.exec(t);n;n=s.exec(t)){const o=t.slice(0,n.index).replace(/\s+$/,""),i=t.slice(n.index+n[0].length).replace(/^\s*\n/,"");t=o+(o&&i.trim()?`
1
+ import{existsSync as c}from"node:fs";import{readFile as u,writeFile as f}from"node:fs/promises";import{resolve as a}from"node:path";import{gitHookOrdner as m,installClsPostCommitHook as h}from"./cls-hook.js";import{instanzIdFuerSkript as p}from"./git-ohne-shell.js";const l=/(?:#!\/bin\/sh\n)?# cachly brain_watch[\s\S]*?\nexit 0\n?/;function d(r){if(!l.test(r))return null;let t=r;for(let n=l.exec(t);n;n=l.exec(t)){const o=t.slice(0,n.index).replace(/\s+$/,""),i=t.slice(n.index+n[0].length).replace(/^\s*\n/,"");t=o+(o&&i.trim()?`
2
2
 
3
3
  `:"")+i}const e=t.trim();return e===""||e.startsWith("#!")&&!e.includes(`
4
4
  `)?"":t.replace(/\s+$/,"")+`
5
- `}async function w(r,t){if(m(t),!c(a(r,".git")))return{result:"skipped-no-git",hookPath:"",alterBlockEntfernt:!1};const e=a(r,".git","hooks","post-commit");let n="";try{n=await u(e,"utf-8")}catch{}const o=p(n);o!==null&&await f(e,o,"utf-8");const i=await h(r,t),l=o!==null;return{result:l&&i==="unchanged"?"upgraded":i,hookPath:e,alterBlockEntfernt:l}}export{s as BRAIN_WATCH_BLOCK_RE,w as installBrainWatchHook,p as ohneBrainWatchBlock};
5
+ `}async function y(r,t){if(p(t),!c(a(r,".git")))return{result:"skipped-no-git",hookPath:"",alterBlockEntfernt:!1};const e=a(m(r),"post-commit");let n="";try{n=await u(e,"utf-8")}catch{}const o=d(n);o!==null&&await f(e,o,"utf-8");const i=await h(r,t),s=o!==null;return{result:s&&i==="unchanged"?"upgraded":i,hookPath:e,alterBlockEntfernt:s}}export{l as BRAIN_WATCH_BLOCK_RE,y as installBrainWatchHook,d as ohneBrainWatchBlock};
@@ -1,8 +1,8 @@
1
- import{existsSync as d}from"node:fs";import{readFile as S,writeFile as r,chmod as s,mkdir as m}from"node:fs/promises";import{resolve as c}from"node:path";import{instanzIdFuerSkript as f}from"./git-ohne-shell.js";const l="v4",u=/(?:#!\/bin\/sh\n)?# cachly CLS[\s\S]*?\nexit 0\n?/;async function g(t,a,h){if(!d(c(t,".git")))return"skipped-no-git";const p=c(t,".git","hooks"),e=c(p,"post-commit");await m(p,{recursive:!0});const n=_(a);let i="";try{i=await S(e,"utf-8")}catch{}if(!i)return await r(e,n+`
2
- `,"utf-8"),await s(e,493).catch(()=>{}),"written";if(i.includes(`cachly CLS \u2014 Continuous Learning Stream ${l}`))return"unchanged";if(u.test(i)){const o=i.replace(u,n);return await r(e,o.endsWith(`
3
- `)?o:o+`
4
- `,"utf-8"),await s(e,493).catch(()=>{}),"upgraded"}return await r(e,i.trimEnd()+`
1
+ import{existsSync as f,statSync as h}from"node:fs";import{readFile as _,writeFile as p,chmod as u,mkdir as C}from"node:fs/promises";import{basename as l,dirname as d,resolve as o}from"node:path";import{gitLesen as y,instanzIdFuerSkript as L}from"./git-ohne-shell.js";const m="v4",S=/(?:#!\/bin\/sh\n)?# cachly CLS[\s\S]*?\nexit 0\n?/;function v(e){const t=o(e,".git","hooks");try{if(!h(o(e,".git")).isDirectory())return t}catch{return t}let s="";try{s=y(e,["rev-parse","--git-path","hooks"]).trim()}catch{return t}if(!s)return t;const n=o(e,s);return l(n)==="_"&&l(d(n))===".husky"?d(n):n}async function H(e,t,s){if(!f(o(e,".git")))return"skipped-no-git";const n=v(e),r=o(n,"post-commit");await C(n,{recursive:!0});const a=x(t);let i="";try{i=await _(r,"utf-8")}catch{}if(!i)return await p(r,a+`
2
+ `,"utf-8"),await u(r,493).catch(()=>{}),"written";if(i.includes(`cachly CLS \u2014 Continuous Learning Stream ${m}`))return"unchanged";if(S.test(i)){const c=i.replace(S,a);return await p(r,c.endsWith(`
3
+ `)?c:c+`
4
+ `,"utf-8"),await u(r,493).catch(()=>{}),"upgraded"}return await p(r,i.trimEnd()+`
5
5
 
6
- `+n+`
7
- `,"utf-8"),await s(e,493).catch(()=>{}),"appended"}const C="try{var cp=require('child_process'),pa=require('path'),fs=require('fs');var p={instance_id:process.env.CACHLY_BRAIN_INSTANCE_ID,source:'git_commit',payload:{message:process.env.CLS_MSG||'',sha:process.env.CLS_SHA||'',files:(process.env.CLS_FILES||'').split(',').filter(Boolean)}};var a=['-y','@cachly-dev/mcp-server@latest','cls-ingest',JSON.stringify(p)];var o={stdio:'ignore',timeout:30000};var d=pa.dirname(process.execPath);var c=[pa.join(d,'node_modules','npm','bin','npx-cli.js'),pa.join(d,'..','lib','node_modules','npm','bin','npx-cli.js')].filter(function(f){return fs.existsSync(f)})[0];if(c){cp.execFileSync(process.execPath,[c].concat(a),o)}else if(process.platform!=='win32'){cp.execFileSync('npx',a,o)}}catch(e){}";function _(t,a){return t=f(t),["#!/bin/sh",`# cachly CLS \u2014 Continuous Learning Stream ${l}`,"# Runs silently on every commit to keep your brain up to date.",`export CACHLY_BRAIN_INSTANCE_ID="${t}"`,'CLS_SHA=$(git rev-parse HEAD 2>/dev/null || echo "")',"CLS_MSG=$(git log -1 --pretty=%B 2>/dev/null | head -1 | cut -c1-200)","CLS_FILES=$(git diff-tree --no-commit-id -r --name-only HEAD 2>/dev/null | tr '\\n' ',' | sed 's/,$//')","export CLS_SHA CLS_MSG CLS_FILES",`node -e "${C}" 2>/dev/null &`,"exit 0"].join(`
8
- `)}export{u as CLS_BLOCK_RE,l as CLS_HOOK_VERSION,C as CLS_NODE_PROGRAM,_ as buildClsPostCommitHook,g as installClsPostCommitHook};
6
+ `+a+`
7
+ `,"utf-8"),await u(r,493).catch(()=>{}),"appended"}const g="try{var cp=require('child_process'),pa=require('path'),fs=require('fs');var p={instance_id:process.env.CACHLY_BRAIN_INSTANCE_ID,source:'git_commit',payload:{message:process.env.CLS_MSG||'',sha:process.env.CLS_SHA||'',files:(process.env.CLS_FILES||'').split(',').filter(Boolean)}};var a=['-y','@cachly-dev/mcp-server@latest','cls-ingest',JSON.stringify(p)];var o={stdio:'ignore',timeout:30000};var d=pa.dirname(process.execPath);var c=[pa.join(d,'node_modules','npm','bin','npx-cli.js'),pa.join(d,'..','lib','node_modules','npm','bin','npx-cli.js')].filter(function(f){return fs.existsSync(f)})[0];if(c){cp.execFileSync(process.execPath,[c].concat(a),o)}else if(process.platform!=='win32'){cp.execFileSync('npx',a,o)}}catch(e){}";function x(e,t){return e=L(e),["#!/bin/sh",`# cachly CLS \u2014 Continuous Learning Stream ${m}`,"# Runs silently on every commit to keep your brain up to date.",`export CACHLY_BRAIN_INSTANCE_ID="${e}"`,'CLS_SHA=$(git rev-parse HEAD 2>/dev/null || echo "")',"CLS_MSG=$(git log -1 --pretty=%B 2>/dev/null | head -1 | cut -c1-200)","CLS_FILES=$(git diff-tree --no-commit-id -r --name-only HEAD 2>/dev/null | tr '\\n' ',' | sed 's/,$//')","export CLS_SHA CLS_MSG CLS_FILES",`node -e "${g}" 2>/dev/null &`,"exit 0"].join(`
8
+ `)}export{S as CLS_BLOCK_RE,m as CLS_HOOK_VERSION,g as CLS_NODE_PROGRAM,x as buildClsPostCommitHook,v as gitHookOrdner,H as installClsPostCommitHook};
@@ -1,2 +1,2 @@
1
- import{existsSync as a,readFileSync as u}from"node:fs";import{resolve as d}from"node:path";import{AMBIENT_HOOK_VERSION as h}from"./ambient-hooks.js";import{netBalance as y}from"./ambient-recall.js";import{cachlyUrl as b}from"./cachly-url.js";const g=18;function N(e=process.version){const t=Number(e.replace(/^v/,"").split(".")[0]);return Number.isFinite(t)&&t>=g?{name:"Node.js",status:"ok",detail:`${e}`}:{name:"Node.js",status:"fail",detail:`${e} \u2014 too old`,hint:`cachly needs Node ${g}+ (built-in fetch). Install a current LTS from nodejs.org.`}}function R(e){if(!e)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 t=e.startsWith("cky_"),n=e.split(".").length===3;return!t&&!n?{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:t?"API key (cky_\u2026)":"JWT"}}async function P(e,t=fetch){try{const n=await t(`${e}/health`,{signal:AbortSignal.timeout(5e3)});return n.ok?{name:"API",status:"ok",detail:`${e} reachable`}:{name:"API",status:"fail",detail:`${e}/health returned HTTP ${n.status}`,hint:`The API answered but is unhealthy \u2014 check ${b("/status","doctor")} or your CACHLY_API_URL.`}}catch{return{name:"API",status:"fail",detail:`${e} not reachable`,hint:"Check your network/proxy. Self-hosted: is CACHLY_API_URL correct and the API running?"}}}async function j(e,t,n=fetch){if(!t)return{name:"Auth",status:"fail",detail:"skipped \u2014 no credential",hint:"Fix the Credential check first."};try{const o=await n(`${e}/api/v1/instances`,{headers:{Authorization:`Bearer ${t}`},signal:AbortSignal.timeout(5e3)});return o.ok?{name:"Auth",status:"ok",detail:"credential accepted"}:o.status===401||o.status===403?{name:"Auth",status:"fail",detail:`API rejected the credential (HTTP ${o.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 ${o.status} from /api/v1/instances`}}catch{return{name:"Auth",status:"warn",detail:"could not verify (network error)"}}}async function S(e,t,n,o=fetch){if(!t||!n)return{name:"Vektoren",status:"warn",detail:"skipped \u2014 no credential or instance"};try{const s=await o(`${e}/api/v1/instances/${n}/vector-coverage`,{headers:{Authorization:`Bearer ${t}`},signal:AbortSignal.timeout(12e3)});if(!s.ok)return{name:"Vektoren",status:"warn",detail:`coverage not measured (HTTP ${s.status})`};const r=await s.json();return r.status==="leer"?{name:"Vektoren",status:"ok",detail:"no lessons yet"}:r.status==="nicht-gemessen"?{name:"Vektoren",status:"warn",detail:"instance not running \u2014 not measured"}:r.status==="aus"?{name:"Vektoren",status:"fail",detail:`semantic search is OFF: ${r.lessons} lessons, 0 vectors`,hint:"Embeddings never ran for this instance. Check the embed provider; recall runs word-only until fixed."}:r.status==="luecke"?{name:"Vektoren",status:"warn",detail:`${r.vectors}/${r.lessons} lessons have vectors (${r.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:`${r.vectors}/${r.lessons} vectors (${r.percent.toFixed(0)}%)`}}catch{return{name:"Vektoren",status:"warn",detail:"could not verify (network error)"}}}function L(e){return e?/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(e)?{name:"Instance",status:"ok",detail:e.slice(0,8)+"\u2026"}:{name:"Instance",status:"warn",detail:`"${e.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."}}const v=[{editor:"cursor",konfig:".cursor/hooks.json",buendel:".cursor/hooks/cachly-ambient-einblendung.mjs"},{editor:"copilot",konfig:".github/hooks/cachly-ambient.json",buendel:".github/hooks/cachly-ambient-einblendung.mjs"},{editor:"codex",konfig:".codex/hooks.json",buendel:".codex/hooks/cachly-ambient-einblendung.mjs"},{editor:"gemini",konfig:".gemini/settings.json",buendel:".gemini/hooks/cachly-ambient-einblendung.mjs"}];function O(e){const t={settingsExists:!1,wiredEvents:[],scriptFiles:[],editorHooks:[]};for(const n of v)try{const o=d(e,n.konfig);a(o)&&u(o,"utf-8").includes("cachly-ambient")&&a(d(e,n.buendel))&&t.editorHooks.push(n.editor)}catch{}try{const n=d(e,".claude/settings.json");if(t.settingsExists=a(n),t.settingsExists){const o=JSON.parse(u(n,"utf-8"));for(const[s,r]of Object.entries(o.hooks??{})){const m=i=>(i??"").replace(/\\/g,"/");if((r??[]).some(i=>(i.hooks??[]).some(c=>m(c.command).includes(".claude/hooks/cachly-ambient-")))){t.wiredEvents.push(s);for(const i of r??[])for(const c of i.hooks??[]){const f=m(c.command).match(/["']?([^"']*\.claude\/hooks\/cachly-ambient-[^"']+)["']?/);if(!f)continue;const l=f[1].replace("$CLAUDE_PROJECT_DIR",e),k=a(l);let p="unknown";if(k)try{p=u(l,"utf-8").includes(h)?"current":"stale"}catch{}t.scriptFiles.push({path:l,exists:k,version:p})}}}}}catch{}return t}const $=["SessionStart","UserPromptSubmit","PreToolUse","Stop"];function F(e){if(!e.settingsExists||e.wiredEvents.length===0){const s=e.editorHooks??[];return s.length>0?{name:"Hooks",status:"ok",detail:`Ambient Recall hooks wired for ${s.join(", ")}`}:{name:"Hooks",status:"warn",detail:"Ambient Recall hooks are not installed in this project",hint:"Run `npx @cachly-dev/mcp-server@latest init` here (add `--editor cursor|copilot|codex|gemini` if you do not use Claude Code) \u2014 memory then injects itself (no hooks = pull-only)."}}const t=$.filter(s=>!e.wiredEvents.includes(s)),n=e.scriptFiles.filter(s=>!s.exists),o=e.scriptFiles.filter(s=>s.exists&&s.version==="stale");return n.length>0?{name:"Hooks",status:"fail",detail:`settings.json wires hooks whose script files are missing (${n.length})`,hint:"Re-run `npx @cachly-dev/mcp-server@latest init` to rewrite the scripts."}:o.length>0?{name:"Hooks",status:"warn",detail:`installed hooks are an older version (current: ${h}); v1/v2 shell hooks never ran on native Windows`,hint:"Re-run `npx @cachly-dev/mcp-server@latest init` to upgrade in place."}:t.length>0?{name:"Hooks",status:"warn",detail:`partial install \u2014 missing events: ${t.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 ${h}`}}function V(e,t){const n=y(e);return e.length===0?{name:"Ledger",status:"ok",detail:`empty (${t}) \u2014 fills up once ambient hooks inject`}:{name:"Ledger",status:"ok",detail:`${e.length} entries, net ${n.net>=0?"+":""}${n.net} tokens`}}const A={ok:"\u2705",warn:"\u26A0\uFE0F ",fail:"\u274C"};function J(e){const t=["","\u{1FA7A} cachly doctor",""];for(const s of e)t.push(` ${A[s.status]} ${s.name.padEnd(11)} ${s.detail}`),s.hint&&s.status!=="ok"&&t.push(` \u21B3 ${s.hint}`);const n=e.filter(s=>s.status==="fail").length,o=e.filter(s=>s.status==="warn").length;return t.push(""),t.push(n>0?` ${n} problem${n>1?"s":""} to fix${o?`, ${o} warning${o>1?"s":""}`:""}.`:o>0?` No blockers \u2014 ${o} warning${o>1?"s":""}.`:" Everything looks healthy. Your brain is wired."),t.push(""),t.join(`
2
- `)}function Y(e){return e.some(t=>t.status==="fail")?1:0}export{P as checkApiReachable,j as checkAuthAccepted,R as checkCredential,F as checkHooks,L as checkInstance,V as checkLedger,N as checkNodeVersion,S as checkVectorCoverage,Y as doctorExitCode,O as inspectAmbientHooks,J as renderDoctorReport};
1
+ import{existsSync as a,readFileSync as u}from"node:fs";import{resolve as d}from"node:path";import{AMBIENT_HOOK_VERSION as h}from"./ambient-hooks.js";import{netBalance as y}from"./ambient-recall.js";import{cachlyUrl as b}from"./cachly-url.js";const g=18;function N(e=process.version){const t=Number(e.replace(/^v/,"").split(".")[0]);return Number.isFinite(t)&&t>=g?{name:"Node.js",status:"ok",detail:`${e}`}:{name:"Node.js",status:"fail",detail:`${e} \u2014 too old`,hint:`cachly needs Node ${g}+ (built-in fetch). Install a current LTS from nodejs.org.`}}function R(e){if(!e)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 t=e.startsWith("cky_"),n=e.split(".").length===3;return!t&&!n?{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:t?"API key (cky_\u2026)":"JWT"}}async function P(e,t=fetch){try{const n=await t(`${e}/health`,{signal:AbortSignal.timeout(5e3)});return n.ok?{name:"API",status:"ok",detail:`${e} reachable`}:{name:"API",status:"fail",detail:`${e}/health returned HTTP ${n.status}`,hint:`The API answered but is unhealthy \u2014 check ${b("/status","doctor")} or your CACHLY_API_URL.`}}catch{return{name:"API",status:"fail",detail:`${e} not reachable`,hint:"Check your network/proxy. Self-hosted: is CACHLY_API_URL correct and the API running?"}}}async function j(e,t,n=fetch){if(!t)return{name:"Auth",status:"fail",detail:"skipped \u2014 no credential",hint:"Fix the Credential check first."};try{const o=await n(`${e}/api/v1/instances`,{headers:{Authorization:`Bearer ${t}`},signal:AbortSignal.timeout(5e3)});return o.ok?{name:"Auth",status:"ok",detail:"credential accepted"}:o.status===401||o.status===403?{name:"Auth",status:"fail",detail:`API rejected the credential (HTTP ${o.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 ${o.status} from /api/v1/instances`}}catch{return{name:"Auth",status:"warn",detail:"could not verify (network error)"}}}async function S(e,t,n,o=fetch){if(!t||!n)return{name:"Vektoren",status:"warn",detail:"skipped \u2014 no credential or instance"};try{const s=await o(`${e}/api/v1/instances/${n}/vector-coverage`,{headers:{Authorization:`Bearer ${t}`},signal:AbortSignal.timeout(12e3)});if(!s.ok)return{name:"Vektoren",status:"warn",detail:`coverage not measured (HTTP ${s.status})`};const r=await s.json();return r.status==="leer"?{name:"Vektoren",status:"ok",detail:"no lessons yet"}:r.status==="nicht-gemessen"?{name:"Vektoren",status:"warn",detail:"instance not running \u2014 not measured"}:r.status==="aus"?{name:"Vektoren",status:"fail",detail:`semantic search is OFF: ${r.lessons} lessons, 0 vectors`,hint:"Embeddings never ran for this instance. Check the embed provider; recall runs word-only until fixed."}:r.status==="luecke"?{name:"Vektoren",status:"warn",detail:`${r.vectors}/${r.lessons} lessons have vectors (${r.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:`${r.vectors}/${r.lessons} vectors (${r.percent.toFixed(0)}%)`}}catch{return{name:"Vektoren",status:"warn",detail:"could not verify (network error)"}}}function L(e){return e?/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(e)?{name:"Instance",status:"ok",detail:e.slice(0,8)+"\u2026"}:{name:"Instance",status:"warn",detail:`"${e.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."}}const v=[{editor:"cursor",konfig:".cursor/hooks.json",buendel:".cursor/hooks/cachly-ambient-einblendung.mjs"},{editor:"copilot",konfig:".github/hooks/cachly-ambient.json",buendel:".github/hooks/cachly-ambient-einblendung.mjs"},{editor:"codex",konfig:".codex/hooks.json",buendel:".codex/hooks/cachly-ambient-einblendung.mjs"},{editor:"gemini",konfig:".gemini/settings.json",buendel:".gemini/hooks/cachly-ambient-einblendung.mjs"}];function O(e){const t={settingsExists:!1,wiredEvents:[],scriptFiles:[],editorHooks:[]};for(const n of v)try{const o=d(e,n.konfig);a(o)&&u(o,"utf-8").includes("cachly-ambient")&&a(d(e,n.buendel))&&t.editorHooks.push(n.editor)}catch{}try{const n=d(e,".claude/settings.json");if(t.settingsExists=a(n),t.settingsExists){const o=JSON.parse(u(n,"utf-8"));for(const[s,r]of Object.entries(o.hooks??{})){const m=i=>(i??"").replace(/\\/g,"/");if((r??[]).some(i=>(i.hooks??[]).some(c=>m(c.command).includes(".claude/hooks/cachly-ambient-")))){t.wiredEvents.push(s);for(const i of r??[])for(const c of i.hooks??[]){const f=m(c.command).match(/["']?([^"']*\.claude\/hooks\/cachly-ambient-[^"']+)["']?/);if(!f)continue;const l=f[1].replace(/\$\{CLAUDE_PROJECT_DIR\}|\$CLAUDE_PROJECT_DIR/,e),k=a(l);let p="unknown";if(k)try{p=u(l,"utf-8").includes(h)?"current":"stale"}catch{}t.scriptFiles.push({path:l,exists:k,version:p})}}}}}catch{}return t}const $=["SessionStart","UserPromptSubmit","PreToolUse","Stop"];function F(e){if(!e.settingsExists||e.wiredEvents.length===0){const s=e.editorHooks??[];return s.length>0?{name:"Hooks",status:"ok",detail:`Ambient Recall hooks wired for ${s.join(", ")}`}:{name:"Hooks",status:"warn",detail:"Ambient Recall hooks are not installed in this project",hint:"Run `npx @cachly-dev/mcp-server@latest init` here (add `--editor cursor|copilot|codex|gemini` if you do not use Claude Code) \u2014 memory then injects itself (no hooks = pull-only)."}}const t=$.filter(s=>!e.wiredEvents.includes(s)),n=e.scriptFiles.filter(s=>!s.exists),o=e.scriptFiles.filter(s=>s.exists&&s.version==="stale");return n.length>0?{name:"Hooks",status:"fail",detail:`settings.json wires hooks whose script files are missing (${n.length})`,hint:"Re-run `npx @cachly-dev/mcp-server@latest init` to rewrite the scripts."}:o.length>0?{name:"Hooks",status:"warn",detail:`installed hooks are an older version (current: ${h}); v1/v2 shell hooks never ran on native Windows`,hint:"Re-run `npx @cachly-dev/mcp-server@latest init` to upgrade in place."}:t.length>0?{name:"Hooks",status:"warn",detail:`partial install \u2014 missing events: ${t.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 ${h}`}}function V(e,t){const n=y(e);return e.length===0?{name:"Ledger",status:"ok",detail:`empty (${t}) \u2014 fills up once ambient hooks inject`}:{name:"Ledger",status:"ok",detail:`${e.length} entries, net ${n.net>=0?"+":""}${n.net} tokens`}}const A={ok:"\u2705",warn:"\u26A0\uFE0F ",fail:"\u274C"};function J(e){const t=["","\u{1FA7A} cachly doctor",""];for(const s of e)t.push(` ${A[s.status]} ${s.name.padEnd(11)} ${s.detail}`),s.hint&&s.status!=="ok"&&t.push(` \u21B3 ${s.hint}`);const n=e.filter(s=>s.status==="fail").length,o=e.filter(s=>s.status==="warn").length;return t.push(""),t.push(n>0?` ${n} problem${n>1?"s":""} to fix${o?`, ${o} warning${o>1?"s":""}`:""}.`:o>0?` No blockers \u2014 ${o} warning${o>1?"s":""}.`:" Everything looks healthy. Your brain is wired."),t.push(""),t.join(`
2
+ `)}function D(e){return e.some(t=>t.status==="fail")?1:0}export{P as checkApiReachable,j as checkAuthAccepted,R as checkCredential,F as checkHooks,L as checkInstance,V as checkLedger,N as checkNodeVersion,S as checkVectorCoverage,D as doctorExitCode,O as inspectAmbientHooks,J as renderDoctorReport};
@@ -1,14 +1,14 @@
1
- import{ckgSlug as j}from"../ckg.js";import{safeJsonParse as x,darfHeraus as N}from"../utils.js";import{ersparteMinuten as P,zaehltAlsErsparnis as F}from"../wertbeitrag.js";import{findePaare as K}from"../zulassung-und-urteil.js";import{Vektorbestand as O}from"../bedeutung.js";import{doppelPaare as H}from"../doppel-kandidat.js";import{DOPPEL_SCHWELLE as U}from"../rangfolge-stellschrauben.js";import{sofortAntwortNeuesBrain as W}from"../leeres-brain.js";import{fuerWeitergabe as z}from"../schreibtor.js";function B(A){return N(A)&&!A.group}const te=new Set(["memory_consolidate","brain_diff","causal_trace","knowledge_decay","autopilot"]);async function ne(A,S,M,L){switch(A){case"memory_consolidate":{const{instance_id:l,dry_run:g=!1,stale_days:$=90}=S,m=await M(l),w=Date.now(),d=$*86400*1e3;let u=0;const _=[];do{const[n,e]=await m.scan(u,"MATCH","cachly:lesson:best:*","COUNT",200);u=parseInt(n),_.push(...e)}while(u!==0);if(_.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 n of _){const e=await m.get(n);if(!e)continue;const s=x(e,null);s&&c.set(n,s)}const y=new O(0);await y.aktualisiere(m).catch(()=>{});const f=H([...c.values()].flatMap(n=>{const e=y.rohvektor(n.topic);return e?[{lektion:n,vektor:e}]:[]})),k=[...c.values()],b=K(k),h=b.filter(n=>n.beziehung==="widerspruch"),v=b.filter(n=>n.beziehung==="ersetzung"),t=[],o=new Map;for(const[n,e]of c){const s=o.get(e.topic)??[];s.push(n),o.set(e.topic,s)}for(const[n,e]of o)new Set(e.map(i=>c.get(i)?.outcome)).size>1&&t.push({topic:n,keys:e});const a=[];for(const[n,e]of c){const s=w-new Date(e.ts).getTime(),i=e.recall_count??0;s>d&&i===0&&a.push(n)}const r=0;if(!g)for(const n of a)await m.expire(n,86400*30);return[`\u{1F52C} **Memory Consolidation Report** ${g?"(dry run \u2014 no changes made)":"\u2705 Applied"}`,"",`\u{1F4CA} **Before:** ${_.length} lessons`,"",`\u{1F501} **Contradictions \u2014 exact topic match (old rule):** ${t.length}`,`\u{1F50E} **Same subject, admitted for judgement:** ${b.length} \u2192 contradiction ${h.length} \xB7 supersession ${v.length}`,...h.slice(0,5).map(n=>` \u2192 \`${n.a.topic}\` vs \`${n.b.topic}\` (${n.naehe.toFixed(2)}): ${n.grund}`),h.length>5?` \u2026 and ${h.length-5} more`:"",...v.slice(0,3).map(n=>` \u21BB \`${n.a.topic}\` \u2192 \`${n.b.topic}\`: ${n.grund}`),...t.slice(0,5).map(n=>` \u2192 \`${n.topic}\`: ${n.keys.length} conflicting entries (kept: success)`),t.length>5?` \u2026 and ${t.length-5} more`:"","",`\u267B\uFE0F **Possible duplicates \u2014 same meaning, different topic (cosine \u2265 ${U.toFixed(2)}, never merged):** ${f.length}`+(y.groesse===0?" (no vectors in this Brain, so nothing could be compared)":""),...f.slice(0,5).map(n=>` \u2192 \`${n.neu}\` \u2248 \`${n.alt}\` (${n.naehe.toFixed(2)})`),f.length>5?` \u2026 and ${f.length-5} more`:"",f.length>0?' If the newer one replaces the older, pass `ersetzt="<older topic>"` on its next `learn_from_attempts`.':"","",`\u{1F570}\uFE0F **Stale entries (${$}d, 0 recalls):** ${a.length}`+(a.length>0&&!g?" \u2192 set to expire in 30 days":""),"",`\u{1F4CA} **After:** ${_.length-r} lessons`,"",g?"\u{1F4A1} Re-run without dry_run=true to apply changes.":'\u2728 Brain consolidated. Run `brain_diff(since="1h")` to see the delta.'].filter(n=>n!=="").join(`
2
- `)}case"brain_diff":{const{instance_id:l,since:g="7d",format:$="summary"}=S,m=await M(l);let w;const d=g.match(/^(\d+)([dhm])$/);if(d){const e=parseInt(d[1]),s=d[2],i=s==="d"?864e5:s==="h"?36e5:6e4;w=Date.now()-e*i}else w=new Date(g).getTime()||Date.now()-7*864e5;let u=0;const _=[];do{const[e,s]=await m.scan(u,"MATCH","cachly:lesson:best:*","COUNT",200);u=parseInt(e),_.push(...s)}while(u!==0);const c=[],y=[],f=[],k=_.length;for(const e of _){const s=await m.get(e);if(s){const i=x(s,null);if(!i)continue;if(new Date(i.ts).getTime()>=w){const T=`cachly:lesson:history:${i.topic}`;await m.llen(T)<=1?c.push(i):y.push(i)}(i.recall_count??0)>0&&f.push(i)}}const h=[`\u{1F4CA} **Brain Diff** \u2014 ${d?`last ${g}`:new Date(w).toLocaleDateString("de-DE")}`,"",`Total lessons in brain: **${k}**`,"",`\u2705 **New** (${c.length}):`,...c.slice(0,$==="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&&$==="summary"?` \u2026 and ${c.length-5} more`:"","",`\u{1F504} **Updated** (${y.length}):`,...y.slice(0,$==="detailed"?20:5).map(e=>` ~ \`${e.topic}\` \u2014 now: ${e.outcome}`),y.length>5&&$==="summary"?` \u2026 and ${y.length-5} more`:"","",`\u{1F50D} **Active** (recalled at least once): ${f.length}`,""].filter(e=>e!==""),v=[],t=m.scanStream({match:"cachly:ckg:node:resolution-*",count:100});await new Promise((e,s)=>{t.on("data",i=>v.push(...i)),t.on("end",e),t.on("error",s)});const o=[];for(const e of v){const s=await m.get(e);if(s){const i=x(s,null);i?.ts&&new Date(i.ts).getTime()>=w&&o.push({topic:i.topic??e,resolution:i.resolution??"unknown",ts:i.ts})}}if(o.length>0){h.push(`\u{1F5F3}\uFE0F **MADC Resolutions** (${o.length} contested beliefs resolved):`);for(const e of o.slice(0,5)){const s=e.resolution==="unanimous_success"?"\u2705":e.resolution==="unanimous_failure"?"\u274C":"\u26A0\uFE0F";h.push(` ${s} \`${e.topic}\` \u2192 ${e.resolution}`)}h.push("")}const a=new Set(y.concat(f).map(e=>e.topic.split(":")[0])),r=[...new Set(c.map(e=>e.topic.split(":")[0]))].filter(e=>!a.has(e));r.length>0&&h.push(`\u{1F331} **New domains bootstrapped:** ${r.map(e=>`\`${e}\``).join(", ")}`,"");const n=(await m.lrange("cachly:fedbrain:federations",-20,-1)).map(e=>x(e,null)).filter(e=>e!==null&&new Date(e.transferred_at).getTime()>=w);if(n.length>0){h.push(`\u{1F9E0} **FedBrain transfers received (${n.length}):**`);for(const e of n.slice(0,3))h.push(` \u{1F4E5} domain \`${e.domain}\` from \`${e.source.slice(0,16)}\u2026\` \u2014 ${e.nodes} nodes, ${e.edges} edges`);h.push("")}return h.push("\u{1F4A1} Run `memory_consolidate` to merge duplicates \xB7 `knowledge_decay` to see confidence scores."),h.join(`
3
- `)}case"causal_trace":{const{instance_id:l,problem:g,max_depth:$=5,tags:m=[]}=S,w=await W(l,L,`\u{1F50D} **Causal Trace: "${g}"**
1
+ import{ckgSlug as L}from"../ckg.js";import{safeJsonParse as x,darfHeraus as N}from"../utils.js";import{ersparteMinuten as F,zaehltAlsErsparnis as K}from"../wertbeitrag.js";import{findePaare as O}from"../zulassung-und-urteil.js";import{Vektorbestand as H}from"../bedeutung.js";import{doppelPaare as U}from"../doppel-kandidat.js";import{DOPPEL_SCHWELLE as W}from"../rangfolge-stellschrauben.js";import{sofortAntwortNeuesBrain as z}from"../leeres-brain.js";import{fuerWeitergabe as G}from"../schreibtor.js";import{kappeAnfrage as V,trefferIstStark as q}from"../pflichtfelder.js";function B(M){return N(M)&&!M.group}const ae=new Set(["memory_consolidate","brain_diff","causal_trace","knowledge_decay","autopilot"]);async function ie(M,D,R,P){switch(M){case"memory_consolidate":{const{instance_id:d,dry_run:_=!1,stale_days:y=90}=D,m=await R(d),f=Date.now(),k=y*86400*1e3;let c=0;const u=[];do{const[t,e]=await m.scan(c,"MATCH","cachly:lesson:best:*","COUNT",200);c=parseInt(t),u.push(...e)}while(c!==0);if(u.length===0)return"\u{1F9E0} **Brain is empty** \u2014 no lessons to consolidate yet. Use `learn_from_attempts` after your next bug fix.";const l=new Map;for(const t of u){const e=await m.get(t);if(!e)continue;const o=x(e,null);o&&l.set(t,o)}const g=new H(0);await g.aktualisiere(m).catch(()=>{});const w=U([...l.values()].flatMap(t=>{const e=g.rohvektor(t.topic);return e?[{lektion:t,vektor:e}]:[]})),$=[...l.values()],b=O($),p=b.filter(t=>t.beziehung==="widerspruch"),S=b.filter(t=>t.beziehung==="ersetzung"),a=[],n=new Map;for(const[t,e]of l){const o=n.get(e.topic)??[];o.push(t),n.set(e.topic,o)}for(const[t,e]of n)new Set(e.map(i=>l.get(i)?.outcome)).size>1&&a.push({topic:t,keys:e});const s=[];for(const[t,e]of l){const o=f-new Date(e.ts).getTime(),i=e.recall_count??0;o>k&&i===0&&s.push(t)}const r=0;if(!_)for(const t of s)await m.expire(t,86400*30);return[`\u{1F52C} **Memory Consolidation Report** ${_?"(dry run \u2014 no changes made)":"\u2705 Applied"}`,"",`\u{1F4CA} **Before:** ${u.length} lessons`,"",`\u{1F501} **Contradictions \u2014 exact topic match (old rule):** ${a.length}`,`\u{1F50E} **Same subject, admitted for judgement:** ${b.length} \u2192 contradiction ${p.length} \xB7 supersession ${S.length}`,...p.slice(0,5).map(t=>` \u2192 \`${t.a.topic}\` vs \`${t.b.topic}\` (${t.naehe.toFixed(2)}): ${t.grund}`),p.length>5?` \u2026 and ${p.length-5} more`:"",...S.slice(0,3).map(t=>` \u21BB \`${t.a.topic}\` \u2192 \`${t.b.topic}\`: ${t.grund}`),...a.slice(0,5).map(t=>` \u2192 \`${t.topic}\`: ${t.keys.length} conflicting entries (kept: success)`),a.length>5?` \u2026 and ${a.length-5} more`:"","",`\u267B\uFE0F **Possible duplicates \u2014 same meaning, different topic (cosine \u2265 ${W.toFixed(2)}, never merged):** ${w.length}`+(g.groesse===0?" (no vectors in this Brain, so nothing could be compared)":""),...w.slice(0,5).map(t=>` \u2192 \`${t.neu}\` \u2248 \`${t.alt}\` (${t.naehe.toFixed(2)})`),w.length>5?` \u2026 and ${w.length-5} more`:"",w.length>0?' If the newer one replaces the older, pass `ersetzt="<older topic>"` on its next `learn_from_attempts`.':"","",`\u{1F570}\uFE0F **Stale entries (${y}d, 0 recalls):** ${s.length}`+(s.length>0&&!_?" \u2192 set to expire in 30 days":""),"",`\u{1F4CA} **After:** ${u.length-r} lessons`,"",_?"\u{1F4A1} Re-run without dry_run=true to apply changes.":'\u2728 Brain consolidated. Run `brain_diff(since="1h")` to see the delta.'].filter(t=>t!=="").join(`
2
+ `)}case"brain_diff":{const{instance_id:d,since:_="7d",format:y="summary"}=D,m=await R(d);let f;const k=_.match(/^(\d+)([dhm])$/);if(k){const e=parseInt(k[1]),o=k[2],i=o==="d"?864e5:o==="h"?36e5:6e4;f=Date.now()-e*i}else f=new Date(_).getTime()||Date.now()-7*864e5;let c=0;const u=[];do{const[e,o]=await m.scan(c,"MATCH","cachly:lesson:best:*","COUNT",200);c=parseInt(e),u.push(...o)}while(c!==0);const l=[],g=[],w=[],$=u.length;for(const e of u){const o=await m.get(e);if(o){const i=x(o,null);if(!i)continue;if(new Date(i.ts).getTime()>=f){const T=`cachly:lesson:history:${i.topic}`;await m.llen(T)<=1?l.push(i):g.push(i)}(i.recall_count??0)>0&&w.push(i)}}const p=[`\u{1F4CA} **Brain Diff** \u2014 ${k?`last ${_}`:new Date(f).toLocaleDateString("de-DE")}`,"",`Total lessons in brain: **${$}**`,"",`\u2705 **New** (${l.length}):`,...l.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}"}`),l.length>5&&y==="summary"?` \u2026 and ${l.length-5} more`:"","",`\u{1F504} **Updated** (${g.length}):`,...g.slice(0,y==="detailed"?20:5).map(e=>` ~ \`${e.topic}\` \u2014 now: ${e.outcome}`),g.length>5&&y==="summary"?` \u2026 and ${g.length-5} more`:"","",`\u{1F50D} **Active** (recalled at least once): ${w.length}`,""].filter(e=>e!==""),S=[],a=m.scanStream({match:"cachly:ckg:node:resolution-*",count:100});await new Promise((e,o)=>{a.on("data",i=>S.push(...i)),a.on("end",e),a.on("error",o)});const n=[];for(const e of S){const o=await m.get(e);if(o){const i=x(o,null);i?.ts&&new Date(i.ts).getTime()>=f&&n.push({topic:i.topic??e,resolution:i.resolution??"unknown",ts:i.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 o=e.resolution==="unanimous_success"?"\u2705":e.resolution==="unanimous_failure"?"\u274C":"\u26A0\uFE0F";p.push(` ${o} \`${e.topic}\` \u2192 ${e.resolution}`)}p.push("")}const s=new Set(g.concat(w).map(e=>e.topic.split(":")[0])),r=[...new Set(l.map(e=>e.topic.split(":")[0]))].filter(e=>!s.has(e));r.length>0&&p.push(`\u{1F331} **New domains bootstrapped:** ${r.map(e=>`\`${e}\``).join(", ")}`,"");const t=(await m.lrange("cachly:fedbrain:federations",-20,-1)).map(e=>x(e,null)).filter(e=>e!==null&&new Date(e.transferred_at).getTime()>=f);if(t.length>0){p.push(`\u{1F9E0} **FedBrain transfers received (${t.length}):**`);for(const e of t.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:d,problem:_,max_depth:y=5,tags:m=[]}=D,f=V(_),k=await z(d,P,`\u{1F50D} **Causal Trace: "${f}"**
4
4
 
5
- No matching lessons found in brain.`);if(w)return w;const d=await M(l),u=g.toLowerCase().replace(/[^a-z0-9\s\-_:]/g," ").split(/\s+/).filter(o=>o.length>2),_={critical:"\u{1F534}",major:"\u{1F7E0}",minor:"\u{1F7E1}"},c=[];try{for(const o of u.slice(0,4)){const a=await d.smembers(`cachly:ckg:idx:from:${j(o)}`),r=await d.smembers(`cachly:ckg:idx:to:${j(o)}`),p=[],n=d.scanStream({match:`cachly:ckg:node:*${o}*`,count:50});await new Promise((e,s)=>{n.on("data",i=>p.push(...i)),n.on("end",e),n.on("error",s)});for(const e of p.slice(0,10)){const s=await d.get(e);if(!s)continue;const i=x(s,null);if(!i)continue;const D=await d.smembers(`cachly:ckg:idx:from:${i.id}`);for(const T of D.slice(0,20)){const R=await d.get(T);if(!R)continue;const C=x(R,null);if(!C||C.edgeType!=="fixes"&&C.edgeType!=="requires")continue;const I=await d.get(`cachly:lesson:best:${C.from.replace(/-/g,":").replace(/^fix:/,"fix:")}`),E=I?x(I,null)??void 0:void 0;c.push({conceptId:i.id,edge:C,lesson:E&&B(E)?E:void 0})}}for(const e of[...a,...r].slice(0,20)){const s=await d.get(e);if(!s)continue;const i=x(s,null);if(!i)continue;const D=await d.get(`cachly:lesson:best:${i.from}`),T=D?x(D,null)??void 0:void 0;c.push({conceptId:i.from,edge:i,lesson:T&&B(T)?T:void 0})}}}catch{}const y=new Set,f=c.filter(o=>{const a=`${o.edge.from}:${o.edge.edgeType}:${o.edge.to}`;return y.has(a)?!1:(y.add(a),!0)}).sort((o,a)=>a.edge.confidence-o.edge.confidence).slice(0,$);let k=0;const b=[];do{const[o,a]=await d.scan(k,"MATCH","cachly:lesson:best:*","COUNT",200);k=parseInt(o),b.push(...a)}while(k!==0);const h=[];for(const o of b){const a=await d.get(o);if(a){const r=x(a,null);if(!r||!B(r)||m.length>0&&!(r.tags??[]).some(e=>m.includes(e)))continue;const p=[r.topic,r.what_failed??"",r.what_worked??"",r.context??""].join(" ").toLowerCase(),n=u.reduce((e,s)=>e+(p.includes(s)?1:0),0);n>0&&h.push({score:n,lesson:r,key:o})}}h.sort((o,a)=>a.score-o.score);const v=h.slice(0,$);if(v.length===0&&f.length===0)return[`\u{1F50D} **Causal Trace: "${g}"**`,"","No matching lessons found in brain.","","\u{1F4A1} After you solve this, call:","```","learn_from_attempts(",` instance_id = "${l}",`,` topic = "fix:${u[0]??"issue"}",`,' outcome = "success",',' what_worked = "...",',` what_failed = "${g}",`,")","```"].join(`
6
- `);const t=[`\u{1F50D} **Causal Trace: "${g}"**`,""];if(f.length>0){t.push("### \u{1F578}\uFE0F CKG Graph (confidence-ranked)");for(const o of f){const a=Math.round(o.edge.confidence*100),r="\u2593".repeat(Math.round(a/10))+"\u2591".repeat(10-Math.round(a/10));t.push(` ${o.edge.from} **\u2192[${o.edge.edgeType}]\u2192** ${o.edge.to}`),t.push(` ${r} ${a}% confidence (${o.edge.successes}/${o.edge.trials} confirmed)`),o.lesson?.what_worked&&t.push(` \u2705 Fix: ${o.lesson.what_worked.slice(0,150)}`),t.push("")}}if(v.length>0){t.push(f.length>0?`### \u{1F4DA} Text Search (${v.length} related lessons)`:`Found **${v.length}** related lessons. Reconstructed causal chain:`,"");const o=v.filter(p=>p.lesson.outcome!=="success"),a=v.filter(p=>p.lesson.outcome==="success");o.length>0&&(t.push("**Root causes & failure chain:**"),o.forEach((p,n)=>{const e=p.lesson,s=_[e.severity??"minor"]??"\u{1F7E1}";t.push(`${n===0?" Root:":" \u2192 :"} ${s} \`${e.topic}\``),e.what_failed&&t.push(` \u21B3 ${e.what_failed.slice(0,120)}`)}),t.push("")),a.length>0&&(t.push("**Solutions that worked before:**"),a.forEach((p,n)=>{const e=p.lesson,s=new Date(e.ts).toLocaleDateString("de-DE");t.push(` ${n+1}. \u2705 \`${e.topic}\` \u2014 ${s} \xB7 in ${e.recall_count??0} answers`),e.what_worked&&t.push(` ${e.what_worked.slice(0,200)}`)}),t.push(""));const r=a[0]?.lesson;if(r?.what_worked){t.push("**\u26A1 Most likely fix:**"),t.push("```"),t.push(r.what_worked.slice(0,500)),t.push("```"),t.push("");const p=F(r)?P(r,"trace"):0;p>0&&d.incrbyfloat(`cachly:stats:time_saved_mins:${l}`,p).catch(()=>{})}}return t.push(`\u{1F4A1} After applying: \`learn_from_attempts(topic="fix:${u[0]??"issue"}", outcome="success", ...)\``),f.length>0&&t.push(`\u{1F578}\uFE0F Explore graph: \`ckg_inspect(concept="${u[0]??"fix"}")\``),t.join(`
7
- `)}case"knowledge_decay":{let f=function(t){const o=Math.round(t/10);return"\u2588".repeat(o)+"\u2591".repeat(10-o)+` ${t}%`};const{instance_id:l,min_age_days:g=0,show_top:$=20}=S,m=await M(l),w=Date.now(),d=g*864e5;let u=0;const _=[];do{const[t,o]=await m.scan(u,"MATCH","cachly:lesson:best:*","COUNT",200);u=parseInt(t),_.push(...o)}while(u!==0);const c=[];for(const t of _){const o=await m.get(t);if(o){const a=x(o,null);if(!a)continue;const r=w-new Date(a.ts).getTime();if(r<d)continue;const p=Math.floor(r/864e5),n=a.recall_count??0,e=Math.max(0,p-7),s=Math.max(5,100-e),i=Math.min(50,n*5),D=a.outcome==="failure"?-20:0,T=Math.min(100,Math.max(0,s+i+D));c.push({topic:a.topic,confidence:T,age_days:p,recalls:n,outcome:a.outcome})}}c.sort((t,o)=>t.confidence-o.confidence);const y=c.slice(0,$),k=c.length>0?Math.round(c.reduce((t,o)=>t+o.confidence,0)/c.length):0,b=c.filter(t=>t.confidence<30).length,h=c.filter(t=>t.confidence>=70).length;return[`\u{1F9EA} **Knowledge Decay Report** \u2014 ${c.length} lessons`,"",`Average confidence: **${f(k)}**`,`\u{1F534} Critical (<30%): ${b} \u{1F7E2} Healthy (\u226570%): ${h}`,"","Lowest confidence lessons (fix these first):","",...y.map((t,o)=>`${(o+1).toString().padStart(2)}. \`${t.topic}\`
8
- ${f(t.confidence)} \xB7 ${t.age_days}d old \xB7 ${t.recalls} recalls \xB7 ${t.outcome}`),"",c.length>$?`\u2026 and ${c.length-$} 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(t=>t!=="").join(`
9
- `)}case"autopilot":{const{instance_id:l,editor:g="claude",project_name:$="this project",style:m="full"}=S,w=z({editor:String(g),project_name:String($)},"the instruction file (CLAUDE.md)");if(!w.ok)return w.meldung;const d=(b,h)=>b.replace(/[\p{C}\s]+/gu," ").trim().slice(0,h),u=d(w.felder.editor,30)||"claude",_=d(w.felder.project_name,80)||"this project",c=`## Cachly AI Brain \u2014 Autopilot
5
+ No matching lessons found in brain.`);if(k)return k;const c=await R(d),u=f.toLowerCase().replace(/[^a-z0-9\s\-_:]/g," ").split(/\s+/).filter(s=>s.length>2),l={critical:"\u{1F534}",major:"\u{1F7E0}",minor:"\u{1F7E1}"},g=[];try{for(const s of u.slice(0,4)){const r=await c.smembers(`cachly:ckg:idx:from:${L(s)}`),h=await c.smembers(`cachly:ckg:idx:to:${L(s)}`),t=[],e=c.scanStream({match:`cachly:ckg:node:*${s}*`,count:50});await new Promise((o,i)=>{e.on("data",v=>t.push(...v)),e.on("end",o),e.on("error",i)});for(const o of t.slice(0,10)){const i=await c.get(o);if(!i)continue;const v=x(i,null);if(!v)continue;const T=await c.smembers(`cachly:ckg:idx:from:${v.id}`);for(const C of T.slice(0,20)){const I=await c.get(C);if(!I)continue;const A=x(I,null);if(!A||A.edgeType!=="fixes"&&A.edgeType!=="requires")continue;const j=await c.get(`cachly:lesson:best:${A.from.replace(/-/g,":").replace(/^fix:/,"fix:")}`),E=j?x(j,null)??void 0:void 0;g.push({conceptId:v.id,edge:A,lesson:E&&B(E)?E:void 0})}}for(const o of[...r,...h].slice(0,20)){const i=await c.get(o);if(!i)continue;const v=x(i,null);if(!v)continue;const T=await c.get(`cachly:lesson:best:${v.from}`),C=T?x(T,null)??void 0:void 0;g.push({conceptId:v.from,edge:v,lesson:C&&B(C)?C:void 0})}}}catch{}const w=new Set,$=g.filter(s=>{const r=`${s.edge.from}:${s.edge.edgeType}:${s.edge.to}`;return w.has(r)?!1:(w.add(r),!0)}).sort((s,r)=>r.edge.confidence-s.edge.confidence).slice(0,y);let b=0;const p=[];do{const[s,r]=await c.scan(b,"MATCH","cachly:lesson:best:*","COUNT",200);b=parseInt(s),p.push(...r)}while(b!==0);const S=[];for(const s of p){const r=await c.get(s);if(r){const h=x(r,null);if(!h||!B(h)||m.length>0&&!(h.tags??[]).some(o=>m.includes(o)))continue;const t=[h.topic,h.what_failed??"",h.what_worked??"",h.context??""].join(" ").toLowerCase(),e=u.reduce((o,i)=>o+(t.includes(i)?1:0),0);e>0&&S.push({score:e,lesson:h,key:s})}}S.sort((s,r)=>r.score-s.score);const a=S.slice(0,y);if(a.length===0&&$.length===0)return[`\u{1F50D} **Causal Trace: "${f}"**`,"","No matching lessons found in brain.","","\u{1F4A1} After you solve this, call:","```","learn_from_attempts(",` instance_id = "${d}",`,` topic = "fix:${u[0]??"issue"}",`,' outcome = "success",',' what_worked = "...",',` what_failed = "${f}",`,")","```"].join(`
6
+ `);const n=[`\u{1F50D} **Causal Trace: "${f}"**`,""];if($.length>0){n.push("### \u{1F578}\uFE0F CKG Graph (confidence-ranked)");for(const s of $){const r=Math.round(s.edge.confidence*100),h="\u2593".repeat(Math.round(r/10))+"\u2591".repeat(10-Math.round(r/10));n.push(` ${s.edge.from} **\u2192[${s.edge.edgeType}]\u2192** ${s.edge.to}`),n.push(` ${h} ${r}% confidence (${s.edge.successes}/${s.edge.trials} confirmed)`),s.lesson?.what_worked&&n.push(` \u2705 Fix: ${s.lesson.what_worked.slice(0,150)}`),n.push("")}}if(a.length>0){n.push($.length>0?`### \u{1F4DA} Text Search (${a.length} related lessons)`:`Found **${a.length}** related lessons. Reconstructed causal chain:`,"");const s=a.filter(t=>t.lesson.outcome!=="success"),r=a.filter(t=>t.lesson.outcome==="success");s.length>0&&(n.push("**Root causes & failure chain:**"),s.forEach((t,e)=>{const o=t.lesson,i=l[o.severity??"minor"]??"\u{1F7E1}";n.push(`${e===0?" Root:":" \u2192 :"} ${i} \`${o.topic}\``),o.what_failed&&n.push(` \u21B3 ${o.what_failed.slice(0,120)}`)}),n.push("")),r.length>0&&(n.push("**Solutions that worked before:**"),r.forEach((t,e)=>{const o=t.lesson,i=new Date(o.ts).toLocaleDateString("de-DE");n.push(` ${e+1}. \u2705 \`${o.topic}\` \u2014 ${i} \xB7 in ${o.recall_count??0} answers`),o.what_worked&&n.push(` ${o.what_worked.slice(0,200)}`)}),n.push(""));const h=r[0]?.lesson;if(h?.what_worked){const t=q(r[0].score,u);n.push(t?"**\u26A1 Most likely fix:**":`**Closest solution (weak match: ${r[0].score} of ${new Set(u).size} words \u2014 check that it fits before using it):**`),n.push("```"),n.push(h.what_worked.slice(0,500)),n.push("```"),n.push("");const e=K(h)?F(h,"trace"):0;e>0&&c.incrbyfloat(`cachly:stats:time_saved_mins:${d}`,e).catch(()=>{})}}return n.push(`\u{1F4A1} After applying: \`learn_from_attempts(topic="fix:${u[0]??"issue"}", outcome="success", ...)\``),$.length>0&&n.push(`\u{1F578}\uFE0F Explore graph: \`ckg_inspect(concept="${u[0]??"fix"}")\``),n.join(`
7
+ `)}case"knowledge_decay":{let w=function(a){const n=Math.round(a/10);return"\u2588".repeat(n)+"\u2591".repeat(10-n)+` ${a}%`};const{instance_id:d,min_age_days:_=0,show_top:y=20}=D,m=await R(d),f=Date.now(),k=_*864e5;let c=0;const u=[];do{const[a,n]=await m.scan(c,"MATCH","cachly:lesson:best:*","COUNT",200);c=parseInt(a),u.push(...n)}while(c!==0);const l=[];for(const a of u){const n=await m.get(a);if(n){const s=x(n,null);if(!s)continue;const r=f-new Date(s.ts).getTime();if(r<k)continue;const h=Math.floor(r/864e5),t=s.recall_count??0,e=Math.max(0,h-7),o=Math.max(5,100-e),i=Math.min(50,t*5),v=s.outcome==="failure"?-20:0,T=Math.min(100,Math.max(0,o+i+v));l.push({topic:s.topic,confidence:T,age_days:h,recalls:t,outcome:s.outcome})}}l.sort((a,n)=>a.confidence-n.confidence);const g=l.slice(0,y),$=l.length>0?Math.round(l.reduce((a,n)=>a+n.confidence,0)/l.length):0,b=l.filter(a=>a.confidence<30).length,p=l.filter(a=>a.confidence>=70).length;return[`\u{1F9EA} **Knowledge Decay Report** \u2014 ${l.length} lessons`,"",`Average confidence: **${w($)}**`,`\u{1F534} Critical (<30%): ${b} \u{1F7E2} Healthy (\u226570%): ${p}`,"","Lowest confidence lessons (fix these first):","",...g.map((a,n)=>`${(n+1).toString().padStart(2)}. \`${a.topic}\`
8
+ ${w(a.confidence)} \xB7 ${a.age_days}d old \xB7 ${a.recalls} recalls \xB7 ${a.outcome}`),"",l.length>y?`\u2026 and ${l.length-y} 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(a=>a!=="").join(`
9
+ `)}case"autopilot":{const{instance_id:d,editor:_="claude",project_name:y="this project",style:m="full"}=D,f=G({editor:String(_),project_name:String(y)},"the instruction file (CLAUDE.md)");if(!f.ok)return f.meldung;const k=(b,p)=>b.replace(/[\p{C}\s]+/gu," ").trim().slice(0,p),c=k(f.felder.editor,30)||"claude",u=k(f.felder.project_name,80)||"this project",l=`## Cachly AI Brain \u2014 Autopilot
10
10
 
11
- **Instance:** \`${l}\`
11
+ **Instance:** \`${d}\`
12
12
 
13
13
  > **Where your data goes:** these calls read/write *your own* Brain instance
14
14
  > (the ID above) \u2014 your infrastructure, EU-hosted, under your control. Nothing
@@ -19,12 +19,12 @@ No matching lessons found in brain.`);if(w)return w;const d=await M(l),u=g.toLow
19
19
  > Sessions are fully automatic. The Brain starts when you call any tool and saves a summary when you close.
20
20
 
21
21
  ### Brain hooks \u2014 run these automatically (safe to skip if a step doesn't apply):
22
- - **Window opens** \u2192 call \`session_start(instance_id="${l}", focus="<today's goal>")\`
23
- - **Bug fixed / deployed / discovered** \u2192 call \`learn_from_attempts(instance_id="${l}", topic="fix:<area>", outcome="success", what_worked="...")\`
24
- - **Window closes** \u2192 call \`session_end(instance_id="${l}", summary="...", files_changed=[...])\`
25
- `,y=`## Cachly AI Brain \u2014 Autopilot for ${_}
22
+ - **Window opens** \u2192 call \`session_start(instance_id="${d}", focus="<today's goal>")\`
23
+ - **Bug fixed / deployed / discovered** \u2192 call \`learn_from_attempts(instance_id="${d}", topic="fix:<area>", outcome="success", what_worked="...")\`
24
+ - **Window closes** \u2192 call \`session_end(instance_id="${d}", summary="...", files_changed=[...])\`
25
+ `,g=`## Cachly AI Brain \u2014 Autopilot for ${u}
26
26
 
27
- **Instance:** \`${l}\`
27
+ **Instance:** \`${d}\`
28
28
 
29
29
  > **What this is & where your data goes:** the calls below read and write to
30
30
  > *your own* Cachly Brain instance (the ID above) \u2014 your infrastructure,
@@ -42,7 +42,7 @@ No matching lessons found in brain.`);if(w)return w;const d=await M(l),u=g.toLow
42
42
  ### Rule 1 \u2014 Session Start (FIRST thing in every window)
43
43
  \`\`\`
44
44
  session_start(
45
- instance_id = "${l}",
45
+ instance_id = "${d}",
46
46
  focus = "<what you plan to work on today>",
47
47
  )
48
48
  \`\`\`
@@ -52,7 +52,7 @@ Returns: last session summary, handoff tasks, recent lessons, open failures.
52
52
  **After EVERY** bug fix, deployment, or technical discovery \u2014 call immediately:
53
53
  \`\`\`
54
54
  learn_from_attempts(
55
- instance_id = "${l}",
55
+ instance_id = "${d}",
56
56
  topic = "category:keyword",
57
57
  outcome = "success" | "failure" | "partial",
58
58
  what_worked = "exact command or approach",
@@ -72,7 +72,7 @@ learn_from_attempts(
72
72
  ### Rule 3 \u2014 Session End (LAST thing before closing)
73
73
  \`\`\`
74
74
  session_end(
75
- instance_id = "${l}",
75
+ instance_id = "${d}",
76
76
  summary = "What was accomplished (2-3 sentences)",
77
77
  files_changed = ["list", "of", "changed", "files"],
78
78
  lessons_learned = <number of learn_from_attempts calls>,
@@ -82,7 +82,7 @@ session_end(
82
82
  ### Rule 4 \u2014 Before any non-trivial task
83
83
  \`\`\`
84
84
  smart_recall(
85
- instance_id = "${l}",
85
+ instance_id = "${d}",
86
86
  query = "<describe what you are about to do>",
87
87
  )
88
88
  \`\`\`
@@ -93,13 +93,13 @@ smart_recall(
93
93
 
94
94
  | When | Call |
95
95
  |---|---|
96
- | Brain feeling cluttered | \`memory_consolidate(instance_id="${l}")\` |
97
- | Weekly review | \`brain_diff(instance_id="${l}", since="7d")\` |
98
- | Weird bug, no idea why | \`causal_trace(instance_id="${l}", problem="<symptom>")\` |
99
- | Before big refactor | \`knowledge_decay(instance_id="${l}")\` |
96
+ | Brain feeling cluttered | \`memory_consolidate(instance_id="${d}")\` |
97
+ | Weekly review | \`brain_diff(instance_id="${d}", since="7d")\` |
98
+ | Weird bug, no idea why | \`causal_trace(instance_id="${d}", problem="<symptom>")\` |
99
+ | Before big refactor | \`knowledge_decay(instance_id="${d}")\` |
100
100
 
101
101
  ---
102
102
 
103
103
  *Cachly v0.6 \xB7 Generated ${new Date().toISOString().slice(0,10)}*
104
- `,f=m==="minimal"?c:y,k=u==="copilot"?".github/copilot-instructions.md":u==="continue"?".continue/cachly-autopilot.md":"CLAUDE.md";return[`\u{1F916} **Autopilot instructions generated** for **${u==="all"?"all editors":u}**`,"",`**File:** \`${k}\` \u2014 create it, or APPEND to your existing one`,"","```markdown",f,"```","",`**How to apply** (\`>>\` appends \u2014 an existing ${k} keeps every line you wrote):`,"```bash",`cat >> ${k} << 'EOF'`,f,"EOF","```","",`\u2728 Once this file is in place, **${u==="copilot"?"GitHub Copilot":u==="continue"?"Continue.dev":"Claude/Cursor/Windsurf"}** will manage the Brain automatically \u2014 no manual calls needed, ever.`].join(`
105
- `)}default:return null}}export{te as ADVANCED_TOOL_NAMES,ne as handleAdvancedTool};
104
+ `,w=m==="minimal"?l:g,$=c==="copilot"?".github/copilot-instructions.md":c==="continue"?".continue/cachly-autopilot.md":"CLAUDE.md";return[`\u{1F916} **Autopilot instructions generated** for **${c==="all"?"all editors":c}**`,"",`**File:** \`${$}\` \u2014 create it, or APPEND to your existing one`,"","```markdown",w,"```","",`**How to apply** (\`>>\` appends \u2014 an existing ${$} keeps every line you wrote):`,"```bash",`cat >> ${$} << 'EOF'`,w,"EOF","```","",`\u2728 Once this file is in place, **${c==="copilot"?"GitHub Copilot":c==="continue"?"Continue.dev":"Claude/Cursor/Windsurf"}** will manage the Brain automatically \u2014 no manual calls needed, ever.`].join(`
105
+ `)}default:return null}}export{ae as ADVANCED_TOOL_NAMES,ie as handleAdvancedTool};