@cachly-dev/mcp-server 0.10.173 → 0.10.175
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 +8 -6
- package/dist/src/ambient-cli.js +2 -7
- package/dist/src/ambient-hook-start.js +1 -0
- package/dist/src/ambient-hook.js +2 -0
- package/dist/src/ambient-hooks.js +6 -5
- package/dist/src/ambient-ledger.js +5 -5
- package/dist/src/ausgabe-ordnung.js +1 -0
- package/dist/src/commit-art.js +1 -0
- package/dist/src/credentials.js +1 -1
- package/dist/src/doctor.js +2 -2
- package/dist/src/einblendung.js +8 -0
- package/dist/src/handlers/brain.js +67 -67
- package/dist/src/handlers/fedbrain.js +31 -31
- package/dist/src/handlers/instances.js +3 -3
- package/dist/src/hook-zugang.js +1 -0
- package/dist/src/index.js +155 -156
- package/dist/src/schreibbeleg.js +5 -5
- package/dist/src/tools.js +1 -1
- package/dist/src/werkzeug-auswahl.js +2 -2
- package/dist/src/zugang.js +6 -0
- package/glama.json +1 -1
- package/hooks/cachly-ambient-einblendung.mjs +27 -0
- package/package.json +5 -2
- package/server.json +2 -2
- package/dist/src/ambient-deps.js +0 -1
- package/dist/src/ambient-memory.js +0 -1
package/README.md
CHANGED
|
@@ -340,9 +340,9 @@ MCP tool — bulk-ingests past outcomes the same way `brain_from_git` ingests co
|
|
|
340
340
|
|
|
341
341
|
---
|
|
342
342
|
|
|
343
|
-
## MCP Tools (
|
|
343
|
+
## MCP Tools (124 total, 27 in the default catalogue)
|
|
344
344
|
|
|
345
|
-
**Your editor sees 27 of them, not
|
|
345
|
+
**Your editor sees 27 of them, not 124 — on purpose.** The full list cost
|
|
346
346
|
~27,750 tokens in *every* request, which is 14 % of a 200k window gone before
|
|
347
347
|
you type anything. The tools you use daily are listed individually; the other
|
|
348
348
|
96 sit behind one dispatcher:
|
|
@@ -354,7 +354,7 @@ cachly_tool(tool: "team_roster", describe: true) get its schema first
|
|
|
354
354
|
|
|
355
355
|
Nothing is unreachable: the server dispatches by name and never consults the
|
|
356
356
|
catalogue, so `team_roster` called directly still works. Set
|
|
357
|
-
`CACHLY_ALLE_WERKZEUGE=1` to get all
|
|
357
|
+
`CACHLY_ALLE_WERKZEUGE=1` to get all 124 listed again.
|
|
358
358
|
|
|
359
359
|
The full tool catalog is generated from `sdk/mcp/src/tools.ts`. Cross-surface
|
|
360
360
|
coverage is tracked in [`../../docs/generated/surface-parity.md`](../../docs/generated/surface-parity.md),
|
|
@@ -467,7 +467,7 @@ following. Manual snippets are in the **Manual Setup** section below.
|
|
|
467
467
|
|
|
468
468
|
| Editor / Client | Auto-setup | Config file written | Global config | Notes |
|
|
469
469
|
|---|---|---|---|---|
|
|
470
|
-
| **Claude Code** | ✅ | `~/.claude/mcp.json` + `.mcp.json` | ✅ global always |
|
|
470
|
+
| **Claude Code** | ✅ | `~/.claude/mcp.json` + `.mcp.json` | ✅ global always | No key: test Brain on first tool call; browser sign-in only as fallback |
|
|
471
471
|
| **Cursor** | ✅ detected via `.cursor/` | `.cursor/mcp.json` | — | Project-level; restart Cursor after setup |
|
|
472
472
|
| **Windsurf** | ✅ detected via `.windsurf/` | `.windsurf/mcp.json` | — | Project-level; restart Windsurf after setup |
|
|
473
473
|
| **VS Code + Copilot** | ✅ detected via `.vscode/` | `.vscode/mcp.json` | — | Requires VS Code MCP extension or Copilot chat |
|
|
@@ -483,7 +483,8 @@ following. Manual snippets are in the **Manual Setup** section below.
|
|
|
483
483
|
|---|---|
|
|
484
484
|
| `autosetup` from a real terminal (TTY) | OAuth device-flow → browser click → API key saved automatically |
|
|
485
485
|
| `autosetup` from VSCode task / CI (non-TTY) | Auto-detects non-interactive, opens browser with step-by-step guide, prints `CACHLY_JWT=... autosetup` instruction |
|
|
486
|
-
| First tool call
|
|
486
|
+
| First tool call without any key | Instant trial: an anonymous test Brain on EU servers, key saved to `~/.cachly/credentials.json` and `~/.claude/mcp.json`, the call proceeds. If that fails (offline, rate limit): inline device-flow with URL + code |
|
|
487
|
+
| First tool call with `CACHLY_BRAIN_INSTANCE_ID` set but no key | Inline device-flow (you already have a Brain — no second account is created) |
|
|
487
488
|
| `CACHLY_JWT=cky_live_xxx npx ... autosetup` | Skips auth step entirely, uses provided key |
|
|
488
489
|
|
|
489
490
|
> **Tip — fastest per-project setup from inside Claude Code:**
|
|
@@ -509,7 +510,8 @@ following. Manual snippets are in the **Manual Setup** section below.
|
|
|
509
510
|
}
|
|
510
511
|
}
|
|
511
512
|
```
|
|
512
|
-
|
|
513
|
+
Without a key, the first tool call creates a test Brain on EU servers — no sign-up.
|
|
514
|
+
Run `npx @cachly-dev/mcp-server@latest autopilot` to use your own account instead.
|
|
513
515
|
</details>
|
|
514
516
|
|
|
515
517
|
<details>
|
package/dist/src/ambient-cli.js
CHANGED
|
@@ -1,7 +1,2 @@
|
|
|
1
|
-
import{
|
|
2
|
-
`).find(
|
|
3
|
-
`)?e[0].summary.trim():`\u{1F9E0} Relevant memory from your cachly brain (auto-recalled):
|
|
4
|
-
`+e.map(o=>{const i=_(o);return`- ${o.summary.trim()}`+(i?`
|
|
5
|
-
`+i:"")}).join(`
|
|
6
|
-
`),n=b(r);return t?`${n}
|
|
7
|
-
${x}`:n}function P(e,t){return t?JSON.stringify({hookSpecificOutput:{hookEventName:e,additionalContext:t}}):""}async function j(e,t,r){let n;const o=new Promise(i=>{n=setTimeout(()=>i(r),t)});try{return await Promise.race([e,o])}finally{n&&clearTimeout(n)}}async function M(e,t){const r=v(e);if(!r)return"";const n=r.hook_event_name??"UserPromptSubmit",o=k(r);if(o===null)return"";try{if(t.backoff&&await t.backoff())return""}catch{}let i=[];try{i=await j(t.recall(o,n),t.timeoutMs??3e3,[])}catch{return""}if(!Array.isArray(i)||i.length===0)return"";const c=n==="UserPromptSubmit"?r.prompt??o:o,a=t.loadMemory?.()??g(),s=t.loadMemory?p(c,i,a,t.gate):{...d(c,i,t.gate),risk:"normal",suppressedDuplicates:[]};if(t.saveMemory)try{t.saveMemory(h(a,s))}catch{}if(!s.inject)return"";const m=n==="UserPromptSubmit"||n==="PreToolUse",u=T(s.selected,m),f=l(u);try{t.onInject?.(f,n)}catch{}return P(n,u)}export{x as CREDIT_FOOTER,P as buildHookOutput,T as formatContextBlock,_ as formatProvenance,v as parseHookPayload,k as recallQueryFor,M as runAmbient,O as stopObservation,$ as truncateToTokens};
|
|
1
|
+
import{promptEinblendung as c,holeBestand as u,sitzungsKontext as l,hookAusgabe as i,estimateTokens as a}from"./einblendung.js";function f(e){const t=(e??"").trim();if(!t)return null;try{const n=JSON.parse(t);return n&&typeof n=="object"?n:null}catch{return null}}const m=new Set(["Edit","Write","MultiEdit","NotebookEdit"]);function p(e){const t=e.hook_event_name??"UserPromptSubmit";if(t==="SessionStart"||t==="Stop")return null;if(t==="PreToolUse"){if(e.tool_name&&!m.has(e.tool_name))return null;const r=e.tool_input?.file_path;if(typeof r!="string"||!r.trim())return null;const o=r.trim().replace(/\\/g,"/"),s=(e.cwd??"").replace(/\\/g,"/").replace(/\/+$/,"");return s&&o.toLowerCase().startsWith(s.toLowerCase()+"/")?o.slice(s.length+1):o}return(e.prompt??"").trim()||null}const g=/\b(fixed|resolved|behoben|gefixt|root cause|the bug was|der fehler (lag|war)|ursache (war|gefunden)|now passes|tests? (are|is|sind) (green|grün)|deployed successfully)\b/i;function b(e){const t=(e.last_assistant_message??"").trim();return t.length<80||!g.test(t)?null:{action:(t.split(`
|
|
2
|
+
`).find(r=>r.trim().length>0)?.trim()??t).slice(0,200),outcome:"success",details:t.slice(0,500),severity:"minor"}}function d(e){const t=e.action.toLowerCase().replace(/[^a-z0-9:\-_\s]/g,"").trim().split(/\s+/).slice(0,4).join("-");return{topic:t?t.includes(":")?t:`auto:${t}`:"auto:stop",outcome:e.outcome,what_worked:e.action,context:e.details,severity:e.severity,source:"ambient-stop",tags:["auto-learned"]}}const x=i,h=5e3;async function k(e,t){try{const n=f(e);if(!n)return"";const r=n.hook_event_name??"UserPromptSubmit";if(!t.cfg.jwt||!t.cfg.instanceId)return"";let o="";if(r==="SessionStart"){const{lessons:s}=await u(t.cfg,{force:!0,fetchFn:t.fetchFn});o=l(s)}else{const s=p(n);if(s===null)return"";o=(await c(s,t.cfg,{nebenlauf:t.nebenlauf,abrufMs:h,leser:r!=="PreToolUse",session:n.session_id??null,protokoll:t.protokoll,fetchFn:t.fetchFn})).kontext}if(!o)return"";try{t.onInject?.(a(o),r)}catch{}return i(r,o)}catch{return""}}export{h as PROMPT_ABRUF_MS,x as buildHookOutput,f as parseHookPayload,p as recallQueryFor,k as runEinblendung,d as stopLernanfrage,b as stopObservation};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{fileURLToPath as o}from"node:url";import{AUFFRISCHEN as r,auffrischenHauptlauf as t,hookHauptlauf as i}from"./ambient-hook.js";const a=process.argv.slice(2);a[0]===r?await t():await i(a,o(import.meta.url));
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{readFileSync as w}from"node:fs";import{parseHookPayload as f,runEinblendung as y,stopLernanfrage as b,stopObservation as g}from"./ambient-cli.js";import{stopAntwort as U}from"./schreibbeleg.js";import{holeBestand as P}from"./einblendung.js";import{appendLedgerEntry as I,reportLedgerEntry as A}from"./ambient-ledger.js";import{findeInstanz as u,findeSchluessel as l,hookUmfeld as d,projektHatHook as H}from"./hook-zugang.js";const T="https://api.cachly.dev",v="ambient-auffrischen",m=new Set(["SessionStart","UserPromptSubmit","PreToolUse","Stop"]);async function k(t){try{const e=f(t.raw),n=e?.hook_event_name??t.args.find(o=>m.has(o))??t.umfeld.env.CACHLY_HOOK_EVENT??"";if(t.args.includes("--plugin")&&n&&H(n,t.umfeld))return"";if(n==="Stop"&&e?.transcript_path)try{const o=w(e.transcript_path,"utf-8").split(`
|
|
2
|
+
`),a=U(o,e.stop_hook_active===!0);if(a)return a}catch{}const i=l(t.umfeld),c=u(t.umfeld);if(!i||!c)return"";const s={apiUrl:t.apiUrl,jwt:i,instanceId:c};if(n==="Stop"){const o=e?g(e):null;return o&&await(t.fetchFn??fetch)(`${s.apiUrl}/api/v1/instances/${s.instanceId}/learn`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${s.jwt}`},body:JSON.stringify(b(o)),signal:AbortSignal.timeout(8e3)}).catch(()=>{}),""}let r;const _=await(t.einblenden??y)(t.raw,{cfg:s,nebenlauf:t.selbst?[t.selbst,v]:void 0,fetchFn:t.fetchFn,onInject:(o,a)=>{const p={ts:new Date().toISOString(),event:a,injected:o,prevented:0};I(p),r=A(s,p,t.fetchFn)}});return r&&await Promise.race([r,new Promise(o=>setTimeout(o,500))]),_}catch{return""}}async function O(){if(process.stdin.isTTY)return"";const t=[];for await(const e of process.stdin)t.push(e);return Buffer.concat(t).toString("utf-8")}function h(){return process.env.CACHLY_API_URL||T}const S={SessionStart:25e3,UserPromptSubmit:8e3,PreToolUse:8e3,Stop:5e4};async function R(t,e){try{const n=await O(),i=f(n)?.hook_event_name??t.find(r=>m.has(r))??"",c=Number(process.env.CACHLY_HOOK_FRIST_MS)||S[i]||S.UserPromptSubmit,s=await Promise.race([k({args:t,raw:n,umfeld:d(),apiUrl:h(),selbst:e}),new Promise(r=>setTimeout(()=>r(""),c))]);s&&await new Promise(r=>process.stdout.write(s,()=>r()))}catch{}process.exit(0)}async function N(){try{const t=d(),e=l(t),n=u(t);e&&n&&await P({apiUrl:h(),jwt:e,instanceId:n},{force:!0})}catch{}process.exit(0)}export{v as AUFFRISCHEN,S as HOOK_FRIST_MS,T as STANDARD_API_URL,N as auffrischenHauptlauf,k as hookAntwort,R as hookHauptlauf};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import{existsSync as
|
|
2
|
-
`)}function
|
|
3
|
-
`&&(
|
|
4
|
-
`,"utf-8"),await
|
|
5
|
-
`,"utf-8")),{sessionStartPath:
|
|
1
|
+
import{existsSync as I,readFileSync as L}from"node:fs";import{readFile as A,writeFile as U,chmod as M,mkdir as w}from"node:fs/promises";import{dirname as j,resolve as l}from"node:path";import{fileURLToPath as B}from"node:url";const D="v5",st="ambient-recall",m="cachly-ambient-einblendung.mjs";function T(t){return t.replace(/\\/g,"\\\\").replace(/'/g,"\\'")}function b(t,o){return["#!/usr/bin/env node",`// cachly Ambient Recall \u2014 ${t} ${D}`,"// 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 (${m}) in this process.`,`process.env.CACHLY_BRAIN_INSTANCE_ID = '${T(o.instanceId)}';`,`process.env.CACHLY_HOOK_EVENT = '${T(t)}';`,`process.argv.splice(2, process.argv.length, '${T(t)}');`,"try {",` await import('./${m}');`,"} catch {"," process.exit(0);","}"].join(`
|
|
2
|
+
`)}function J(){const t=j(B(import.meta.url));for(const o of[l(t,"..",E,m),l(t,"..","..",E,m)])if(I(o))return o;return null}function K(t){return b("SessionStart",t)}function F(t){return b("UserPromptSubmit",t)}function G(t){return b("PreToolUse",t)}function V(t){return b("Stop",t)}const W={SessionStart:30,UserPromptSubmit:10,PreToolUse:10,Stop:60},Y="Edit|Write|MultiEdit|NotebookEdit";function $(t,o){const e=(n,i,u)=>({...u?{matcher:u}:{},hooks:[{type:"command",command:`node "${i}"${o?` ${o(n)}`:""}`,timeout:W[n]}]}),r={SessionStart:[e("SessionStart",t.sessionStart)],UserPromptSubmit:[e("UserPromptSubmit",t.userPromptSubmit)]};return t.preToolUse&&(r.PreToolUse=[e("PreToolUse",t.preToolUse,Y)]),t.stop&&(r.Stop=[e("Stop",t.stop)]),r}const H=".claude/hooks",Q=".claude/settings.json",R={SessionStart:"cachly-ambient-session-start.mjs",UserPromptSubmit:"cachly-ambient-prompt-submit.mjs",PreToolUse:"cachly-ambient-pre-tool.mjs",Stop:"cachly-ambient-stop.mjs"},q=".claude/hooks/cachly-ambient-";function z(t,o){const e=t.hooks??[];return e.length>0&&e.every(r=>{const n=r.command??"";return n.includes(q)||o.has(n)||[...o].some(i=>n.includes(`"${i}"`))})}function X(t,o){const e={...t,hooks:{...t.hooks??{}}},r=e.hooks,n=$(o),i=new Set([o.sessionStart,o.userPromptSubmit,o.preToolUse,o.stop].filter(a=>!!a));for(const[a,c]of Object.entries(n)){const p=(Array.isArray(r[a])?r[a]:[]).filter(f=>!z(f,i));r[a]=[...p,...c]}const u=JSON.stringify(e)!==JSON.stringify(t);return{settings:e,changed:u}}async function nt(t,o,e,r={}){const n=l(t,H);await w(n,{recursive:!0});const i={instanceId:o,apiKey:e},u=["SessionStart","UserPromptSubmit","PreToolUse","Stop"],a={SessionStart:K(i),UserPromptSubmit:F(i),PreToolUse:G(i),Stop:V(i)},c=s=>l(n,R[s]),p=J();if(!p)throw new Error(`cachly hook bundle ${m} missing from the package`);const f=L(p,"utf-8"),h=l(n,m),k=async s=>{try{return await A(s,"utf-8")}catch{return""}};let y=!1,O=await k(h)===f;for(const s of u){const x=await k(c(s));x&&(y=!0),x!==a[s]+`
|
|
3
|
+
`&&(O=!1)}const _=O?"unchanged":y?"upgraded":"written";if(_!=="unchanged"){await U(h,f,"utf-8");for(const s of u)await U(c(s),a[s]+`
|
|
4
|
+
`,"utf-8"),await M(c(s),493).catch(()=>{})}const P=l(t,Q),N=I(P);let g={};if(N)try{g=JSON.parse(await A(P,"utf-8"))}catch{g={}}const d=s=>r.portable?`$CLAUDE_PROJECT_DIR/${H}/${R[s]}`:c(s),{settings:C,changed:v}=X(g,{sessionStart:d("SessionStart"),userPromptSubmit:d("UserPromptSubmit"),preToolUse:d("PreToolUse"),stop:d("Stop")});let S;return N?v?S="merged":S="unchanged":S="written",S!=="unchanged"&&(await w(l(t,".claude"),{recursive:!0}),await U(P,JSON.stringify(C,null,2)+`
|
|
5
|
+
`,"utf-8")),{sessionStartPath:c("SessionStart"),promptSubmitPath:c("UserPromptSubmit"),preToolUsePath:c("PreToolUse"),stopPath:c("Stop"),bundlePath:h,scripts:_,settings:S}}const rt=["SessionStart","UserPromptSubmit","Stop"],E="hooks";function it(){const t=`\${CLAUDE_PLUGIN_ROOT}/${E}/${m}`,o=$({sessionStart:t,userPromptSubmit:t,stop:t},e=>`${e} --plugin`);return JSON.stringify({hooks:o},null,2)+`
|
|
6
|
+
`}export{st as AMBIENT_CLI_SUBCOMMAND,D as AMBIENT_HOOK_VERSION,m as HOOK_BUENDEL,E as PLUGIN_HOOK_DIR,rt as PLUGIN_HOOK_EVENTS,Y as PRE_TOOL_USE_MATCHER,$ as buildAmbientSettingsHooks,it as buildPluginHooksJson,G as buildPreToolUseHook,K as buildSessionStartHook,V as buildStopHook,F as buildUserPromptSubmitHook,J as hookBuendelQuelle,nt as installAmbientHooks,X as mergeAmbientSettings};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import{appendFile as u,readFile as
|
|
2
|
-
`)){const o=
|
|
3
|
-
`,"utf-8");const
|
|
4
|
-
`).filter(Boolean).slice(-
|
|
1
|
+
import{appendFile as u,readFile as a,rename as d,writeFile as p}from"node:fs/promises";import{mkdir as f}from"node:fs/promises";import{homedir as m}from"node:os";import{dirname as l,resolve as y}from"node:path";function c(){return y(m(),".cachly","ambient-ledger.jsonl")}function v(e){return JSON.stringify({ts:e.ts,event:e.event,injected:Math.max(0,Math.round(e.injected)),prevented:Math.max(0,Math.round(e.prevented)),...e.note?{note:e.note.slice(0,200)}:{}})}function h(e){const n=[];for(const r of e.split(`
|
|
2
|
+
`)){const o=r.trim();if(o)try{const t=JSON.parse(o);if(typeof t!="object"||t===null)continue;const i=Number(t.injected),s=Number(t.prevented);if(!Number.isFinite(i)||!Number.isFinite(s))continue;n.push({ts:typeof t.ts=="string"?t.ts:"",event:typeof t.event=="string"?t.event:"unknown",injected:Math.max(0,i),prevented:Math.max(0,s),...typeof t.note=="string"?{note:t.note}:{}})}catch{}}return n}const E=256*1024,j=400;async function L(e,n=c()){try{await f(l(n),{recursive:!0}),await u(n,v(e)+`
|
|
3
|
+
`,"utf-8");const r=await a(n,"utf-8");if(Buffer.byteLength(r,"utf-8")>E){const t=r.split(`
|
|
4
|
+
`).filter(Boolean).slice(-j).join(`
|
|
5
5
|
`)+`
|
|
6
|
-
`,
|
|
6
|
+
`,i=n+".tmp";await p(i,t,"utf-8"),await d(i,n)}}catch{}}async function N(e=c()){try{return h(await a(e,"utf-8"))}catch{return[]}}function M(e,n,r=fetch){return!e.instanceId||!e.jwt||process.env.CACHLY_NO_TELEMETRY==="1"?Promise.resolve():r(`${e.apiUrl}/api/v1/instances/${e.instanceId}/ambient-events`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${e.jwt}`},body:JSON.stringify({events:[n]}),signal:AbortSignal.timeout(3e3)}).then(()=>{},()=>{})}export{L as appendLedgerEntry,c as defaultLedgerPath,h as parseLedger,N as readLedger,M as reportLedgerEntry,v as serializeEntry};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function l(o){return[...o].sort((e,r)=>r-e)}function i(o,e){const r=o.filter(t=>e.has(t.key)).sort((t,c)=>(e.get(t.key)??0)-(e.get(c.key)??0)),s=o.filter(t=>!e.has(t.key)).sort((t,c)=>c.hybridScore-t.hybridScore),u=[];let n=0,h=0;for(;n<r.length||h<s.length;)h<s.length&&(n>=r.length||s[h].hybridScore>r[n].hybridScore)?u.push(s[h++]):u.push(r[n++]);return u}export{i as ordneNachHausordnung,l as rangWerte};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function o(t){const e=t.toLowerCase();return/\b(fix|fixed|fixes|bug|hotfix|patch|revert|resolve|closes? #\d+)\b/.test(e)?{category:"fix",outcome:"success",severity:/\b(critical|crash|security|auth|data loss|outage|prod|production)\b/.test(e)?"critical":/\b(major|breaking|regression|hotfix)\b/.test(e)?"major":"minor"}:/\b(feat|feature|add|added|implement|new|introduce)\b/.test(e)?{category:"feat",outcome:"success",severity:"minor"}:/\b(refactor|clean|cleanup|improve|simplify|extract|rename)\b/.test(e)?{category:"refactor",outcome:"success",severity:"minor"}:/\b(perf|optimize|speed|cache|latency|memory|performance)\b/.test(e)?{category:"perf",outcome:"success",severity:"major"}:/\b(security|cve|auth|csrf|xss|sql|injection|sanitize|escape|encrypt)\b/.test(e)?{category:"security",outcome:"success",severity:"critical"}:/\b(deploy|ci|cd|build|docker|k8s|helm|infra|devops)\b/.test(e)?{category:"deploy",outcome:"success",severity:"major"}:/\b(test|spec|coverage|assert|mock|unit|integration)\b/.test(e)?{category:"test",outcome:"success",severity:"minor"}:{category:"chore",outcome:"success",severity:"minor"}}function c(t){return o(t).category}function i(t){return t.toLowerCase().replace(/[^a-z0-9\s\-_]/g," ").split(/\s+/).filter(s=>s.length>3&&!["that","this","with","from","when","into","also","some","were"].includes(s)).slice(0,3).join("-")||"general"}function n(t){return`${c(t)}:${i(t)}`}function a(t){const e=new Set;for(const r of t)r&&e.add(n(r));return e.size}function u(t){return/^\s*["']?revert\b/i.test(t)}export{o as classifyCommit,c as commitArt,i as extractDomain,u as istRueckbau,n as lektionsThema,a as zaehleLektionen};
|
package/dist/src/credentials.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{existsSync as o,mkdirSync as
|
|
1
|
+
import{existsSync as o,mkdirSync as f,writeFileSync as p,renameSync as l,chmodSync as y,readFileSync as s}from"node:fs";import{resolve as a,dirname as m}from"node:path";function h(){return process.env.HOME??process.env.USERPROFILE??process.env.APPDATA??""}function S(e){return/^\$\{[^}]*\}$/.test(e.trim())}function r(e){if(!(typeof e!="string"||e.length===0))return S(e)?void 0:e}function u(e={}){const n=e.home??h();return a(n,".cachly","credentials.json")}function I(e,n={}){const t=i(n),c=t?.apiKey===e?r(t.instanceId):void 0;d({apiKey:e,...c?{instanceId:c}:{}},n)}function x(e,n){const t=i(n);!r(e)||!t?.apiKey||t.apiKey!==n.apiKey||t.instanceId!==e&&d({...t,instanceId:e},n)}function P(e={}){return r(i(e)?.instanceId)}function i(e){try{const n=u(e);if(!o(n))return null;const t=JSON.parse(s(n,"utf8"));return t&&typeof t=="object"?t:null}catch{return null}}function d(e,n){try{const t=u(n);f(m(t),{recursive:!0});const c=`${t}.${process.pid}-${Date.now()}.tmp`;p(c,JSON.stringify(e,null,2),"utf8");try{y(c,384)}catch{}l(c,t)}catch{}}function v(e){return r(i(e)?.apiKey)}function C(e){try{const n=a(e.cwd??process.cwd(),".mcp.json");if(!o(n))return;const t=JSON.parse(s(n,"utf8"));return r(t.mcpServers?.cachly?.env?.CACHLY_JWT)}catch{return}}function H(e={}){const n=e.env??process.env;return r(n.CACHLY_JWT)??r(n.CACHLY_API_KEY)??v(e)??C(e)}export{u as credentialsPath,S as istPlatzhalter,P as readInstanceId,H as resolveApiKey,I as saveApiKey,x as saveInstanceId};
|
package/dist/src/doctor.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{existsSync as f,readFileSync as p}from"node:fs";import{resolve as
|
|
2
|
-
`)}function
|
|
1
|
+
import{existsSync as f,readFileSync as p}from"node:fs";import{resolve as y}from"node:path";import{AMBIENT_HOOK_VERSION as l}from"./ambient-hooks.js";import{netBalance as g}from"./ambient-recall.js";import{cachlyUrl as v}from"./cachly-url.js";const k=18;function N(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 H(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_"),n=t.split(".").length===3;return!e&&!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:e?"API key (cky_\u2026)":"JWT"}}async function P(t,e=fetch){try{const n=await e(`${t}/health`,{signal:AbortSignal.timeout(5e3)});return n.ok?{name:"API",status:"ok",detail:`${t} reachable`}:{name:"API",status:"fail",detail:`${t}/health returned HTTP ${n.status}`,hint:`The API answered but is unhealthy \u2014 check ${v("/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 R(t,e,n=fetch){if(!e)return{name:"Auth",status:"fail",detail:"skipped \u2014 no credential",hint:"Fix the Credential check first."};try{const r=await n(`${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 S(t,e,n,r=fetch){if(!e||!n)return{name:"Vektoren",status:"warn",detail:"skipped \u2014 no credential or instance"};try{const s=await r(`${t}/api/v1/instances/${n}/vector-coverage`,{headers:{Authorization:`Bearer ${e}`},signal:AbortSignal.timeout(12e3)});if(!s.ok)return{name:"Vektoren",status:"warn",detail:`coverage not measured (HTTP ${s.status})`};const a=await s.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 L(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 F(t){const e={settingsExists:!1,wiredEvents:[],scriptFiles:[]};try{const n=y(t,".claude/settings.json");if(e.settingsExists=f(n),e.settingsExists){const r=JSON.parse(p(n,"utf-8"));for(const[s,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(s);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 $=["SessionStart","UserPromptSubmit","PreToolUse","Stop"];function V(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=$.filter(s=>!t.wiredEvents.includes(s)),n=t.scriptFiles.filter(s=>!s.exists),r=t.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."}: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 j(t,e){const n=g(t);return t.length===0?{name:"Ledger",status:"ok",detail:`empty (${e}) \u2014 fills up once ambient hooks inject`}:{name:"Ledger",status:"ok",detail:`${t.length} entries, net ${n.net>=0?"+":""}${n.net} tokens`}}const A={ok:"\u2705",warn:"\u26A0\uFE0F ",fail:"\u274C"};function O(t){const e=["","\u{1FA7A} cachly doctor",""];for(const s of t)e.push(` ${A[s.status]} ${s.name.padEnd(11)} ${s.detail}`),s.hint&&s.status!=="ok"&&e.push(` \u21B3 ${s.hint}`);const n=t.filter(s=>s.status==="fail").length,r=t.filter(s=>s.status==="warn").length;return e.push(""),e.push(n>0?` ${n} problem${n>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 J(t){return t.some(e=>e.status==="fail")?1:0}export{P as checkApiReachable,R as checkAuthAccepted,H as checkCredential,V as checkHooks,L as checkInstance,j as checkLedger,N as checkNodeVersion,S as checkVectorCoverage,J as doctorExitCode,F as inspectAmbientHooks,O as renderDoctorReport};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import{readFileSync as b,existsSync as I,writeFileSync as v,mkdirSync as P,statSync as D,appendFileSync as J}from"node:fs";import{dirname as w,join as h}from"node:path";import{tmpdir as y}from"node:os";import{createHash as Y}from"node:crypto";import{spawn as q}from"node:child_process";import{Einblendbestand as k,torWoerter as N}from"./einblendung-kern.js";import{rahmeEin as x}from"./einblendung-rahmen.js";import{leserText as z}from"./seltenheitsbestand.js";import{mischeMitLeser as L}from"./leser.js";import{LESER_TIEFE as Z,LESER_ZEITLIMIT_MS as F}from"./rangfolge-stellschrauben.js";export*from"./einblendung-kern.js";const K=600*1e3,T=8e3,W=60*1e3,C=120,j=Z,G=1500,R=F,V=h(y(),"cachly-ambient-netlog.jsonl");function Se(e=process.cwd()){const t={apiUrl:process.env.CACHLY_API_URL||"https://api.cachly.dev",jwt:process.env.CACHLY_JWT||"",instanceId:process.env.CACHLY_BRAIN_INSTANCE_ID||""};if(t.jwt&&t.instanceId)return t;let n=e;for(let s=0;s<8;s++){const o=h(n,".mcp.json");if(I(o))try{const i=JSON.parse(b(o,"utf8"))?.mcpServers?.cachly?.env??{};if(t.apiUrl=t.apiUrl||i.CACHLY_API_URL||"https://api.cachly.dev",t.jwt=t.jwt||i.CACHLY_JWT||"",t.instanceId=t.instanceId||i.CACHLY_BRAIN_INSTANCE_ID||"",t.jwt&&t.instanceId)return t}catch{}const r=w(n);if(r===n)break;n=r}return t}function $(e){return Y("sha1").update(e).digest("hex").slice(0,12)}function X(e){return h(y(),`cachly-ambient-bestand-${$(e)}.json`)}function Q(e){return h(y(),`cachly-ambient-auffrischen-${$(e)}.lock`)}async function M(e,{fetchFn:t=fetch,zeitlimitMs:n=T}={}){try{const s=await t(`${e.apiUrl}/api/v1/instances/${e.instanceId}/export`,{headers:{Authorization:`Bearer ${e.jwt}`},signal:AbortSignal.timeout(n)});if(!s.ok)return null;const o=await s.json();return Array.isArray(o?.lessons)?o.lessons.map(r=>typeof r=="string"?ee(r):r).filter(r=>!!r&&typeof r=="object"):null}catch{return null}}function ee(e){try{return JSON.parse(e)}catch{return null}}function O(e,t){try{P(w(e),{recursive:!0}),v(e,JSON.stringify({at:Date.now(),lessons:t}),{mode:384})}catch{}}function H(e){try{const t=JSON.parse(b(e,"utf8"));if(Array.isArray(t?.lessons))return{at:Number(t.at)||0,lessons:t.lessons}}catch{}return null}function te(e,t){const n=Q(e.instanceId);try{if(I(n)&&Date.now()-D(n).mtimeMs<W)return;v(n,String(Date.now()),{mode:384})}catch{return}try{q(process.execPath,t,{detached:!0,stdio:"ignore",env:{...process.env,CACHLY_API_URL:e.apiUrl,CACHLY_JWT:e.jwt,CACHLY_BRAIN_INSTANCE_ID:e.instanceId}}).unref()}catch{}}async function re(e,{force:t=!1,nebenlauf:n,abrufMs:s=T,fetchFn:o}={}){if(!e.jwt||!e.instanceId)return{lessons:[],quelle:"nicht-eingerichtet"};const r=X(e.instanceId);if(t){const a=await M(e,{fetchFn:o,zeitlimitMs:s});if(a)return O(r,a),{lessons:a,quelle:"server"};const u=H(r);return u?{lessons:u.lessons,quelle:"platte-alt"}:{lessons:[],quelle:"server-nicht-erreichbar"}}const i=H(r);if(i){const a=Date.now()-i.at<K;return!a&&n&&te(e,n),{lessons:i.lessons,quelle:a?"platte":"platte-alt"}}const l=await M(e,{fetchFn:o,zeitlimitMs:s});return l?(O(r,l),{lessons:l,quelle:"server"}):{lessons:[],quelle:"server-nicht-erreichbar"}}function _(e){return Math.ceil(String(e).length/4)}function S(e){const t=e.severity?`[${e.severity}] `:"",n=String(e.what_worked??"").replace(/\s+/g," ").trim(),s=n.length>C?`${n.slice(0,C)}\u2026`:n;return`- ${t}${e.topic}: ${s}`}const m={lessons:[],tokens:0,topScore:0,belege:0};function U(e,t,n){const s=[];let o=0;for(const{lektion:r}of e.slice(0,t)){const i=_(S(r));if(o+i>n)break;s.push(r),o+=i}return{lessons:s,tokens:o,topScore:Math.round(e[0].punkte*100)/100,belege:e[0].belege}}function Ee(e,t,{minBelege:n=2,topK:s=3,tokenBudget:o=600}={}){if(!Array.isArray(t)||t.length===0)return{...m};if(N(e).length===0)return{...m};const r=new k(t).sortiere(e).filter(i=>i.belege>=n);return r.length===0?{...m}:U(r,s,o)}function ne(e){return z(e,G)}async function se(e,t,n,{zeitlimitMs:s=R,fetchFn:o=fetch}={}){if(!e?.jwt||!e?.instanceId||n.length===0||["0","off","aus","false"].includes(String(process.env.CACHLY_LESER||"").toLowerCase()))return null;try{const r=await o(`${e.apiUrl}/api/v1/rerank`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${e.jwt}`},body:JSON.stringify({query:t,texts:n,instance_id:e.instanceId}),signal:AbortSignal.timeout(s)});if(!r.ok)return null;const i=await r.json(),l=Array.isArray(i.scores)?i.scores:null;return!l||l.length!==n.length||!l.every(a=>typeof a=="number"&&Number.isFinite(a))?null:{scores:l,provider:typeof i.provider=="string"?i.provider:"?",ms:Number(i.ms)||0}}catch{return null}}async function oe(e,t,n,{minBelege:s=2,topK:o=3,tokenBudget:r=600,tiefe:i=j,zeitlimitMs:l=R,fetchFn:a}={}){const u={...m,leser:"aus",leserMs:0};if(!Array.isArray(t)||t.length===0||N(e).length===0)return u;let c=new k(t).sortiere(e).filter(p=>p.belege>=s);if(c.length===0)return u;let E="aus",A=0;if(c.length>1&&i>0){const p=c.slice(0,i),B=c.slice(i),f=await se(n,e,p.map(g=>ne(g.lektion)),{zeitlimitMs:l,fetchFn:a});f&&(c=[...L(p.map(d=>d.punkte),f.scores,1).map(d=>p[d]),...B],E=f.provider,A=f.ms)}return{...U(c,o,r),leser:E,leserMs:A}}function ie(e){if(!e.lessons.length)return"";const t=e.lessons.map(S).join(`
|
|
2
|
+
`);return`\u{1F9E0} Cachly Brain \u2014 relevant lessons for this task (auto-recall, ${e.lessons.length}):
|
|
3
|
+
${x(t)}
|
|
4
|
+
(ambient-recall \xB7 ~${e.tokens} tokens \xB7 match score ${e.topScore})`}const ae=5;function Ae(e){if(!e.length)return"";const t=e.filter(o=>{const r=String(o.topic??"");return!(/^auto:/.test(r)||/^[a-z]+:?$/.test(r))}),s=[...t.length?t:e].sort((o,r)=>(Number(r.recall_count)||0)-(Number(o.recall_count)||0)).slice(0,ae).map(S).join(`
|
|
5
|
+
`);return`\u{1F9E0} Cachly Brain \u2014 ambient recall active (${e.length} lessons). Top by proven reuse:
|
|
6
|
+
${x(s)}
|
|
7
|
+
(Relevant lessons for each task are auto-injected per prompt. ~${_(s)} tokens.)`}function ge(e,t){return t?JSON.stringify({hookSpecificOutput:{hookEventName:e,additionalContext:t}}):""}async function be(e,t,{nebenlauf:n,abrufMs:s,leser:o=!0,session:r=null,protokoll:i=!0,fetchFn:l}={}){const{lessons:a,quelle:u}=await re(t,{nebenlauf:n,abrufMs:s,fetchFn:l}),c=await oe(e,a,t,{tiefe:o?j:0,fetchFn:l});if(i)try{J(V,JSON.stringify({at:Date.now(),session:r,prompt_tokens:_(e),injected_tokens:c.tokens,matches:c.lessons.length,top_score:c.topScore,belege:c.belege,leser:c.leser,leser_ms:c.leserMs,quelle:u,bestand:a.length})+`
|
|
8
|
+
`)}catch{}return{kontext:ie(c),auswahl:c,quelle:u,bestand:a.length}}export{T as ABRUF_TIMEOUT_MS,C as ANZEIGE_ZEICHEN,K as BESTAND_TTL_MS,R as LESER_HOOK_ZEITLIMIT_MS,G as LESER_MAX_ZEICHEN,j as LESER_TIEFE,V as NETZ_PROTOKOLL,ae as SITZUNG_TOP_N,X as bestandPfad,_ as estimateTokens,re as holeBestand,ge as hookAusgabe,se as leserPunkteApi,ne as leserText,L as mischeMitLeser,be as promptEinblendung,ie as promptKontext,S as renderLesson,Se as resolveConfig,Ee as selectRelevant,oe as selectRelevantMitLeser,Ae as sitzungsKontext,te as starteNebenlauf};
|