Skip to main content
Glama
README.md
<!-- mcp-name: io.github.VonderVuflya/yggdrasil -->
<h1 align="center">๐ŸŒณ Yggdrasil</h1>

<p align="center"><b>Stop re-explaining your project to every new AI session.</b><br/>
One local memory for Claude Code, Codex, and every MCP agent โ€” shared across sessions, tools, and projects. Zero dependencies. Nothing leaves your machine.</p>

<p align="center">
  <a href="https://github.com/VonderVuflya/Yggdrasil/releases/latest"><img src="https://img.shields.io/github/v/release/VonderVuflya/Yggdrasil?label=release&color=blue" alt="Latest release"></a>
  <a href="https://pypi.org/project/yggdrasil-memory/"><img src="https://img.shields.io/pypi/v/yggdrasil-memory?label=PyPI&color=blue" alt="PyPI"></a>
  <a href="https://glama.ai/mcp/servers/VonderVuflya/Yggdrasil"><img src="https://glama.ai/mcp/servers/VonderVuflya/Yggdrasil/badges/score.svg" alt="Glama quality score"></a>
  <a href="./BENCHMARKS.md"><img src="https://img.shields.io/badge/recall@1-0.94%20ยท%20reproducible-brightgreen" alt="Benchmarks"></a>
  <a href="./LICENSE"><img src="https://img.shields.io/badge/License-AGPL%203.0-blue.svg" alt="AGPL-3.0"></a>
  <img src="https://img.shields.io/badge/status-alpha-orange" alt="alpha">
</p>

<p align="center">
  <a href="#-install">Install</a> ยท
  <a href="#-how-it-works">How it works</a> ยท
  <a href="#-the-numbers">Numbers</a> ยท
  <a href="#-yggdrasil-vs-the-rest">Compare</a> ยท
  <a href="#-faq">FAQ</a>
</p>

<p align="center">
  Read this in: <a href="./i18n/README.ru.md">ะ ัƒััะบะธะน</a> ยท <a href="./i18n/README.zh.md">็ฎ€ไฝ“ไธญๆ–‡</a> ยท <a href="./i18n/README.es.md">Espaรฑol</a> ยท <a href="./i18n/README.fr.md">Franรงais</a> ยท <a href="./i18n/README.ja.md">ๆ—ฅๆœฌ่ชž</a> ยท <a href="./i18n/README.de.md">Deutsch</a>
</p>

---

<p align="center">
  <img src="docs/demo.gif" alt="Yggdrasil โ€” a brand-new session already knows your project, and recalls a fix from another project" width="880">
</p>

Every new chat, your AI forgets. You re-explain the project, the decisions, the gotchas โ€” every time, in every tool. **Yggdrasil is a tiny always-on memory that any agent plugs into.** Open a new session, in any project, with any AI, and it already knows what you decided, what broke, and what's still open.

```text
$ cd ~/projects/checkout-api && claude        # a brand-new session

๐ŸŒณ Yggdrasil  (injected automatically at session start)
   โ€ข [project_status] payments refactor: idempotency keys added; open: e2e tests
   โ€ข [lesson] webhook 401 โ†’ signing secret rotated; update env + redeploy

> "have I solved a flaky websocket reconnect anywhere before?"

๐ŸŒณ recall โ†’ found in project `realtime-dash`:
   refresh the token *before* opening the socket, then retry with capped backoff.
```

No "let me remind you what we did yesterday." It's just there.

## ๐Ÿš€ Install

