@cachly-dev/mcp-server 0.10.148 → 0.10.149

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,5 +1,5 @@
1
1
  import{execSync as Xe}from"node:child_process";import{createRequire as Qe}from"node:module";import{calculateConfidence as Ae,confidenceBadge as Ct,STRUCTURED_TEMPLATES as xt,CONFIDENCE_WARN_VALUE as et,CONFIDENCE_STALE_VALUE as Tt,CONFIDENCE_WARN_DAYS as Nt}from"../confidence.js";import{ckgSlug as he,extractProblemConcept as Oe,ckgUpsertNode as Ce,ckgUpdateEdge as fe,ckgUpsertPersonNode as Et,ckgUpsertFileNode as tt,ckgRecordCollaboration as At,ckgUpsertServiceNode as Mt}from"../ckg.js";import{safeJsonParse as j,scanKeys as we,leseOderNull as $e,darfHeraus as xe}from"../utils.js";import{ersparteMinuten as st,istStarterLektion as Me,zaehltAlsErsparnis as nt}from"../wertbeitrag.js";import{lessonPreviewLines as Te,trimTo as It}from"../lesson-preview.js";import{normalisiereZeit as Pe,zaehleZeitangaben as je}from"../zeit-normalisieren.js";import{abstentionSatz as Rt,alterssatzFuerBelegte as Dt,beurteileTreffer as Bt,istBelegt as Lt}from"../abstention.js";import{meldeEinmal as ze,meldeUndVermerke as Ie,fehlerText as Fe,OHNE_VEKTOREN as Ot,OHNE_DIENST as Pt,OHNE_FRAGEVEKTOR as jt,SINNPFAD_ABBRUCH as zt,OHNE_SCHREIBVEKTOR as We}from"../aussetzer.js";import{versuchStart as Ft,neueKennung as Wt,wendeZuteilungAn as Kt,schliesseVersuchAb as Gt}from"../versuch.js";function ot(ie,Z,Q){if(ie.includes("lesson:best:")){const p=j(Z,{});if(p&&typeof p=="object"&&(p.what_worked||p.what_failed||p.commands?.length))return Te(p,{maxChars:Q,indent:" "})}const de=It(Z,Q);return de?[de]:[]}import{getRole as Ke,ROLE_BADGE as Ht,getScopes as at,lessonVisibleToScope as it,reviewModeEnabled as Jt,storeLessonProposal as qt}from"./team.js";import{keywordSearch as Re,wortindexEntwerten as Ut,tokenize as Ge,splitMultiQuery as Vt,levenshtein as Yt,indexVocab as rt}from"../search.js";import{rerankByQuality as Zt,qualityMultiplier as Xt,extractLessonQuality as Qt}from"../rerank.js";import{computeEmbedding as _e,hasEmbedProvider as Ne,EMBED_PROVIDER as ve}from"../embeddings.js";import{repariereFelder as es}from"../feldreparatur.js";import{VEKTOR_PRAEFIX as ct,NAME_VEKTOR_PRAEFIX as lt,ZWEIT_VEKTOR_PRAEFIX as He,packe as Ee,textFuerVektor as De,textFuerNamensVektor as ts,Vektorbestand as Je}from"../bedeutung.js";import{schreibeEingaenge as ss,Eingangsbestand as ns}from"../eingaenge.js";import{Seltenheitsbestand as os}from"../seltenheitsbestand.js";import{spurLegen as as}from"../spuren.js";import{schlageErsetzungVor as is}from"../ersetzung-vorschlag.js";const ke="cachly:vek:nachtrag",rs=365;import{etikettenFuer as cs,lieferJournalSchluessel as ht,JOURNAL_DECKEL as ls}from"../etiketten.js";import{klassifiziereFehlschlag as hs,suchProtokollSchluessel as ut,trichterSchluessel as dt,SUCH_DECKEL as us}from"../recall-trichter.js";import{leiteEntscheidungAb as ds,entscheidungsZeile as ft}from"../entscheidung-ableiten.js";import{bewerteTopf as fs,spreizeImTopf as pt,reichereAn as ps,inhaltsWoerter as mt,grobStamm as ms,GEWICHTE as gs,knapperSiegSatz as ys}from"../rangfolge.js";import{pruefeWirkung as ws,werteAus as _s,lehrwert as bs,leiteAb as $s}from"../wirkungsspur.js";import{SINN_TOPF as vs,EINGANG_SCHWELLE as ks,EINGANG_SORTIER_GEWICHT as Ss,ZWEIT_MODELL as qe,ZWEIT_GEWICHT as Cs,ZWEIT_MINDESTDECKUNG as xs,NAHDUPLIKAT_SCHWELLE as Ts}from"../rangfolge-stellschrauben.js";const me=new Je,Ue=new Je(6e4,lt),Ve=new Je(6e4,He),Ye=new ns,Be=new os,Ns=vs,Es=ks,As=Ss;import{upgradeNudge as Ms}from"../upgrade-nudge.js";import{recallTiefe as gt,TIEFE_VOLL as Is,TIEFE_VOLL_MEHRTHEMIG as Rs}from"../recall-tiefe.js";import{nutzungInWorten as Ds,nutzungsSchluessel as Bs,verdichte as Ls}from"../werkzeug-nutzung.js";import{inWorten as Os,lieferBild as Ps,lieferSchluessel as js,trefferSchluessel as zs}from"../geliefert.js";import{COAUTHOR_PREF_KEY as Fs,autorHinweis as Ws,coautorAus as Ks,echterGitLeser as Gs,ermittleAutor as Hs,mitCoautor as Js,nurMensch as yt}from"../autor.js";import{TOOLS as qs}from"../tools.js";import{vorspannHinweis as Us}from"../vorspann.js";import{autorAbzeichen as Vs,fremdanteil as Ys}from"../autor-abzeichen.js";import{cachlyUrl as Zs}from"../cachly-url.js";const Le=(()=>{try{const ie=Qe(import.meta.url);for(const Z of["../../../package.json","../../package.json"])try{const Q=ie(Z);if(Q.name==="@cachly-dev/mcp-server"&&Q.version)return Q.version}catch{}}catch{}return"0.10.100"})(),Xs=()=>{try{return Qe(import.meta.url)("../../../CACHLY_CAPABILITIES.json").mcp?.total_tools??null}catch{}return null},Qs={"0.10.119":["\u{1F50D} **Explainable briefings \u2014 provenance in every warning**"," \u{1F4CE} `/briefing` warnings now carry `outcome`, `author`, `learned_at` and `matched_on` (the context tokens that triggered the warning) \u2014 editors can show WHY a fix hint fired, not just what it claims.",' \u{1F9F0} VS Code 0.12.0 uses this: severity + confidence in the toast, full lesson card with provenance, "Not helpful" per-file suppression, reviewable CLS auto-learns.'," \u{1F9F9} CI docs now describe exactly what ships: scan mode uses the REST `/scan` endpoint, the GitLab template covers learn/scan/confirm."],"0.10.115":["\u{1F527} **`brain_get_pref` + `brain_set_pref` \u2014 persistent Brain preferences**"," \u{1F4D6} `brain_get_pref` reads back any stored preference (omit key to list all)."," \u270F\uFE0F `brain_set_pref` saves a preference to Redis \u2014 survives restarts, scoped per Brain instance.",' \u{1F4A1} Known key: `auto_changelog` \u2014 set to "false" to silence the new-lessons digest at session_start.'," \u{1F4CA} 140 MCP tools"],"0.10.114":["\u{1F4CB} **`brain_changelog` \u2014 weekly knowledge digest in one call**"," \u{1F4C5} Generates a grouped Markdown changelog of lessons learned in the last N days."," \u{1F465} Grouped by topic category, annotated with author, recall count, and confidence \u2014 paste directly into standup or Slack."," \u2699\uFE0F Params: `days` (default 7), `max_lessons` (default 30), `include_failures` (default true)."," \u{1F4CA} 138 MCP tools"],"0.10.113":["\u{1F4C8} **Week-over-week savings trend in `cache_stats`**"," \u{1F4C5} `cache_stats` now shows **7-day hits** + **WoW trend %** (e.g. `+23.0% vs prev week`) so ROI momentum is visible, not just the cumulative total."," \u2B07\uFE0F Goes red (\u{1F4C9}) on drops, green (\u{1F4C8}) on growth \u2014 first week always shows +100% (new)."," \u{1F527} Backed by new `CountHitsInWindow` repo method + `hits_last_7d`, `hits_prev_7d`, `week_over_week_pct` fields in `SavingsEstimate`."," \u{1F4CA} 137 MCP tools"],"0.10.112":["\u2699\uFE0F **Configurable cost_per_call_usd \u2014 accurate ROI for any LLM**"," \u{1F4A1} New `set_cost_per_call` tool: set your real per-call cost (claude-opus-4.8 \u2192 $0.02, gpt-5.5 \u2192 $0.015, claude-sonnet-4.6 \u2192 $0.009, claude-haiku-4.5 \u2192 $0.001) so savings estimates in `cache_stats` reflect your actual bill."," \u{1F527} New API: `PUT /api/v1/instances/:id/cost-per-call` \u2014 persisted per instance, used by `cache_stats`, org savings, and SSE analytics stream."," \u{1F4CA} 137 MCP tools"],"0.10.111":["\u{1F3E2} **Org-wide ROI + GitLab CI \u2014 the CFO number, aggregated**"," \u{1F4B0} `cache_org_stats` now shows **org-wide Tokenmaxxing ROI**: total savings summed across every instance in your org, monthly projection, top-5 instances by savings. One number for the whole team."," \u{1F50C} Backed by new API endpoint `GET /api/v1/orgs/:id/savings` (org members only)"," \u{1F98A} GitLab CI integration: `brain-from-ci-gitlab.yml` \u2014 two `.post` jobs (on_success/on_failure), same helper script, pipeline never fails because of Brain push"," \u{1F6E0}\uFE0F GitHub Action template fixed: `secrets.*` in job-level `if` is invalid \u2014 guard removed (helper script skips cleanly on missing secrets)"," \u{1F4CA} 136 MCP tools"],"0.10.110":["\u{1F3E2} **Zero friction: ROI-Projektor, Org-Shared-Cache, CI ready-to-paste**"," \u{1F4C8} `cache_stats` \u2014 zero-hit projection calculator: shows expected monthly savings for 5/10/20-dev teams before a single cache hit lands. ROI visible from day 1."," \u{1F3E2} New `cache_org_stats` \u2014 shared org-level cache stats across multiple instances. Convention-based (`org:{id}:sem`), zero API changes needed."," \u{1F517} `cache_set`/`cache_get` now accept optional `org_id` \u2014 write to personal + org namespace, fall back to org on miss. Fully backwards-compatible."," \u{1F4CB} Copy-paste CI integration: `brain-from-ci-action.yml` \u2192 Brain self-calibrates confidence on every GitHub Actions run. No config beyond two repo secrets."," \u{1F4CA} 136 MCP tools"],"0.10.109":["\u{1F4B0} **Tokenmaxxing ROI + Closed-loop CI learning**"," \u{1F4CA} `cache_stats` now shows a **Tokenmaxxing ROI** section \u2014 total tokens saved, monthly cost projection, and top 3 most-cached prompts. Finally a number you can show the CFO."," \u{1F504} New `brain_from_ci` \u2014 bulk-ingest CI history into the Brain (red\u2192green = learned fix, green\u2192red = CKG causal edge). The brain_from_git equivalent for CI. Bootstrap the Brain from your CI logs in one call."," \u{1F4CA} 134 MCP tools"],"0.10.108":["\u{1F9E0} **Focused briefing + trustworthy autopilot**"," \u{1F3AF} New `session_start_summary` \u2014 top-N focused briefing by topic. Scores lessons by relevance \xD7 confidence + recall_count + severity + recency. Use it for a quick pre-brief without loading the full Brain.",' \u{1F91D} Autopilot template now explains the data flow upfront ("your own Brain, EU-hosted, no third parties") \u2014 no more friction with cautious AI assistants during onboarding'," \u{1F4CA} 132 MCP tools"],"0.10.107":["\u{1F3AF} **Recall@3 gap closed \u2014 the moat proof got sharper (the head-to-head that matters)**"," \u{1F50D} External-corpus Recall@3 **82.1% \u2192 98.2%** \xB7 Precision@1 **80.4%** \xB7 MRR **89.0%** \u2014 now ahead of flat-file memory on every metric"," \u{1F9EC} Root-cause fix: documents are no longer indexed with cross-lingual synonyms (the symmetric-expansion bug inflated any doc containing a common word ~28\xD7, burying topic-specific lessons). Queries still expand \u2014 a \u65E5\u672C\u8A9E query still finds English lessons."," \u2696\uFE0F Reranker now compresses BM25 with score^0.3 before applying lesson quality, so a proven success can overtake a text-similar failed attempt even when the distractor scores 5\u20136\xD7 higher in raw BM25"," \u{1F6E1}\uFE0F CI bench gate widened to both home + external corpora with committed floors (`npm run bench:gate`) \u2014 recall quality can never silently regress again"," \u{1F4CA} 121 MCP tools"],"0.10.98":['\u{1F310} **Model-Neutrality as Feature (W9) \u2014 "Bring your own model, keep your brain."**'," \u{1F50C} New `brain_portability()` \u2014 shows your Brain ID, all compatible MCP clients, and ready-to-paste config snippets for each; proves the same Brain works in Claude, Cursor, Copilot, Windsurf, Cline, Zed and more"," \u{1F9E0} Your lessons, crystals, and predictions are model-agnostic \u2014 no vendor lock-in at the memory layer",' \u{1F4D6} New docs: cachly.dev/docs/model-neutral \u2014 "Same Brain, Any Model" guide with live config examples'," \u{1F4CA} 121 MCP tools (new: `brain_portability`)"],"0.10.97":['\u{1F4A0} **Team Crystallize (W8) \u2014 the team-wide, causal counter to per-user "Dreaming"**'," \u{1F9EC} New `team_crystallize()` \u2014 surfaces fixes that 2+ teammates *independently converged on*, the one thing a single-user memory structurally can't build",' \u{1F9E9} Cross-person clustering: groups structurally similar problems across people & namespaces (e.g. "pool exhaustion" solved the same way in payments, auth & db)',' \u{1F465} Each pattern names who converged \u2014 instant "ask @alice and @bob, they both hit this"'," \u{1F48E} Shows up in `crystal_view` alongside your per-brain crystal"," \u{1F4CA} 120 MCP tools (new: `team_crystallize`)"],"0.10.96":["\u{1F510} **Roles & Team-Scopes now ENFORCED (W6) \u2014 the enterprise governance layer**"," \u{1F6E1}\uFE0F `team_confirm` is now reviewer-gated, `team_learn` blocks viewers (recall-only) \u2014 roles stop being cosmetic the moment an admin is assigned"," \u{1F4DC} New `team_audit` tool \u2014 immutable, admin-only trail of every role change & lesson confirmation, for compliance & security reviews"," \u{1F513} Zero impact on open teams: governance activates only once you bootstrap an admin; before that everything works exactly as before"," \u{1F4CA} 119 MCP tools (new: `team_audit`)"],"0.10.95":["\u{1F9E0} **Domain Brain Marketplace (W10) \u2014 bootstrap your Brain with curated community knowledge**"," \u{1F6D2} `brain_marketplace()` \u2014 browse installable packs of high-trust lessons by domain: \u2638\uFE0F Kubernetes, \u{1F510} Auth, \u{1F5C4}\uFE0F Database, \u269B\uFE0F React, \u{1F4B3} Payments & more",' \u{1F4E6} `brain_install(slug="k8s")` \u2014 merges a domain\'s verified lessons into your Brain; live in `smart_recall` instantly, even offline',' \u{1F6E1}\uFE0F Idempotent + non-destructive: installed lessons are tagged `source:"marketplace:<slug>"` and NEVER override your own'," \u{1F50C} New gRPC `Subscribe` RPC \u2014 agents get new lessons streamed live the moment they're learned (completes the M2M loop with `Learn`)"," \u{1F4CA} 118 MCP tools (new: `brain_marketplace`, `brain_install`)"],"0.10.94":['\u{1F91D} **Person\u2194Person Collaboration Graph (W5) \u2014 "Frag X und Y, die haben das zusammen gel\xF6st"**'," \u{1F578}\uFE0F New `brain_collab_pairs()` tool: shows every contributor pair who've touched the same files or recalled each other's lessons \u2014 ideal for onboarding and bus-factor analysis"," \u{1F4E1} `smart_recall` now wires CKG `collaborates` edges live when cross-author reuse fires \u2014 collaboration graph grows with every team recall"," \u26A0\uFE0F Bus-factor section: flags solo contributors whose knowledge no teammate has yet recalled"," \u{1F4CA} 116 MCP tools (new: `brain_collab_pairs`)"],"0.10.93":['\u{1F5FA}\uFE0F **`brain_plan` \u2014 your Brain goes from "what did we learn?" to "what should I do?"**',` \u{1F9ED} Give it a task you're about to start ("upgrade Postgres 14\u219216") and it returns an ordered, grounded action plan: ranked failure modes to avoid, proven fix steps (dependency-aware, with commands), and a pre-flight checklist \u2014 all from your own lessons`,' \u{1F52C} Generative layer on the CKG: where `brain_predict` answers "what might fail?", `brain_plan` answers "what, in what order?"'," \u{1F4CA} 115 MCP tools (new: `brain_plan`)"],"0.10.92":["\u{1F44B} **Welcome-back digest \u2014 re-entry after a week away now leads with what changed**"," \u{1F331} `session_start` detects a gap \u2265 7 days and surfaces how many lessons your Brain gained while you were gone (git, CI & teammates kept it learning), with the freshest topics"," \u{1F9E0} Zero extra latency \u2014 the digest is computed from data session_start already loads"," \u{1F50C} New gRPC `Learn` RPC (M2M write path) \u2014 agents & CI can now teach the Brain directly, byte-compatible with the MCP lesson format"," \u{1F4CA} 115 MCP tools"],"0.10.91":["\u{1F550} **Lesson-anchored time attribution \u2014 every recall shows when it was learned and how much it saves**",` \u{1F4A1} "Brain saved you" banner now includes: when the lesson was learned (e.g. "12 May"), estimated original debug cost by severity (critical=2h, major=1h, minor=20min), and team attribution when a teammate's lesson fires`,' \u{1F4C5} Origin date formatted as "12 May" (same year) or "12 May 2024" (different year) \u2014 no clutter',' \u{1F465} Cross-author recalls now show "@author" on the same banner line \u2014 the "team knowledge reuse" value is front-and-center'," \u{1F4CA} 115 MCP tools"],"0.10.90":["\u{1F4CA} **External benchmark corpus v2 \u2014 the proof is now statistically solid**"," \u{1F9EA} Expanded from 8 queries \u2192 61 lessons / 56 queries across 10 real-world engineering domains (k8s, DB, auth, CI, frontend, API, payments, observability, network, Node.js, security, infra)"," \u2694\uFE0F 10 adversarial distractors added \u2014 failure lessons that share the exact same vocabulary as the query, forcing the quality reranker to earn its lift"," \u{1F4C8} Results on the larger corpus: cachly +2.9% Precision@1 vs flat-file, +12.5% vs raw BM25 baseline \u2014 on a harder dataset, not an easy one"," \u{1F4CA} 115 MCP tools"],"0.10.89":["\u26A1 **One-command onboarding \u2014 `autopilot` is now the single canonical setup**"," \u{1F680} Every surface (README, docs, emails, dashboard, blog) now leads with `npx @cachly-dev/mcp-server@latest autopilot` \u2014 one command signs you in, configures every editor, and bootstraps from git",' \u{1F9F9} Retired the split-brain instructions (legacy `@cachly-dev/init` wrapper + the old "three steps") so there\'s exactly one thing to copy-paste'," \u{1F527} Fixed a build-breaking duplicate `export` on buildClaudeMdBlock that failed CI on v0.10.88"," \u{1F522} Version hygiene \u2014 package.json, server.json, lockfile and MCP_VERSION resynced (server.json/lockfile were stuck at 0.10.85)"," \u{1F4CA} 115 MCP tools"],"0.10.88":["\u2705 **Honest setup + tested config writer (v0.10.88)**",' \u{1F512} setup/autopilot now report the truth: if no editor config could be written it exits non-zero with the exact failures instead of falsely claiming "Brain is ready"'," \u{1F4CA} New setup_config_write_failed telemetry \u2014 permission/path failures that block activation are now visible in reporting"," \u{1F9EA} 24 new unit tests for the config writer (buildServerEnv, buildMcpConfig, mergeMcpConfig, buildClaudeMdBlock) \u2014 the merge logic that must never clobber your other MCP servers is now regression-guarded"," \u{1F4CA} 115 MCP tools"],"0.10.87":["\u{1F310} **Multi-editor auth persistence + full activation telemetry (v0.10.87)**"," \u2705 JWT + instance_id now persisted to Cursor (`~/.cursor/mcp.json`) and Windsurf configs on auth \u2014 no more infinite re-auth for non-Claude-Code users",' \u{1F4E1} `device_flow_failed(reason="timeout")` now fires when sign-in window expires \u2014 blind spot in the funnel is gone'," \u{1F4E1} `auto_provision_failed` now fires on network errors too (was only HTTP non-2xx)"," \u{1F4E1} `brain_from_git_failed` fires with error reason when auto-bootstrap throws \u2014 no more empty brain with no explanation",' \u2728 Git bootstrap shows lesson count in session_start: "Brain bootstrapped \u2014 23 lessons loaded" \u2014 the WOW moment'," \u{1F527} API: McpEvent stores brain quality metrics (fixes/features/refactors/total) \u2014 git learning quality now measurable"," \u{1F4CA} 115 MCP tools"],"0.10.86":["\u{1F6E1}\uFE0F **Zero-friction activation \u2014 your Brain self-heals into existence**"," \u{1F527} Auto-provision now runs on *any* tool call, not just at sign-in \u2014 if instance creation ever fails once, the next call recovers it automatically"," \u{1F91D} Parallel tool calls on startup are coalesced into one resolve (no more duplicate-instance races)",' \u{1F4AC} Clearer "instance still starting" message \u2014 leads with "just retry", no manual config needed'," \u{1F4C8} New activation-funnel telemetry: install \u2192 sign-in \u2192 provision, so silent churn is visible"," \u{1F4CA} 115 MCP tools"],"0.10.85":["\u{1F680} **One-command onboarding \u2014 `npx @cachly-dev/mcp-server@latest autopilot`**"," \u26A1 Single command does it all: auth \u2192 instance \u2192 every editor config \u2192 CLAUDE.md \u2192 Brain bootstrap \u2192 health"," \u{1F331} Seeds the 16-lesson starter corpus when git history is empty \u2014 your first smart_recall hits instantly"," \u{1F916} Fully automatic (zero prompts); `setup` remains for interactive, pick-your-editors onboarding"],"0.10.84":["\u{1F916} **M2M & agent-ecosystem reach \u2014 cachly for every caller, human or machine**"," \u{1F511} OAuth2 `client_credentials` grant \u2014 set CACHLY_CLIENT_ID + CACHLY_CLIENT_SECRET for fully headless auth (CI, agents, AI-to-AI)"," \u{1F5C2}\uFE0F `npx ... tool-specs --format=openai|anthropic|langchain` \u2014 export all 121 tools in any framework's dialect"," \u{1F310} `npx ... openapi` \u2014 OpenAPI 3.1 doc (1 POST path per tool) for Assistants / codegen / Postman"," \u{1F9EA} 14 new tests: client_credentials helpers, all four spec dialects, OpenAPI required-body inference"],"0.10.83":["\u{1F9E0} **Brain Viz \u2014 3D graph export**"," \u{1F310} `brain_graph(instance_id)` \u2014 exports the Causal Knowledge Graph as a render-ready {nodes, links} payload"," \u{1F3A8} Schema `cachly.brain_graph/v1`: node kinds (concept/person/file/service) with color groups + size, edges with confidence"," \u{1F5A5}\uFE0F Consumed verbatim by the 3D frontend (react-force-graph-3d / three.js) \u2014 the visual brain map",' \u{1F50E} Filters: `domain`, `min_confidence`, `max_nodes` (with truncation flag), `format="summary"`'," \u{1F9EA} 10 tests: kind detection, dangling-edge pruning, confidence/domain filters, schema contract"," \u{1F4CA} 115 MCP tools"],"0.10.82":["\u{1F4E1} **Telemetry API contracts \u2014 dashboard ingest pipeline prepared**"," \u{1F5C2}\uFE0F `src/telemetry-types.ts` \u2014 canonical event types + resolved event format (TelemetryEventRaw / TelemetryEventResolved)"," \u{1F510} `user_fingerprint` \u2014 non-reversible JWT sub hash, PII-free user clustering for dashboard"," \u{1F4CA} `metrics` block on key events: smart_recall (hit/topic), brain_from_git (fixes/features/total), brain_seed_starter (seeded_count)"," \u{1F6A6} Internal API contract documented: GET /internal/telemetry/stream \u2192 TelemetryEventResolved[]"],"0.10.81":["\u23F1\uFE0F **Onboarding-Bench \u2014 time-to-first-recall, now measured**"," \u{1F4D0} `npm run bench:onboarding` \u2014 cold (empty) vs seeded (starter) first-query hit rate via the real search engine"," \u{1F4CA} Result: first-query hit@1 **0% \u2192 87.5%**, hit@3 **0% \u2192 100%**, MRR **0% \u2192 93.8%**"," \u{1F6E1}\uFE0F CI-defended in `onboarding-bench.test.ts` (cold must answer 0%, seeded thresholds enforced)"," \u{1F4CB} `PLAN-DASHBOARD.md` \u2014 full build spec for the external Team-Knowledge-Reuse dashboard (separate repo)"],"0.10.80":["\u{1F331} **Starter corpus \u2014 first recall in seconds, not sessions**"," \u{1F4DA} `brain_seed_starter(instance_id)` \u2014 seed 16 curated universal lessons (Docker cache, JWT skew, K8s OOM, N+1, cache stampede\u2026)"," \u{1F680} Auto-seeds on first session when git history is empty/shallow \u2014 your first `smart_recall` returns a real hit",' \u{1F6E1}\uFE0F Tagged `source:"starter"`, idempotent, never overrides your own lessons'," \u23F1\uFE0F Directly attacks time-to-first-recall (<2 min goal); stamps `born_at` on seed"," \u{1F9EA} 18 new tests: seeding, idempotency, topic_filter, force, user-lesson protection"," \u{1F4CA} 113 MCP tools"],"0.10.79":["\u{1F310} **Brain Marketplace \u2014 full shareable Brain lifecycle**"," \u{1F4CB} `brain_share_list(instance_id)` \u2014 see all your public shares with lesson counts + share IDs"," \u{1F5D1}\uFE0F `brain_unshare(share_id)` \u2014 revoke a share; link goes dead immediately"," \u{1F50D} `brain_discover(query, topic)` \u2014 search public Brains in the cachly marketplace"," \u{1F4E4} `publish` CLI \u2014 fancy card + share URL; `--public` for discoverable; `--title` custom name"," \u{1F9EA} stress tests: team_expertise_map with 50 contributors, chain import/unshare, discover fallback"," \u{1F4CA} 112 MCP tools"],"0.10.78":["\u{1F310} **Phase 3: Shareable / Public Brains**",' \u{1F4E4} `brain_share(instance_id="...")` \u2014 export a Brain snapshot as a shareable link (public or unlisted)',' \u{1F4E5} `brain_import(instance_id="...", share_id="...")` \u2014 import any public Brain into yours (1-line)'," \u{1F50D} Optional topic_filter, min_confidence, topic_prefix \u2014 precise control over what gets shared/imported"," \u{1F512} dry_run mode on both tools \u2014 preview before committing"," \u{1F9EA} Regression suite added: share handler, buildServerEnv, stdout cleanness"," \u{1F4CA} 109 MCP tools"],"0.10.77":["\u{1FA79} **Frictionless onboarding + first-class self-hosting/BYOK**"," \u{1F680} Editor-launched server now serves all tools even before sign-in \u2014 zero-credential device flow finally triggers on first tool call"," \u{1F512} No more stdout pollution: stdio JSON-RPC stream stays clean when no JWT is set"," \u{1F3E0} `setup --api-url` / `init --api-url` point at a self-hosted backend; URL baked into config only when non-default",' \u{1F9EC} `health` now shows your BYOK embedding provider + accepts cky_ long-lived keys (was a false "invalid token" failure)'],"0.10.76":["\u{1F4CA} **Agent-trace benchmark + editor matrix + setup timer**"," \u{1F4C2} New `agent-traces-corpus.json` \u2014 22 lessons, 15 queries, realistic AI-agent debug patterns"," \u{1F3C6} Benchmark result: **P@1 +66.7%, MRR +9.5% vs flat-file** (adversarial failure-distractors per category)"," \u{1F4CB} README: first-class editor support matrix (Claude Code, Cursor, Windsurf, VSCode, Cline, Continue.dev, Zed)",' \u23F1\uFE0F setup now shows elapsed time + "Brain is ready" banner at the end'],"0.10.75":["\u{1FA79} **Setup hardened \u2014 works from the VSCode plugin & any non-TTY terminal**"," \u{1F50C} `setup` no longer hangs on a non-interactive stdin (VSCode tasks, CI) \u2014 auto-detects + runs in automatic mode"," \u{1F510} Device-flow now uses the API proxy first (the old direct-Keycloak call returned 403) \u2014 sign-in actually completes"," \u{1F310} When auto sign-in is unavailable, the browser opens with a step-by-step API-key guide instead of a silent paste prompt"," \u{1F4CA} Funnel fix: `device_flow_completed` / `device_flow_failed(reason)` now emitted from setup too"],"0.10.73":["\u{1F510} **Team visibility scopes + one-command init + external bench**",' \u{1F195} `team_grant_scope` / `team_scopes` \u2014 scope lessons to a sub-team (group="security"); only members + admins recall them',' \u{1F3F7}\uFE0F `learn_from_attempts(group="...")` \u2014 orthogonal to private; smart_recall enforces it per requester'," \u26A1 `init` is now zero-arg + idempotent (reuses saved creds, only writes what changed, <60s)"," \u{1F4E6} `npm run bench:external` \u2014 prove recall-lift on a third-party-labeled corpus (portable JSON format)"," \u{1F4CA} 107 MCP tools"],"0.10.72":["\u{1F451} **Role model \u2014 admin \xB7 reviewer \xB7 contributor \xB7 viewer (Phase 3)**"," \u{1F195} `team_assign_role` \u2014 establish governance; first call bootstraps, admins manage the rest"," \u{1F195} `team_whoami` \u2014 see your own role and capabilities"," \u{1F195} `team_roster` \u2014 full team role table (\u{1F451}\u{1F6E1}\uFE0F\u270F\uFE0F\u{1F441}\uFE0F)"," \u{1F6E1}\uFE0F `team_confirm` is now role-aware \u2014 admin/reviewer auto-get senior weight; contributor gets peer; no self-promotion"," \u{1F451} Setup CLI now prompts for governance bootstrap (idempotent). 105 MCP tools"],"0.10.71":['\u{1F6E1}\uFE0F **Self-healing auth** \u2014 no more silent "0 recalls because the token quietly died"'," \u267B\uFE0F Near-expiry tokens auto-refresh into a long-lived key while still valid (zero interaction)"," \u{1F501} A rejected (401) call self-heals once and retries before surfacing an error"," \u{1F4E3} If the credential is truly dead, session_start + get_api_status say so plainly + how to fix"],"0.10.70":["\u{1F6F0}\uFE0F **Service/System nodes in the graph (Phase 3)** \u2014 the brain now models running systems",' \u{1F195} `brain_service_map(service="prometheus")` \u2014 who owns it + every failure/fix known',' \u{1F3F7}\uFE0F Tag lessons with `service="..."` (`service_kind="system"` for infra) \u2014 person\u2192operates, file\u2192runs_in edges',` \u{1F6A8} Incident triage: a restarting pod \u2192 instant "who knows this and what's broken before"`," \u{1F4CA} 102 MCP tools"],"0.10.69":["\u{1F4C1} **Personalized context-aware recall (Phase 3)** \u2014 pass `context_files` to `smart_recall`"," \u{1F3AF} Lessons learned on your current files bubble up \u2014 even when the query doesn't name the file"," \u{1F3F7}\uFE0F `\u{1F4C1} context match` badge on every file-boosted result so you know WHY it ranked up"," \u2705 Works alongside keyword, semantic, CKG, and governance signals \u2014 a 6th ranking dimension"],"0.10.68":["\u{1F91D} **Collaboration graph (Phase 3)** \u2014 person\u2194person edges from shared files"," \u{1F465} `brain_who_knows` now shows who the top expert frequently works with",' \u{1F68C} Bus-factor insight: "ask X and Y together \u2014 they solved this on shared files"'," \u{1F6E1}\uFE0F Stability: every network call in the agent hot path is now timeout-bounded"],"0.10.64":["\u{1F4C8} **The three decisive metrics, now measurable**"," \u23F1\uFE0F `brain_metrics()` \u2014 time-to-first-recall, recall-lift, team-knowledge-reuse in one view"," \u{1F465} Cross-author reuse tracked: recall a teammate's lesson \u2192 counted + surfaced inline"," \u{1F6E1}\uFE0F Stability: scans now capped + timed out (a huge keyspace can't hang the agent)"],"0.10.61":["\u{1F3AF} **Phase 3C: 100 MCP tools milestone \u2014 zero-setup knowledge graph**"," \u{1F504} `brain_from_git` now auto-builds Person+File nodes from git history (zero setup!)"," \u{1F50D} `skill_gaps()` \u2014 find domains with unresolved failures, missing attribution"," \u{1F4CA} `brain_coverage()` \u2014 scored 0-100 health report: lessons \xB7 attribution \xB7 file coverage"],"0.10.60":["\u{1F195} **Phase 3B: File map + team overview + visibility**"," \u{1F4C1} `brain_file_map(file_paths=[...])` \u2014 experts + lessons per file before you touch it"," \u{1F5FA}\uFE0F `team_expertise_map()` \u2014 full team skills matrix in one table",' \u{1F512} `visibility: "private"` on `learn_from_attempts` \u2014 private notes never leak to smart_recall'," \u{1F4CA} 98 MCP tools"],"0.10.59":["\u{1F195} **Phase 3A: Org-wide knowledge graph**",' \u{1F465} `brain_who_knows(topic="...")` \u2014 find your team\'s experts on any topic instantly',' \u{1F578}\uFE0F Person + File nodes auto-built from `learn_from_attempts(author="...", file_paths=[...])`'," \u{1F464} Author attribution now shown inline in `smart_recall` results"," \u{1F4CA} 96 MCP tools \xB7 386 tests green"],"0.10.58":["\u{1F9F9} Zero lint warnings \u2014 all unused imports cleaned across every handler","\u2705 Build: 0 errors, 0 warnings \xB7 379 tests green"],"0.10.57":[`\u{1F195} **What's new in v${Le}:**`," \u{1F527} **Critical fix** \u2014 corrected the npm bin/main entry path (`dist/src/index.js`); 0.10.50\u20130.10.52 shipped a broken entry point"," \u{1F527} Version hygiene \u2014 synced package.json, server.json, lockfile, MCP_VERSION; `latest` now points forward again"," \u2705 Accurate tool count (95) across README, server.json, CLI banners"," \u2705 Includes all Phase 2/3 work: quality reranking, Cachly-Bench, hybrid + CKG recall, team_confirm governance"],"0.10.52":[" \u2705 `smart_recall` \u2014 CKG traversal as Layer 3: finds lessons that FIXED causal-graph-similar problems (\u{1F578}\uFE0F causal graph badge)"," \u2705 Bench corpus expanded: governance adversarial pair proves review-boost end-to-end (+22% P@1 headline)"," \u{1F527} Import cleanup: unused symbols removed from brain.ts (no functional change)"," \u{1F4A1} `smart_recall` now shows keyword / semantic / CKG / hybrid match type for every result"],"0.10.51":[" \u2705 `team_confirm` \u2014 human review raises lesson recall ranking (\u{1F6E1}\uFE0F senior / \u2714\uFE0F peer)"," \u2705 Governance-aware recall \u2014 confirmed lessons outrank unreviewed auto-learned entries"],"0.10.50":[" \u2705 `smart_recall` \u2014 unified keyword + semantic hybrid list (one ranked result, not two sections)",' \u2705 `smart_recall` \u2014 "Brain saved you here" banner surfaces time saved inline per proven lesson'," \u2705 Quality reranking \u2014 proven successes outrank symptom-dense failures (+11% Precision@1)"," \u2705 Cachly-Bench \u2014 IR benchmark with CI regression guard (see BENCH.md for the proof)"," \u{1F527} Contradiction resolution now persisted to Redis (90-day TTL)"," \u{1F4A1} Run `smart_recall` to see your Brain's recall quality in action"],"0.10.49":[" \u2705 `brain_from_git` \u2014 auto-seed your Brain from git history in seconds"," \u2705 `brain_predict_failures` \u2014 predict CI/build failures before they happen"," \u2705 VS Code extension \u2014 Brain status bar, ambient learning, CodeLens hints"," \u2705 Team Brain \u2014 share lessons across your whole engineering team"," \u{1F527} Stability \u2014 auto-expiring memory keys, longer provisioning wait, robust recall"]},zn=new Set(["learn_from_attempts","recall_best_solution","smart_recall","recall_feedback","session_start","session_start_summary","session_end","session_ping","session_handoff","auto_learn_session","brain_who_knows","brain_file_map","team_expertise_map","skill_gaps","brain_coverage","brain_metrics","brain_service_map","brain_collab_pairs","brain_portability","brain_changelog","brain_set_pref","brain_get_pref"]),en=Zs("/billing","upgrade"),wt=new Map,_t=new Set,tn=1680*60*60;function sn(ie=new Date){return`cachly:quota:recalls:${ie.toISOString().slice(0,7)}`}async function bt(ie){try{const Z=sn();await ie.incr(Z)===1&&await ie.expire(Z,tn)}catch{}}function $t(ie){return`cachly:stats:drei-ausgaenge:${ie}`}async function vt(ie,Z,Q){try{await ie.hincrby($t(Z),Q,1)}catch{}}async function kt(ie,Z){const Q=wt.get(ie);if(Q&&Q.expiresAt>Date.now())return Q.gate;try{const de=await Z(`/api/v1/instances/${ie}/memory`),p=de?.recall_limit??-1,t=de?.total_recall_count??0,n={reached:p>0&&t>=p,limit:p,used:t,goodwillMessage:de?.goodwill_message,notice:de?.notice};return wt.set(ie,{gate:n,expiresAt:Date.now()+6e4}),n}catch{return{reached:!1,limit:-1,used:0}}}async function Fn(ie,Z,Q,de){switch(ie){case"learn_from_attempts":{const{instance_id:p,topic:t,outcome:n,what_worked:b,what_failed:E="",context:f="",severity:o="major",file_paths:v=[],commands:l=[],tags:h=[],depends_on:u=[],author:S="",visibility:T="team",service:a="",service_kind:c="service",group:x="",grund:_="",ersetzt:k="",gilt_ab:M=""}=Z,{what_worked:I,what_failed:e="",context:J=""}=es({what_worked:b,what_failed:E,context:f}),$=await Q(p),se=new Date,oe=se.toISOString(),V=Pe(I,se),N=Pe(e,se),P=Pe(J,se),te=je(I,se)+je(e,se)+je(J,se),Y=Hs({uebergeben:S,umgebung:process.env.CACHLY_AUTHOR,git:Gs(process.env.CACHLY_WORKSPACE??process.cwd())});let ee="";try{ee=Ks(await $.get(`cachly:prefs:${Fs}:${p}`))}catch{}const s=Js(Y.autor,ee),m=Ws(Y);if(T!=="private"&&await Jt($,p))return qt($,t,{topic:t,outcome:n,what_worked:V,what_failed:N,context:P,severity:o,file_paths:v,commands:l,tags:h,depends_on:u,...s?{author:s}:{},...a?{service:a}:{},...x?{group:String(x).toLowerCase().trim()}:{},visibility:T,recall_count:0,ts:oe,confidence:1,version:3});const y=t.split(":")[0],i=xt[y],r=[];if(i)for(const R of i.required)R==="commands"&&l.length===0&&r.push(`\u{1F4CB} ${i.hint}`);let d=!1,O=0,D,B=[];const L=await $.get(`cachly:lesson:best:${t}`),ce=String(_??"").trim();if(L&&!ce)return[`\u270B **Update abgewiesen:** \`${t}\` existiert bereits \u2014 eine Aktualisierung braucht das Feld \`grund\`.`,"","Eine Zeile genuegt: WARUM war die bisherige Fassung falsch oder unvollstaendig?",'Beispiel: grund="Die IP stimmte nicht mehr \u2014 node-4 ist seit dem Umzug Peer .7"',"","Der Diff sagt, WAS sich aendert. Die grund-Zeile sagt, warum es vorher falsch war \u2014","und genau sie wird in zwei Monaten gelesen (audit_trail, recall_best_solution)."].filter(Boolean).join(`
2
- `);if(L)try{const R=JSON.parse(L);O=R.recall_count??0,D=R.last_recalled_at,B=R.audit_trail??[],B.push({ts:oe,action:"updated",prev_outcome:R.outcome,grund:ce}),B.length>20&&(B=B.slice(-20)),d=!0;const W=[];if(R.outcome==="success"&&n==="failure"?W.push("\u26A0\uFE0F **Contradiction detected!** Existing lesson has outcome: `success`, but you're storing `failure`.",'The existing "success" lesson will be preserved. Only the audit trail is updated.',"If you meant to mark this as failed permanently, store a new lesson with a distinct topic slug."):R.outcome==="failure"&&n==="success"&&W.push("\u2705 **Conflict resolved!** Previous lesson was `failure` \u2014 now overwriting with `success`."),W.length>0){B[B.length-1].action="contradiction-resolved";try{const ae=he(t),X=he(`resolution:${t}`);await fe($,ae,"contradicts",X,!1)}catch{}try{const ae=`cachly:contradictions:${t}`,X=R.outcome==="success"&&n==="failure"?"kept-success":"overwrote-failure";await $.rpush(ae,JSON.stringify({ts:oe,prev_outcome:R.outcome,new_outcome:n,direction:X})),await $.ltrim(ae,-50,-1),await $.expire(ae,180*86400)}catch{}W.push(`\u{1F5F3}\uFE0F Run \`madc_deliberate(topic="${t}")\` to resolve via expert agent voting.`)}}catch{}else B=[{ts:oe,action:"created"}];const pe=[];if(n==="failure"){const R=[],W=$.scanStream({match:"cachly:lesson:best:*",count:100});await new Promise((X,A)=>{W.on("data",K=>R.push(...K)),W.on("end",X),W.on("error",A)});const ae=t.split(/[:\-_]/).filter(X=>X.length>2);for(const X of R.slice(0,50)){const A=await $.get(X);if(A)try{const K=JSON.parse(A);if(K.outcome!=="success")continue;const ne=(K.topic??"").split(/[:\-_]/).filter(Se=>Se.length>2);if(ae.filter(Se=>ne.includes(Se)).length>=1&&K.topic!==t){pe.push(`\u26A0\uFE0F **"I Was Wrong"**: lesson \`${K.topic}\` (success, ${K.severity??"major"}) might have prevented this failure.`,` \u2192 Use \`recall_best_solution(topic="${K.topic}")\` before next attempt.`,` \u2192 To mark it critical: \`learn_from_attempts(topic="${K.topic}", ..., severity="critical")\``);break}}catch{}}}for(const R of u){const W=`cachly:dep:${R}`,ae=await $.get(W),X=j(ae,[]);X.includes(t)||X.push(t),await $.set(W,JSON.stringify(X),"EX",90*86400)}const w={topic:t,outcome:n,what_worked:V,what_failed:N,context:P,severity:o,file_paths:v,commands:l,tags:h,depends_on:u,...s?{author:s}:{},...a?{service:a}:{},...x?{group:String(x).toLowerCase().trim()}:{},visibility:T,recall_count:O,...D?{last_recalled_at:D}:{},...M&&!Number.isNaN(Date.parse(String(M)))?{gilt_ab:new Date(String(M)).toISOString()}:{},ts:oe,verified_at:n==="success"||n==="partial"?oe:void 0,confidence:1,audit_trail:B,version:3},g=String(k??"").trim();let C="",H=null;g===t&&g?C="\u26A0\uFE0F `ersetzt` zeigt auf dasselbe Thema \u2014 fuer Korrekturen im selben Thema reicht das Update samt `grund`. Der Verweis wurde ignoriert.":g&&(H=await $.get(`cachly:lesson:best:${g}`),H?w.ersetzt=g:C=`\u26A0\uFE0F Die zu ersetzende Lektion \`${g}\` existiert nicht \u2014 der Verweis wurde NICHT gespeichert. Themennamen zeigt \`smart_recall\`.`);let G="";if(!g)try{const R=(await Re($,["cachly:lesson:best:*"],`${t} ${V}`.slice(0,300),5)).filter(W=>W.key.startsWith("cachly:lesson:best:")).map(W=>{const ae=j(W.content,{});return{topic:ae.topic??W.key.slice(19),what_worked:ae.what_worked}});G=is(t,V,R)??""}catch{}if(n==="failure")try{const W=(await $.lrange(ut(p),0,199)).map(A=>j(A,{ts:"",frage:"",geliefert:[]})).filter(A=>A.ts!==""),ae=(await Re($,["cachly:lesson:best:*"],`${t} ${N||V}`.slice(0,300),5)).filter(A=>A.key.startsWith("cachly:lesson:best:")).map(A=>{const K=j(A.content,{});return{topic:K.topic??A.key.slice(19),what_worked:K.what_worked}}),X=hs(t,String(N||""),W,ae);await $.hincrby(dt(p),X,1)}catch{}const F=ds([V,N,f].filter(Boolean).join(" ").slice(0,2e3));F&&(w.entscheidung=F);const q=JSON.stringify(w);$.set(`cachly:stats:born_at:${p}`,oe,"EX",365*86400,"NX").catch(()=>{});const U=`cachly:lessons:${t}`;await $.rpush(U,q),await $.ltrim(U,-100,-1),await $.expire(U,rs*86400);let z=!1;if(n==="success"||n==="partial"?(await $.set(`cachly:lesson:best:${t}`,q),z=!0):L||(await $.set(`cachly:lesson:best:${t}`,q),z=!0),H&&z)try{const R=JSON.parse(H);R.ersetzt_durch=t,R.gilt_bis=oe,R.ersetzt_am=oe,await $.set(`cachly:lesson:best:${g}`,JSON.stringify(R))}catch{}z&&Ut(),z&&Ne()&&(async()=>{const R=j(q,{});try{await $.sadd(ke,t).catch(()=>{});const W=await _e(De(R),{geduld:"lang"});if(W?.length&&(await $.set(`${ct}${t}`,Ee(W)),await $.srem(ke,t).catch(()=>{})),ve==="cachly"||ve==="ollama")try{const ae=await _e(De(R),{geduld:"lang",modell:qe});ae?.length&&await $.set(`${He}${t}`,Ee(ae))}catch{}}catch(W){await $.sadd(ke,t).catch(()=>{}),await Ie($,We,`Volltext-Vektor nicht geschrieben (${ve}): ${Fe(W)} \u2014 die Lektion ist gespeichert, der Bedeutungsabgleich uebergeht sie.`)}try{const W=await _e(ts(t),{geduld:"lang"});W?.length&&await $.set(`${lt}${t}`,Ee(W))}catch(W){await Ie($,We,`Namens-Vektor nicht geschrieben (${ve}): ${Fe(W)} \u2014 siehe oben, die Lektion selbst ist gespeichert.`)}try{await ss($,t,R,W=>_e(W,{geduld:"lang"}))}catch(W){await Ie($,We,`Eingaenge nicht geschrieben (${ve}): ${Fe(W)} \u2014 siehe oben, die Lektion selbst ist gespeichert.`)}await St($,t,3)})();try{const R="cachly:session:decision-log",W=JSON.stringify({ts:oe,topic:t,outcome:n,what_worked:V.slice(0,120)});await $.rpush(R,W),await $.ltrim(R,-50,-1)}catch{}let re=null;try{const R=he(t),W=t.split(":")[0]??"unknown";await Ce($,R,W,W);for(const X of h){const A=he(`tag:${X}`);await Ce($,A,"tag","tag"),await fe($,R,"co-occurs",A,n==="success",n==="partial")}for(const X of u){const A=he(X);await fe($,R,"requires",A,!0)}if((W==="fix"||W==="debug")&&(n==="success"||n==="partial")){const X=N||P||"",A=X?Oe(X):null;if(A){const K=he(`problem:${A}`);await Ce($,K,"problem","problem"),await fe($,R,"fixes",K,n==="success",n==="partial")}}if(n==="failure"&&(N||V)){const A=Oe(N||V);if(A){const K=he(`cause:${A}`);await Ce($,K,"cause","cause"),await fe($,R,"causes",K,!1)}}if(n==="failure"){const X=await $.smembers(`cachly:ckg:idx:from:${R}`);for(const A of X){const K=await $.get(A);if(!K)continue;const ne=j(K,null);if(ne&&ne.edgeType==="fixes"&&ne.confidence>.7&&ne.trials>=3){re=`\u26A0\uFE0F **belief_conflict** on \`${t}\`: previously confirmed fix (confidence ${ne.confidence.toFixed(2)}, n=${ne.trials}) now reports failure. Both beliefs retained as \`contested\`. Use \`brain_resolve_conflict(instance_id="...", topic="${t}", winner="success"|"failure")\` to arbitrate, or \`ckg_inspect(concept="${R}")\` to review.`;const le=`cachly:ckg:conflict:${R}`;await $.set(le,JSON.stringify({topic:t,concept_id:R,detected_at:new Date().toISOString(),fix_confidence:ne.confidence,fix_trials:ne.trials,failure_outcome:n,reported_by:s||"unknown",what_failed:N||"",resolved:!1}),"EX",3600*24*90)}}}if(n==="success"&&(W==="fix"||W==="debug")){const X=await $.smembers(`cachly:ckg:idx:from:${R}`);for(const A of X.slice(0,10)){const K=await $.get(A);if(!K)continue;const ne=j(K,null);if(!ne||ne.edgeType!=="fixes")continue;const le=await $.smembers(`cachly:ckg:idx:from:${ne.to}`);for(const Se of le.slice(0,5)){const Ze=await $.get(Se);if(!Ze)continue;const ge=j(Ze,null);ge&&ge.edgeType==="co-occurs"&&(ge.successes=(ge.successes||0)+.1,ge.trials=(ge.trials||0)+.1,ge.confidence=(ge.successes+1)/(ge.trials+2),ge.last_updated=new Date().toISOString(),await $.set(Se,JSON.stringify(ge)))}}}if(Math.random()<.01){const X=Date.now()-7776e6,A=await $.smembers(`cachly:ckg:idx:from:${R}`);for(const K of A){const ne=await $.get(K);if(!ne)continue;const le=j(ne,null);le&&le.trials<3&&le.last_updated&&new Date(le.last_updated).getTime()<X&&(le.confidence=le.confidence*.9,le.last_updated=new Date().toISOString(),await $.set(K,JSON.stringify(le)))}}}catch{}if(s){$.set(`cachly:agents:active:${s}`,JSON.stringify({author:s,last_topic:t,last_outcome:n,ts:new Date().toISOString()}),"EX",3600).catch(()=>{});try{const R=t.split(":")[0]??"unknown",W=await Et($,yt(s),R),ae=he(t);await fe($,W,"authored",ae,n==="success",n==="partial");for(const X of v.slice(0,8)){const A=await tt($,X);await fe($,W,"touched",A,!0),await At($,A,W)}}catch{}}if(a&&typeof a=="string"&&a.trim())try{const R=t.split(":")[0]??"unknown",W=c==="system"?"system":"service",ae=await Mt($,a.trim(),R,W),X=he(t);if(await fe($,X,"affects",ae,n==="success",n==="partial"),s){const A=`person:${he(yt(s))}`;await fe($,A,"operates",ae,!0)}for(const A of v.slice(0,8)){const K=`file:${he(A)}`;await fe($,K,"runs_in",ae,!0)}}catch{}if(s&&(n==="success"||n==="partial")){const R={topic:t,outcome:n,what_worked:V,what_failed:N??"",severity:o,author:s,file_paths:v,commands:l,tags:[...new Set([...h,"team"])],timestamp:new Date().toISOString(),recall_count:0,version:2,auto_propagated:!0},W=`cachly:lessons:${t}`;$.rpush(W,JSON.stringify(R)).catch(()=>{})}return[`${n==="success"?"\u2705":n==="partial"?"\u26A0\uFE0F":"\u274C"} **Lesson ${d?"updated":"stored"}:** \`${t}\` (${n}) ${o==="critical"?"\u{1F534}":o==="major"?"\u{1F7E1}":"\u{1F7E2}"} ${o}`,re??"","",`**What worked:** ${V}`,N?`**What failed:** ${N}`:"",P?`**Context:** ${P}`:"",v.length>0?`**Files:** ${v.map(R=>`\`${R}\``).join(", ")}`:"",l.length>0?`**Commands:** ${l.map(R=>`\`${R}\``).join(", ")}`:"",h.length>0?`**Tags:** ${h.map(R=>`#${R}`).join(" ")}`:"","",d?`\u267B\uFE0F Updated (recall count: ${O} \xB7 audit entries: ${B.length})`:`\u{1F4A1} Recall later with \`recall_best_solution(topic="${t}")\``,s&&(n==="success"||n==="partial")?`\u{1F465} Auto-shared with team (by _${s}_${Y.quelle!=="uebergeben"?` \xB7 aus ${Y.quelle}`:""}) \u2014 visible in \`team_recall\``:"",m??"",u.length>0?`\u{1F517} Depends on: ${u.map(R=>`\`${R}\``).join(", ")} \u2192 trace with \`trace_dependency\``:"",G,H&&z?`\u21B7 Ersetzt \`${g}\` \u2014 die alte Fassung wird im Abruf verdraengt und nennt ihre Nachfolgerin.`:C,...r,...pe,te>0?`\u{1F5D3}\uFE0F ${te} relative Zeitangabe${te===1?"":"n"} datiert (z. B. "gestern" \u2192 "gestern (${new Date(se.getTime()-864e5).toISOString().slice(0,10)})") \u2014 der Originaltext bleibt unveraendert, das Datum steht daneben.`:"",Us(V)??""].filter(R=>R!=="").join(`
2
+ `);if(L)try{const R=JSON.parse(L);O=R.recall_count??0,D=R.last_recalled_at,B=R.audit_trail??[],B.push({ts:oe,action:"updated",prev_outcome:R.outcome,grund:ce}),B.length>20&&(B=B.slice(-20)),d=!0;const W=[];if(R.outcome==="success"&&n==="failure"?W.push("\u26A0\uFE0F **Contradiction detected!** Existing lesson has outcome: `success`, but you're storing `failure`.",'The existing "success" lesson will be preserved. Only the audit trail is updated.',"If you meant to mark this as failed permanently, store a new lesson with a distinct topic slug."):R.outcome==="failure"&&n==="success"&&W.push("\u2705 **Conflict resolved!** Previous lesson was `failure` \u2014 now overwriting with `success`."),W.length>0){B[B.length-1].action="contradiction-resolved";try{const ae=he(t),X=he(`resolution:${t}`);await fe($,ae,"contradicts",X,!1)}catch{}try{const ae=`cachly:contradictions:${t}`,X=R.outcome==="success"&&n==="failure"?"kept-success":"overwrote-failure";await $.rpush(ae,JSON.stringify({ts:oe,prev_outcome:R.outcome,new_outcome:n,direction:X})),await $.ltrim(ae,-50,-1),await $.expire(ae,180*86400)}catch{}W.push(`\u{1F5F3}\uFE0F Run \`madc_deliberate(topic="${t}")\` to resolve via expert agent voting.`)}}catch{}else B=[{ts:oe,action:"created"}];const pe=[];if(n==="failure"){const R=[],W=$.scanStream({match:"cachly:lesson:best:*",count:100});await new Promise((X,A)=>{W.on("data",K=>R.push(...K)),W.on("end",X),W.on("error",A)});const ae=t.split(/[:\-_]/).filter(X=>X.length>2);for(const X of R.slice(0,50)){const A=await $.get(X);if(A)try{const K=JSON.parse(A);if(K.outcome!=="success")continue;const ne=(K.topic??"").split(/[:\-_]/).filter(Se=>Se.length>2);if(ae.filter(Se=>ne.includes(Se)).length>=1&&K.topic!==t){pe.push(`\u26A0\uFE0F **"I Was Wrong"**: lesson \`${K.topic}\` (success, ${K.severity??"major"}) might have prevented this failure.`,` \u2192 Use \`recall_best_solution(topic="${K.topic}")\` before next attempt.`,` \u2192 To mark it critical: \`learn_from_attempts(topic="${K.topic}", ..., severity="critical")\``);break}}catch{}}}for(const R of u){const W=`cachly:dep:${R}`,ae=await $.get(W),X=j(ae,[]);X.includes(t)||X.push(t),await $.set(W,JSON.stringify(X),"EX",90*86400)}const w={topic:t,outcome:n,what_worked:V,what_failed:N,context:P,severity:o,file_paths:v,commands:l,tags:h,depends_on:u,...s?{author:s}:{},...a?{service:a}:{},...x?{group:String(x).toLowerCase().trim()}:{},visibility:T,recall_count:O,...D?{last_recalled_at:D}:{},...M&&!Number.isNaN(Date.parse(String(M)))?{gilt_ab:new Date(String(M)).toISOString()}:{},ts:oe,verified_at:n==="success"||n==="partial"?oe:void 0,confidence:1,audit_trail:B,version:3},g=String(k??"").trim();let C="",H=null;g===t&&g?C="\u26A0\uFE0F `ersetzt` zeigt auf dasselbe Thema \u2014 fuer Korrekturen im selben Thema reicht das Update samt `grund`. Der Verweis wurde ignoriert.":g&&(H=await $.get(`cachly:lesson:best:${g}`),H?w.ersetzt=g:C=`\u26A0\uFE0F Die zu ersetzende Lektion \`${g}\` existiert nicht \u2014 der Verweis wurde NICHT gespeichert. Themennamen zeigt \`smart_recall\`.`);let G="";if(!g)try{const R=(await Re($,["cachly:lesson:best:*"],`${t} ${V}`.slice(0,300),5)).filter(W=>W.key.startsWith("cachly:lesson:best:")).map(W=>{const ae=j(W.content,{});return{topic:ae.topic??W.key.slice(19),what_worked:ae.what_worked}});G=is(t,V,R)??""}catch{}if(n==="failure")try{const W=(await $.lrange(ut(p),0,199)).map(A=>j(A,{ts:"",frage:"",geliefert:[]})).filter(A=>A.ts!==""),ae=(await Re($,["cachly:lesson:best:*"],`${t} ${N||V}`.slice(0,300),5)).filter(A=>A.key.startsWith("cachly:lesson:best:")).map(A=>{const K=j(A.content,{});return{topic:K.topic??A.key.slice(19),what_worked:K.what_worked}}),X=hs(t,String(N||""),W,ae);await $.hincrby(dt(p),X,1)}catch{}const F=ds([V,N,f].filter(Boolean).join(" ").slice(0,2e3));F&&(w.entscheidung=F);const q=JSON.stringify(w);$.set(`cachly:stats:born_at:${p}`,oe,"EX",365*86400,"NX").catch(()=>{});const U=`cachly:lessons:${t}`;await $.rpush(U,q),await $.ltrim(U,-100,-1),await $.expire(U,rs*86400);let z=!1;if(n==="success"||n==="partial"?(await $.set(`cachly:lesson:best:${t}`,q),z=!0):L||(await $.set(`cachly:lesson:best:${t}`,q),z=!0),H&&z)try{const R=JSON.parse(H);R.ersetzt_durch=t,R.gilt_bis=oe,R.ersetzt_am=oe,await $.set(`cachly:lesson:best:${g}`,JSON.stringify(R))}catch{}z&&Ut(),z&&Ne()&&(await $.sadd(ke,t).catch(()=>{}),(async()=>{const R=j(q,{});try{const W=await _e(De(R),{geduld:"lang"});if(W?.length&&(await $.set(`${ct}${t}`,Ee(W)),await $.srem(ke,t).catch(()=>{})),ve==="cachly"||ve==="ollama")try{const ae=await _e(De(R),{geduld:"lang",modell:qe});ae?.length&&await $.set(`${He}${t}`,Ee(ae))}catch{}}catch(W){await $.sadd(ke,t).catch(()=>{}),await Ie($,We,`Volltext-Vektor nicht geschrieben (${ve}): ${Fe(W)} \u2014 die Lektion ist gespeichert, der Bedeutungsabgleich uebergeht sie.`)}try{const W=await _e(ts(t),{geduld:"lang"});W?.length&&await $.set(`${lt}${t}`,Ee(W))}catch(W){await Ie($,We,`Namens-Vektor nicht geschrieben (${ve}): ${Fe(W)} \u2014 siehe oben, die Lektion selbst ist gespeichert.`)}try{await ss($,t,R,W=>_e(W,{geduld:"lang"}))}catch(W){await Ie($,We,`Eingaenge nicht geschrieben (${ve}): ${Fe(W)} \u2014 siehe oben, die Lektion selbst ist gespeichert.`)}await St($,t,3)})());try{const R="cachly:session:decision-log",W=JSON.stringify({ts:oe,topic:t,outcome:n,what_worked:V.slice(0,120)});await $.rpush(R,W),await $.ltrim(R,-50,-1)}catch{}let re=null;try{const R=he(t),W=t.split(":")[0]??"unknown";await Ce($,R,W,W);for(const X of h){const A=he(`tag:${X}`);await Ce($,A,"tag","tag"),await fe($,R,"co-occurs",A,n==="success",n==="partial")}for(const X of u){const A=he(X);await fe($,R,"requires",A,!0)}if((W==="fix"||W==="debug")&&(n==="success"||n==="partial")){const X=N||P||"",A=X?Oe(X):null;if(A){const K=he(`problem:${A}`);await Ce($,K,"problem","problem"),await fe($,R,"fixes",K,n==="success",n==="partial")}}if(n==="failure"&&(N||V)){const A=Oe(N||V);if(A){const K=he(`cause:${A}`);await Ce($,K,"cause","cause"),await fe($,R,"causes",K,!1)}}if(n==="failure"){const X=await $.smembers(`cachly:ckg:idx:from:${R}`);for(const A of X){const K=await $.get(A);if(!K)continue;const ne=j(K,null);if(ne&&ne.edgeType==="fixes"&&ne.confidence>.7&&ne.trials>=3){re=`\u26A0\uFE0F **belief_conflict** on \`${t}\`: previously confirmed fix (confidence ${ne.confidence.toFixed(2)}, n=${ne.trials}) now reports failure. Both beliefs retained as \`contested\`. Use \`brain_resolve_conflict(instance_id="...", topic="${t}", winner="success"|"failure")\` to arbitrate, or \`ckg_inspect(concept="${R}")\` to review.`;const le=`cachly:ckg:conflict:${R}`;await $.set(le,JSON.stringify({topic:t,concept_id:R,detected_at:new Date().toISOString(),fix_confidence:ne.confidence,fix_trials:ne.trials,failure_outcome:n,reported_by:s||"unknown",what_failed:N||"",resolved:!1}),"EX",3600*24*90)}}}if(n==="success"&&(W==="fix"||W==="debug")){const X=await $.smembers(`cachly:ckg:idx:from:${R}`);for(const A of X.slice(0,10)){const K=await $.get(A);if(!K)continue;const ne=j(K,null);if(!ne||ne.edgeType!=="fixes")continue;const le=await $.smembers(`cachly:ckg:idx:from:${ne.to}`);for(const Se of le.slice(0,5)){const Ze=await $.get(Se);if(!Ze)continue;const ge=j(Ze,null);ge&&ge.edgeType==="co-occurs"&&(ge.successes=(ge.successes||0)+.1,ge.trials=(ge.trials||0)+.1,ge.confidence=(ge.successes+1)/(ge.trials+2),ge.last_updated=new Date().toISOString(),await $.set(Se,JSON.stringify(ge)))}}}if(Math.random()<.01){const X=Date.now()-7776e6,A=await $.smembers(`cachly:ckg:idx:from:${R}`);for(const K of A){const ne=await $.get(K);if(!ne)continue;const le=j(ne,null);le&&le.trials<3&&le.last_updated&&new Date(le.last_updated).getTime()<X&&(le.confidence=le.confidence*.9,le.last_updated=new Date().toISOString(),await $.set(K,JSON.stringify(le)))}}}catch{}if(s){$.set(`cachly:agents:active:${s}`,JSON.stringify({author:s,last_topic:t,last_outcome:n,ts:new Date().toISOString()}),"EX",3600).catch(()=>{});try{const R=t.split(":")[0]??"unknown",W=await Et($,yt(s),R),ae=he(t);await fe($,W,"authored",ae,n==="success",n==="partial");for(const X of v.slice(0,8)){const A=await tt($,X);await fe($,W,"touched",A,!0),await At($,A,W)}}catch{}}if(a&&typeof a=="string"&&a.trim())try{const R=t.split(":")[0]??"unknown",W=c==="system"?"system":"service",ae=await Mt($,a.trim(),R,W),X=he(t);if(await fe($,X,"affects",ae,n==="success",n==="partial"),s){const A=`person:${he(yt(s))}`;await fe($,A,"operates",ae,!0)}for(const A of v.slice(0,8)){const K=`file:${he(A)}`;await fe($,K,"runs_in",ae,!0)}}catch{}if(s&&(n==="success"||n==="partial")){const R={topic:t,outcome:n,what_worked:V,what_failed:N??"",severity:o,author:s,file_paths:v,commands:l,tags:[...new Set([...h,"team"])],timestamp:new Date().toISOString(),recall_count:0,version:2,auto_propagated:!0},W=`cachly:lessons:${t}`;$.rpush(W,JSON.stringify(R)).catch(()=>{})}return[`${n==="success"?"\u2705":n==="partial"?"\u26A0\uFE0F":"\u274C"} **Lesson ${d?"updated":"stored"}:** \`${t}\` (${n}) ${o==="critical"?"\u{1F534}":o==="major"?"\u{1F7E1}":"\u{1F7E2}"} ${o}`,re??"","",`**What worked:** ${V}`,N?`**What failed:** ${N}`:"",P?`**Context:** ${P}`:"",v.length>0?`**Files:** ${v.map(R=>`\`${R}\``).join(", ")}`:"",l.length>0?`**Commands:** ${l.map(R=>`\`${R}\``).join(", ")}`:"",h.length>0?`**Tags:** ${h.map(R=>`#${R}`).join(" ")}`:"","",d?`\u267B\uFE0F Updated (recall count: ${O} \xB7 audit entries: ${B.length})`:`\u{1F4A1} Recall later with \`recall_best_solution(topic="${t}")\``,s&&(n==="success"||n==="partial")?`\u{1F465} Auto-shared with team (by _${s}_${Y.quelle!=="uebergeben"?` \xB7 aus ${Y.quelle}`:""}) \u2014 visible in \`team_recall\``:"",m??"",u.length>0?`\u{1F517} Depends on: ${u.map(R=>`\`${R}\``).join(", ")} \u2192 trace with \`trace_dependency\``:"",G,H&&z?`\u21B7 Ersetzt \`${g}\` \u2014 die alte Fassung wird im Abruf verdraengt und nennt ihre Nachfolgerin.`:C,...r,...pe,te>0?`\u{1F5D3}\uFE0F ${te} relative Zeitangabe${te===1?"":"n"} datiert (z. B. "gestern" \u2192 "gestern (${new Date(se.getTime()-864e5).toISOString().slice(0,10)})") \u2014 der Originaltext bleibt unveraendert, das Datum steht daneben.`:"",Us(V)??""].filter(R=>R!=="").join(`
3
3
  `)}case"recall_feedback":{const{instance_id:p}=Z,t=await Q(p),n=ws(Z,new Date().toISOString());if(!n.ok)return[`\u26A0\uFE0F ${n.grund}`,"","Beispiel:","```",'recall_feedback(instance_id="\u2026", query="warum bricht der Deploy ab",',' topic="ci:zeitgrenze-meldet-cancelled", helped=true, rank=0)',"```","rank=0 heisst: die Lektion stand GAR NICHT in der Antwort.","Das ist die wertvollste Rueckmeldung von allen."].join(`
4
4
  `);const b=n.eintrag,E=`cachly:wirkung:${p}`;await t.rpush(E,JSON.stringify(b));const o=(await t.lrange(E,0,-1).catch(()=>[])).map(S=>j(S,null)).filter(Boolean),v=_s(o),l=bs(b),h={"fehlender-treffer":"Sie half, stand aber gar nicht in der Antwort \u2014 der Topf hat sie nicht aufgenommen. Das ist der wertvollste Fall.","zu-weit-hinten":`Sie half, stand aber auf Platz ${b.platz} \u2014 die Sortierung war zu schwach.`,"falsch-und-sicher":`Sie stand auf Platz ${b.platz} und half nicht \u2014 die Sortierung war sich sicher und lag falsch.`,bestaetigung:"Sie stand vorn und half. Bestaetigung.",unauffaellig:"Half nicht und stand hinten. Sagt wenig."},u=v.trefferquote===null?"noch keine hilfreiche Rueckmeldung":`${(v.trefferquote*100).toFixed(0)} % der hilfreichen Lektionen standen unter den ersten dreien`;return[`\u2705 Notiert: **${b.thema}** \u2014 ${b.geholfen?"hat geholfen":"hat nicht geholfen"}, Platz ${b.platz===0?"\u2014 (stand nicht drin)":b.platz}`,"",h[l],"",`Spur: ${v.gesamt} Rueckmeldungen, ${u}.`,`Davon: ${v.nachLehrwert["fehlender-treffer"]} nicht gefunden \xB7 ${v.nachLehrwert["zu-weit-hinten"]} zu weit hinten \xB7 ${v.nachLehrwert["falsch-und-sicher"]} falsch und sicher.`].join(`
5
5
  `)}case"recall_best_solution":{const{instance_id:p,topic:t,author:n}=Z,b=await Q(p);try{const h=await b.get(`cachly:letzter-recall:${p}`),u=h?j(h,null):null,S=$s(u,t,new Date().toISOString());S&&(await b.rpush(`cachly:wirkung:${p}`,JSON.stringify(S)),await b.del(`cachly:letzter-recall:${p}`))}catch{}const E=await b.get(`cachly:lesson:best:${t}`);if(E){const h=j(E,null);if(!h)return`\u26A0\uFE0F Lesson data for \`${t}\` is corrupted. Re-store it with \`learn_from_attempts\`.`;const u=h.group;if(u){const L=n?await at(b,p,n).catch(()=>new Set):new Set,ce=n?await Ke(b,p,n).catch(()=>null)==="admin":!1;if(!it(u,L,ce))return`\u{1F4ED} No lessons found for \`${t}\`. Use \`learn_from_attempts\` after solving it.`}const S=Ae(h),T=h.verified_at??h.ts,a=(Date.now()-new Date(T).getTime())/864e5,c=Ct(S,a),x=new Date().toISOString(),_={...h,recall_count:(h.recall_count??0)+1,verified_at:x,last_recalled_at:x,confidence:1};await b.set(`cachly:lesson:best:${t}`,JSON.stringify(_)),bt(b);const k=nt(h)?st(h):0;k>0&&b.incrbyfloat(`cachly:stats:time_saved_mins:${p}`,k).catch(()=>{});const M=h.severity==="critical"?"\u{1F534}":h.severity==="major"?"\u{1F7E1}":h.severity?"\u{1F7E2}":"",I=h.audit_trail??[],e=I.filter(L=>L&&typeof L.grund=="string"&&L.grund.trim().length>0).slice(-3).map(L=>` \xB7 ${L.ts?new Date(L.ts).toLocaleDateString("de-DE"):"?"}: ${L.grund.trim()}`),J=I.length>1?[`_Audit: ${I.length} changes \xB7 stored ${new Date(h.ts).toLocaleDateString("de-DE")}_`,...e.length>0?["**Warum zuletzt geaendert:**",...e]:[]].join(`
package/glama.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "https://glama.ai/mcp/schemas/server.json",
3
- "version": "0.10.148",
3
+ "version": "0.10.149",
4
4
  "maintainers": [
5
5
  "HeinrichNebula"
6
6
  ],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cachly-dev/mcp-server",
3
- "version": "0.10.148",
3
+ "version": "0.10.149",
4
4
  "mcpName": "io.github.cachly-dev/mcp-server",
5
5
  "description": "Your AI forgets everything between sessions. cachly fixes that permanently: 123 MCP tools for Claude Code, Cursor, Copilot & Windsurf. Learns from every bug fix, commit and CI run. Arrives pre-briefed. Free tier forever, EU servers, GDPR.",
6
6
  "type": "module",
package/server.json CHANGED
@@ -6,13 +6,13 @@
6
6
  "url": "https://github.com/cachly-dev/cachly-mcp",
7
7
  "source": "github"
8
8
  },
9
- "version": "0.10.148",
9
+ "version": "0.10.149",
10
10
  "packages": [
11
11
  {
12
12
  "registryType": "npm",
13
13
  "name": "@cachly-dev/mcp-server",
14
14
  "identifier": "@cachly-dev/mcp-server",
15
- "version": "0.10.148",
15
+ "version": "0.10.149",
16
16
  "transport": {
17
17
  "type": "stdio"
18
18
  }