Two commands, inside **Claude Code** (the plugin launches via [`uv`](https://docs.astral.sh/uv/)):

```text
/plugin marketplace add VonderVuflya/Yggdrasil
/plugin install yggdrasil
```

The engine lazy-starts on first use and generates its own local token โ€” no API key, no cloud, nothing to configure. Codex and Cursor use the same flow.

<details>
<summary>All other channels โ€” CLI daemon, Homebrew, npm, Claude Desktop, from sourceโ€ฆ</summary>

| Host / tool | Command |
| --- | --- |
| **uvx** _(recommended CLI)_ | `uvx --from yggdrasil-memory ygg install` |
| **npm / npx** | `npx yggdrasil-memory install` |
| **pipx** | `pipx install yggdrasil-memory && ygg install` |
| **pip** | `pip install yggdrasil-memory && ygg install` |
| **Homebrew** _(macOS)_ | `brew install VonderVuflya/tap/yggdrasil && ygg install` |
| **Claude Desktop** _(app)_ | drag the `.mcpb` from the [latest release](https://github.com/VonderVuflya/Yggdrasil/releases/latest) onto Settings โ†’ Extensions, paste your token (`ygg token`) โ€” the desktop app then shares the same memory as your CLI agents ([guide](./packaging/mcpb/README.md)) |
| **from source** | `uvx --from git+https://github.com/VonderVuflya/yggdrasil.git ygg install` |

`ygg install` is a one-time guided setup: it installs an always-on background service, registers the MCP tools with every agent host it finds โ€” **Claude Code, Codex, OpenCode** โ€” and, if your hardware allows, recommends optional local models (or pick `none` to stay zero-config).

<details>
<summary><b>OpenCode</b> โ€” nothing to configure</summary>

Install [OpenCode](https://opencode.ai) first, then run `ygg install` (or `ygg redeploy` if Yggdrasil is already set up) โ€” the entry is written for you and merged into any existing `opencode.json`. Confirm with:

```bash
opencode mcp list        # -> โœ“ yggdrasil connected
```

Installed OpenCode *after* Yggdrasil? Just re-run `ygg install`.

If you'd rather write it by hand, note that OpenCode's schema differs from Claude's in four places at once โ€” servers live under `mcp` (not `mcpServers`), `type` is required, `command` is one array (not `command` + `args`), and env is `environment` (not `env`), so the Claude snippet won't port:

```jsonc
// ~/.config/opencode/opencode.json
{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "yggdrasil": {
      "type": "local",
      "command": ["/path/to/python3", "~/.yggdrasil/scripts/ygg_mcp_server.py"],
      "enabled": true,
      "environment": { "YGG_ENGINE_URL": "http://127.0.0.1:42069" }
    }
  }
}
```

No token goes in the config โ€” the engine reads the 0600 `~/.yggdrasil/token` itself. Run `ygg doctor` if the tools don't show up.

</details>

There is also a [`yggdrasil-memory` skill](./skills/) for any Claude surface: MCP connects the *tools*, the skill teaches the agent *when* to use them. Use both for the best behavior.

Try it with nothing installed and a throwaway DB: `uvx --from yggdrasil-memory ygg serve --reset --db /tmp/ygg.sqlite`.

</details>

Then just work: ask your agent *"recall what we decided about this project"*, tell it *"remember this decision"* โ€” next session it's already there. Verify the install any time with `ygg doctor`.

**Already have history?** Seed memory from your existing Claude Code + Codex transcripts, Obsidian vaults, and `CLAUDE.md` repos โ€” distilled locally:

```bash
ygg seed --dry-run    # see what it would import; drop the flag to distill for real
```

**Leaving another memory tool?** `ygg import --from mcp-memory --path memory.json` pulls its whole store into Yggdrasil (deduped, secret-guarded) โ€” then you can delete it.

## Why

- ๐Ÿง  **Persistent** โ€” decisions, lessons, and project status survive across sessions.
- ๐Ÿ”Œ **One brain, every tool** โ€” Claude Code, Codex, OpenCode, and any MCP host share the same memory.
- ๐ŸŒ **Cross-project recall** โ€” *"this looks like what you did in project B โ€” reuse it?"*
- ๐Ÿงน **Curated, not captured** โ€” your agent saves the few things that matter; governance dedupes and archives, never deletes.
- ๐ŸŒฑ **Self-maintaining** *(opt-in)* โ€” a small local model consolidates memory in the background. Zero API tokens.
- ๐Ÿชช **One identity everywhere** โ€” an optional name and persona every agent picks up, so Claude Code and Codex feel like the same assistant.
- ๐Ÿ”’ **100% local** โ€” your memory lives on your machine. No cloud, no account, no telemetry.

## ๐Ÿง  How it works

Yggdrasil is **memory + tools** โ€” the *intelligence* is your LLM. It just makes sure the right memory is in front of the right agent at the right moment.

- ๐Ÿ›Ž๏ธ **Always-on daemon** โ€” a tiny local service (~21 MB RAM) your agents reach over MCP tools (`ygg_search`, `ygg_recall`, `ygg_remember` โ€ฆ).
- ๐Ÿช **Hooks** โ€” session start auto-injects identity, project status, and open follow-ups (~300 tokens); an optional per-prompt hook auto-recalls memory relevant to *each request*.
- ๐Ÿ“Œ **Ranking** โ€” pinned and frequently-recalled memories surface first.
- ๐Ÿงน **Governance** โ€” duplicates and conflicts are queued for review; changes are non-destructive (archive, never delete).
- ๐Ÿ““ **Obsidian** โ€” every memory doubles as a plain-Markdown note you can read, edit, and grep.

## ๐ŸŽ›๏ธ Memory tiers โ€” zero-config by default

Out of the box, Yggdrasil runs on **SQLite + FTS5 with zero dependencies** โ€” instant keyword search, no models, nothing to download. Optional **local** models add two independent tiers:

| Tier | You add | You gain |
| --- | --- | --- |
| **0 ยท default** | nothing โ€” SQLite + FTS5 | keyword search, zero deps, instant โ€” recall@1 = **0.77** |
| **1 ยท semantic** | an **embedding** model (`all-minilm` 45 MB ยท `paraphrase-multilingual` ~560 MB) | search by **meaning**, across languages โ€” recall@1 = **0.94**, recall@3 **1.00** |
| **2 ยท self-maintaining** | a small **LLM** (`qwen2.5:1.5b` ~1 GB) | background dedupe/merge of memory (propose-only) |

The runtime only *computes* vectors and runs the background model โ€” every memory and every vector stays in the same local SQLite.

`ygg install` scans for [Ollama](https://ollama.com), [LM Studio](https://lmstudio.ai) and llama.cpp, offers to start whichever is installed but idle, and then shows one menu per job with **green for models you already have and red for models it would download**. Pick a row, and it writes `embed_backend`, `embed_url` and `distill_url` for you โ€” including the part nobody guesses right, that `embed_url` wants the `/v1` base while `distill_url` wants the host root. A runtime on another machine is one URL: paste it and the dialect is detected. `ygg recommend` shows the same scan plus the full catalog without changing anything.

<details>
<summary>Full model menu</summary>

**Embeddings (semantic search):**

| Model | Size | Good for |
| --- | --- | --- |
| `all-minilm` | 45 MB | English, tiny & fast |
| `nomic-embed-text` | 274 MB | English, better quality (768d) |
| `mxbai-embed-large` | 670 MB | English, high quality (1024d) |
| `paraphrase-multilingual` | ~560 MB | multilingual (EN/RU + 50 langs, 768d) |
| `bge-m3` | 1.2 GB | multilingual, top quality (heavier) |

**Embedding backend** โ€” Ollama by default. `ygg install` sets all of this for you
once you pick a runtime; the manual route below is for scripted setups and for
changing one thing later. To use an OpenAI-compatible `/v1/embeddings` server
instead (llama.cpp's `llama-server --embeddings`, OpenRouter, LM Studio, vLLM),
set `embed_backend`:

```bash
# local llama.cpp โ€” no key needed
ygg config set embed_backend openai
ygg config set embed_url http://127.0.0.1:8080/v1
ygg config set embed_model bge-small-en-v1.5
ygg redeploy

# OpenRouter โ€” free embeddings, no GPU needed
ygg config set embed_backend openai
ygg config set embed_url https://openrouter.ai/api/v1
ygg config set embed_model nvidia/llama-nemotron-embed-vl-1b-v2:free
ygg config set embed_api_key sk-or-...    # or export YGG_EMBED_API_KEY
ygg redeploy
```

The key is stored in `~/.yggdrasil/embed_api_key` (0600) rather than
`config.json`, and reaches the daemon as a **file path** โ€” so it never shows up
in `ps`, the launchd plist or the systemd unit. `ygg config list` masks it.

Check it took with `ygg doctor` โ€” dense should name your model:

```
โœ“ dense    active (nvidia/llama-nemotron-embed-vl-1b-v2:free)
```

Wiring LM Studio or OpenRouter by hand? The gotchas (two URL shapes, model ids, JIT loading, inference vs provisioning keys, privacy filters) are in [docs/model-backends.md](./docs/model-backends.md).

Staying local still wins on quality *and* privacy: on the 232-memory / 110-query
corpus, local `paraphrase-multilingual` scores recall@1 **0.964** vs **0.946**
for the free hosted model โ€” and your memories never leave the machine. Reach for
a hosted backend when the box can't run Ollama, not to chase accuracy.

Bigger vectors do **not** buy accuracy here โ€” on the same corpus
`mxbai-embed-large` (1024d) scores 0.809 and `nomic-embed-text` (768d) 0.818,
a difference their confidence intervals swallow whole. What actually moves the
number is whether the model handles *your* languages: both are English-only and
collapse to 0.40โ€“0.45 on cross-language queries, where the multilingual default
holds 0.95.

**Background consolidation (small LLM):**

| Model | Size | Good for |
| --- | --- | --- |
| `qwen2.5:0.5b` | ~400 MB | tiny, fast on CPU |
| `qwen2.5:1.5b` | ~1 GB | best CPU default |
| `llama3.2:3b` | ~2 GB | better quality, slower on CPU |

The engine itself is swappable โ€” any service meeting the `MemoryBackend` contract is a drop-in (`YGG_ENGINE_URL`); see [docs/backend-boundary.md](./docs/backend-boundary.md).

</details>

## ๐Ÿ”— One memory across your machines

Work on a laptop and a beefier box? Link them once and they stop being two brains.

```text
# on the laptop
$ ygg link
๐Ÿ”— pairing code   valid 5 minutes ยท single use

    ygg://192.168.3.152:42070/a4e0df489737#FOucjgJ1FrUBโ€ฆ

# on the other machine โ€” paste it
$ ygg link ygg://192.168.3.152:42070/a4e0df489737#FOucjgJ1FrUBโ€ฆ
  โœ“ linked with laptop   https://192.168.3.152:42070
  โœ“ laptop: fetch 84 ยท push 12 ยท delete 0 ยท tombstones 0
```

That's it. From then on a memory written on one machine is on the other in about
a second, `ygg delete` removes it from **both**, and either machine keeps working
with its own local copy when the other is asleep โ€” reads never wait for the
network. When a machine comes back, the two compare fingerprints and repair
whatever they missed, without you running anything.

There is **no polling**. Yggdrasil is called a handful of times a day, so writes
push as they happen and a sync is triggered by reads; idle machines send nothing.

**What this is not:** a cloud. There is no relay, no account, no server of ours in
the path. The machines talk to each other directly over your own network, over
TLS with each other's certificate pinned, using a key pair that exists only on
those two machines. The pairing code itself names the issuing machine's
certificate, so even the very first connection is verified โ€” something else on
the network answering in its place gets refused before the code's secret is sent. Nothing binds a network port until you run
`ygg link` โ€” install Yggdrasil and it stays on loopback, exactly as before.

```text
ygg link --list          # what's linked, and whether writes are actually arriving
ygg link --sync          # reconcile now, don't wait for a read
ygg unlink <name>        # revoke that machine's key here
```

<details>
<summary>Details: what travels, what stays, and when to use <code>ygg sync</code> instead</summary>

**Travels:** memories, their relations, and deletions. **Stays put:** how often
*you* opened a memory and which machine you read it on โ€” that's local ranking,
not knowledge. Embedding vectors normally stay local too, with one exception: if
both machines run the same embedding model, the vector rides along, so a compute
box can do the embedding for the laptop and the laptop never runs a model.

**The newer edit wins.** Edit a memory on both machines before they talk and the
more recent edit is kept on both (a shorter correction included).

**Deletions win.** If you delete a memory on one machine and edit it on the other
at the same time, the deletion wins and the edit is lost. That's deliberate:
"I deleted the leaked token" has to hold even when two clocks disagree. When you
mean *changed my mind*, archive it instead โ€” nothing is lost, and
`ygg unarchive` brings it back on both machines (the later decision wins).

**A firewall is caught at pairing.** The machine that printed the code calls the
other one back straight away; if it can't get through, `ygg link` says so and
prints the one command that opens the port to that machine only (ufw, firewalld,
Windows). `ygg link --list` and `ygg doctor` keep checking afterwards: a peer
shows โœ“ only once writes are confirmed delivered, otherwise the error and how
many changes are waiting. A machine that gets a new address from DHCP is found
again the next time it makes contact.

**`ygg sync` (git) is still there** and does a different job: machines that can't
see each other, a work laptop behind a different network, a bare repo on a USB
stick you carry. Use `ygg link` for machines on one network, `ygg sync` for
everything else. Both share the same merge rules, so you can run both. If both
machines are on a Tailscale tailnet, `ygg link --tailscale` works across networks too.

**If the pairing code shows the wrong address** (it prefers real Ethernet and
Wi-Fi over VM bridges and VPNs, but can still guess wrong), pin it:
`ygg config set sync_host 192.168.3.152` โ€” or print the code on the other machine:
the side that pastes it always uses the address the route actually takes.

</details>

## ๐Ÿ“Š The numbers

Measured by [`eval/ygg_eval.py`](./eval/ygg_eval.py) โ€” 232 memories, 110 labelled queries, ranking weights tuned on the *dev* split only, so **holdout is the unbiased number** (recall@1, with the `paraphrase-multilingual` model):

| Search view | holdout recall@1 | recall@3 | zero-dep lexical |
| --- | --- | --- | --- |
| **Within a project** (the real path, pool ~11) | **0.94** | **1.00** | 0.76 |
| **Whole store** (no filter, pool 232) | 0.72 | 0.87 | 0.69 |

**Within a project โ€” the path you use โ€” the right memory is #1 for 0.94 of queries and in the top 3 every time (recall@3 = 1.00).** Searching the whole store with no filter is harder (recall@1 0.72, recall@3 0.87 across all 232). Zero-dep lexical mode already solves keyword and code-identifier queries (1.00); the local model adds meaning and cross-language (crosslingual 0.25 โ†’ 0.95). The [full breakdown in BENCHMARKS.md](./BENCHMARKS.md) has 95% CIs, pool sizes, and per-class scores โ€” rerun it in a minute: `python3 eval/ygg_eval.py --report`.

**Corpus size is part of the number.** On a real 4,799-memory store the default multilingual embedder falls to the lexical baseline (recall@1 0.55); `bge-m3` holds 0.775 there. If your store grows into the thousands, pick `bge-m3` โ€” details in [BENCHMARKS.md](./BENCHMARKS.md).

## ๐Ÿ†š Yggdrasil vs the rest

Everyone else either auto-captures transcripts or sells you a cloud. Yggdrasil's bet: keep the **few things that matter**, curated and de-duped, in plain rows you own โ€” and share them across **every** tool and project.

| | **Yggdrasil** | Built-in memory <sub>(Claude Code ยท Codex)</sub> | [claude-mem](https://github.com/thedotmack/claude-mem) | [mem0](https://github.com/mem0ai/mem0) / OpenMemory | [basic-memory](https://github.com/basicmachines-co/basic-memory) |
| --- | --- | --- | --- | --- | --- |
| Curated decisions / lessons / status (not transcripts) | โœ… | โš ๏ธ auto-notes | โŒ captures everything | โš ๏ธ | โš ๏ธ free-form notes |
| One memory **across tools** | โœ… | โŒ vendor-siloed | โœ… | โœ… | โœ… |
| **Cross-project** recall ("solved this in project B") | โœ… | โŒ repo-scoped | โš ๏ธ | โš ๏ธ | โš ๏ธ |
| **100% local** by default | โœ… | โœ… | โš ๏ธ cloud sync add-on | โŒ hosted-first | โœ… |
| **Zero dependencies** (stdlib + SQLite) | โœ… | โ€” | โŒ Node + Bun + worker daemon | โŒ Docker + Qdrant + LLM key | โŒ |
| Works with **no LLM & no API key** | โœ… | โœ… | โŒ AI-compresses | โŒ | โœ… |
| **Semantic search, fully local** | โœ… opt-in Ollama | โŒ grep-only | โš ๏ธ optional Chroma | โš ๏ธ needs API key or Docker stack | โŒ |
| Plain **Markdown you own** (Obsidian-ready) | โœ… | โœ… | โŒ | โŒ | โœ… |

**Closest neighbor โ€” claude-mem:** capture-everything memory that records and AI-compresses every session (Node 20+ *and* Bun, a persistent worker daemon; Chroma optional). Yggdrasil is the opposite bet: a small, high-signal store instead of a growing firehose. **mem0** is an SDK plus a hosted platform for building *apps* that remember *their users* โ€” even self-hosted it needs an LLM API key. **Built-in memories** are genuinely useful โ€” and structurally siloed: one vendor, one repo, one machine, literal grep. Yggdrasil is the layer above them (and `ygg seed` can bootstrap itself from those same transcripts). Different layer entirely: [context-mode](https://github.com/mksglu/context-mode) (live context window) and [Context7](https://github.com/upstash/context7) (fresh library docs) โ€” both pair fine with Yggdrasil.

## ๐Ÿงฐ Commands

Agents see six MCP tools: `ygg_health`, `ygg_bootstrap`, `ygg_search`, `ygg_recall`, `ygg_remember`, `ygg_materialize` โ€” auto-registered by the plugin or `ygg install`.

<details>
<summary>Full <code>ygg</code> CLI reference</summary>

**Memory ops**

| Command | What it does |
| --- | --- |
| `ygg recall --query "โ€ฆ"` | **Cross-project** search โ€” "have I done this anywhere?" |
| `ygg search --project P --query "โ€ฆ"` | Project-scoped search (`--type`, `--tag`, `--limit`, `--json`) |
| `ygg remember --project P --type lesson --content "โ€ฆ"` | Save a durable memory (secret-guarded, deduped) |
| `ygg bootstrap --project P` | Pull a project's memory before starting work |
| `ygg pin --id ID` ยท `ygg unpin --id ID` | Pin a memory so it reliably surfaces |
| `ygg relate --from A --rel solves --to B` ยท `ygg relations --id ID` | Link memories (`solves`/`supersedes`/`contradicts`) ยท see why a memory exists / what replaced it |
| `ygg supersede --id OLD --by NEW` | Archive an outdated memory โ€” `--by` records what replaced it |
| `ygg unarchive --id ID` | Bring an archived memory back (on linked machines too) |
| `ygg materialize --id ID --project P` | Export one memory to an Obsidian note |
| `ygg export-native --project P` | Write a curated digest into `AGENTS.md`/`MEMORY.md` โ€” feed Claude Code & Codex's native memory |
| `ygg import --from TOOL --path P` | Migrate another memory tool's store into Yggdrasil (`mcp-memory`, `basic-memory`; `--dry-run` first) |
| `ygg review [--apply]` | Work the governance queue โ€” consolidate duplicates, flag stale/conflicting memories (archive-only, reversible) |
| `ygg delete --id ID` ยท `ygg reset โ€ฆ` | Hard-delete one memory ยท bulk-undo a bad seed (confirms first) |

**Cold start**

| Command | What it does |
| --- | --- |
| `ygg seed` | Distill Claude Code + Codex transcripts, Obsidian vaults, `CLAUDE.md` repos โ€” incremental, deduped, fully local |
| `ygg seed --dry-run` ยท `--force` | Discover + estimate only ยท re-distill everything |
| `ygg seed --schedule 03:30` | Nightly auto-distill (launchd) โ€” memory keeps itself fresh; `off` / `status` |
| `ygg sync --repo <your-git-repo>` | Sync memory across machines through **your own** git repo โ€” plain JSON files, no cloud in the loop |
| `ygg link` ยท `ygg link <code>` | Link two of **your** machines directly on one network โ€” live shared memory, pinned TLS, no relay |
| `ygg link --list` ยท `ygg unlink <name>` | Show linked machines ยท revoke one |
| `ygg distill --source PATH` | Distill one dir/file into lessons |
| `ygg reindex` | Backfill missing embeddings (restores dense recall) |

**Service & setup**

| Command | What it does |
| --- | --- |
| `ygg install` ยท `ygg doctor` ยท `ygg update` | Guided setup ยท diagnose with actionable fixes ยท upgrade |
| `ygg config` | Show/set persistent settings (`list` ยท `get` ยท `set` ยท `unset`) |
| `ygg status` ยท `start` ยท `stop` ยท `restart` ยท `logs` | Manage the always-on daemon |
| `ygg hooks` ยท `unhooks` ยท `register` | SessionStart hook on/off ยท (re)register MCP |
| `ygg recommend` ยท `token` ยท `uninstall` | Model catalog ยท print auth token ยท remove everything |

Give it a personality โ€” edit `~/.yggdrasil/identity.json`:

```json
{ "name": "Jarvis", "persona": "concise, proactive, dry wit", "user_facts": ["prefers TypeScript", "ships small PRs"] }
```

Heavy seeding, weak laptop? Point distillation at *any* box on your LAN โ€” a desktop with Ollama, LM Studio, llama.cpp, **even an iPhone running a local-LLM server app**: `ygg config set distill_url http://<box>:11434`. Yggdrasil auto-detects the API dialect (Ollama or OpenAI-compatible); your data still never leaves your network โ€” details in [docs/ygg-cli.md](./docs/ygg-cli.md).

</details>

## โ“ FAQ

<details>
<summary><b>Claude Code already has built-in memory โ€” why Yggdrasil?</b></summary>

Built-in memories are per-vendor, per-repo, per-machine, and retrieved by literal text match. Yggdrasil is the layer above: the *same* memory in Claude Code, Codex, and any MCP host, recall *across* projects, optional semantic search โ€” still 100% local. It bridges them **both ways**: `ygg seed` distills your existing native memory + transcripts into the shared brain, and `ygg export-native` writes a curated digest back into `AGENTS.md`/`MEMORY.md` โ€” so even a fresh clone or a tool without Yggdrasil still gets your curated memory.
</details>

<details>
<summary><b>Does it send my code or memory to the cloud?</b></summary>

No. The engine, the database, and the optional models all run locally. No account, no telemetry. The only outbound call is a version check against PyPI.

If you link two of your own machines with `ygg link`, memories travel **between those two machines** over your own network โ€” directly, over TLS with a pinned certificate, with no relay and no server of ours in the path. Until you run that command nothing listens beyond loopback.
</details>

<details>
<summary><b>Can a malicious repo plant instructions in my memory?</b></summary>

Recalled memory is injected as clearly-framed *reference notes, not instructions*, with where each note came from and a marker when it belongs to a different project โ€” so the agent treats it as data and asks before acting on anything in it. Secrets are redacted from transcripts before distillation and refused on write. Still: memory is only as trustworthy as what wrote it โ€” review `ygg review` now and then.
</details>

<details>
<summary><b>Does it automatically remember everything?</b></summary>

No โ€” by design. Retrieval is automatic; *writing* is deliberate (the agent calls `ygg_remember` for durable lessons). Capture-everything pollutes memory and burns tokens, so we don't. The optional background model consolidates what's already saved (propose-only).
</details>

<details>
<summary><b>Do I need a GPU or an API key?</b></summary>

No. The default is pure lexical search โ€” zero dependencies, instant. Semantic search is opt-in and uses a *local* model via Ollama. The installer recommends one that fits your hardware.
</details>

<details>
<summary><b>How heavy is it, and what does it cost in tokens?</b></summary>

The engine idles at **~21 MB RAM** (lexical default) with ~0% CPU; disk is tens of KB per memory. Session start injects ~300 tokens; each tool call returns a small snippet. All heavy work (indexing, embeddings, consolidation) runs off-LLM on your machine.
</details>

<details>
<summary><b>Can I edit or delete memories by hand?</b></summary>

Yes. Memories materialize to Markdown notes in an Obsidian vault โ€” read, edit, or remove them like any file. Governance and review never delete anything โ€” they archive, which is reversible. Only you hard-delete, explicitly, with `ygg delete` (and on linked machines that delete reaches both).
</details>

## ๐Ÿšฆ Status & roadmap

**Alpha.** The happy path and the governance loop are gate-tested (`scripts/run_gates.sh`); not yet hardened for multi-user or production use. macOS today; Linux/Windows service installers are built and in final on-device testing.

Shipped recently: ๐Ÿ”— machine-to-machine memory (`ygg link`) ยท relation graph (`ygg relate`: `solves` / `supersedes` / `contradicts`).

Next: ๐Ÿ›ฐ๏ธ cross-surface (one memory across CLI, web, and phone โ€” the HTTP facade is in, see [docs/cross-surface.md](./docs/cross-surface.md)) ยท ๐Ÿง Linux/Windows GA.

## ๐Ÿค Contributing

Issues and PRs welcome. Run `scripts/run_gates.sh` and `python3 -m unittest discover -s tests` before submitting โ€” all gates must stay green.

## ๐Ÿ“œ License

**GNU AGPL v3.0** โ€” see [LICENSE](./LICENSE). Free and open source: use, modify, self-host, redistribute. If you modify it or offer it as a network service, you must release your source under the same license.

TDQS

A4.4/5.0

Scored across 6 tools

Disambiguation4/5

The tools are largely distinct: health, remember, and materialize are clearly separate, while recall, bootstrap, and search all retrieve memories but are differentiated by scope (cross-project vs. project context vs. targeted query). Descriptions include explicit guidance on when to use each, reducing but not eliminating the chance of confusion.

Naming Consistency4/5

All tools share the consistent 'ygg_' prefix and use lowercase snake_case. Most follow a verb-based pattern (recall, bootstrap, search, remember, materialize), though 'ygg_health' is a noun rather than a verb, creating a minor inconsistency.

Tool Count5/5

Six tools is a well-scoped count for a memory server. Each tool earns its place: health monitoring, cross-project recall, project priming, targeted search, memory persistence, and file export. There are no redundant or filler tools.

Completeness3/5

The server covers create, read/retrieve, and export operations well, but lacks explicit update and delete capabilities for stored memories. Automatic near-duplicate merging partially addresses updates, but there is no clear way to edit or remove a stale or incorrect memory, which is a notable lifecycle gap.

Maintenance

ActivityMaintained
ResponsivenessNo issues