[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-1119":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":10,"language":11,"languages":10,"totalLinesOfCode":10,"stars":12,"forks":13,"watchers":14,"openIssues":15,"contributorsCount":16,"subscribersCount":16,"size":16,"stars1d":17,"stars7d":14,"stars30d":18,"stars90d":16,"forks30d":16,"starsTrendScore":19,"compositeScore":20,"rankGlobal":10,"rankLanguage":10,"license":21,"archived":22,"fork":22,"defaultBranch":23,"hasWiki":22,"hasPages":22,"topics":24,"createdAt":10,"pushedAt":10,"updatedAt":34,"readmeContent":35,"aiSummary":36,"trendingCount":16,"starSnapshotCount":16,"syncStatus":17,"lastSyncTime":37,"discoverSource":38},1119,"graymatter","angelnicolasc\u002Fgraymatter","angelnicolasc","Three lines of code to give your AI agents persistent memory. Reduce 90% token consumption while also maintaining quality.","",null,"Go",388,32,5,4,0,2,34,6,54.46,"MIT License",false,"main",[25,26,27,28,29,30,31,32,33],"agent-memory","ai-agents","ai-tools","claude-code","cli","go","offline-first","persistent-memory","self-hosted","2026-06-12 04:00:07","\u003Cdiv align=\"center\">\n  \u003Cimg width=\"1500\" height=\"276\" alt=\"graymatter-banner\" src=\".github\u002Fassets\u002Fgraymatter-banner-1.jpg\" \u002F>\n\u003C\u002Fdiv>\n\n\u003Ch1 align=\"center\"> GrayMatter \u003C\u002Fh1>\n\n\n\u003Cp align=\"center\">\n  \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fangelnicolasc\u002Fgraymatter\u002Factions\u002Fworkflows\u002Fci.yml\">\u003Cimg src=\"https:\u002F\u002Fgithub.com\u002Fangelnicolasc\u002Fgraymatter\u002Factions\u002Fworkflows\u002Fci.yml\u002Fbadge.svg\" alt=\"CI\">\u003C\u002Fa>\n  \u003Ca href=\"https:\u002F\u002Fpkg.go.dev\u002Fgithub.com\u002Fangelnicolasc\u002Fgraymatter\">\u003Cimg src=\"https:\u002F\u002Fpkg.go.dev\u002Fbadge\u002Fgithub.com\u002Fangelnicolasc\u002Fgraymatter.svg\" alt=\"Go Reference\">\u003C\u002Fa>\n  \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fangelnicolasc\u002Fgraymatter\u002Freleases\u002Ftag\u002Fv0.5.1\">\u003Cimg src=\"https:\u002F\u002Fimg.shields.io\u002Fgithub\u002Fv\u002Frelease\u002Fangelnicolasc\u002Fgraymatter\" alt=\"Latest Release\">\u003C\u002Fa>\n  \u003Cimg src=\"https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Fcoverage-73.5%25-brightgreen\" alt=\"Coverage 73.5%\">\n  \u003Cimg src=\"https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Fplatforms-linux%20%7C%20macOS%20%7C%20windows-blue\" alt=\"Platforms\">\n  \u003Cimg src=\"https:\u002F\u002Fgoreportcard.com\u002Fbadge\u002Fgithub.com\u002Fangelnicolasc\u002Fgraymatter\" alt=\"Go Report Card\">\n  \u003Cimg src=\"https:\u002F\u002Fimg.shields.io\u002Fgithub\u002Flicense\u002Fangelnicolasc\u002Fgraymatter\" alt=\"License\">\n\u003Cdiv align=\"center\">\n\u003Cbr \u002F>\n\n\u003Cstrong>Three lines of code to give your AI agents persistent memory and cut token usage by 90%.\u003C\u002Fstrong>\n\u003Cbr \u002F>\u003Cbr \u002F>\nOne binary. Drop it in. Run it. No Docker, no databases, no config files, no cloud accounts, no bullshit.\n\u003Cbr \u002F>\u003Cbr \u002F>\n\u003Cstrong>General-purpose MCP server. Zero vendor lock-in.\u003C\u002Fstrong>\n\u003Cbr \u002F>\nWorks with Claude Code, Cursor, Codex, OpenCode, Antigravity — and any MCP-compatible client.\n\u003Cbr \u002F>\nAlso a plain Go library if you don't use MCP.\n\u003Cbr \u002F>\u003Cbr \u002F>\nFree. Offline. No account required.\n\n\u003Cbr \u002F>\n\u003C\u002Fdiv>\n\n```go\nctx := context.Background()\nmem := graymatter.New(\".graymatter\")\nmem.Remember(ctx, \"agent\", \"user prefers bullet points, hates long intros\")\nfacts, _ := mem.Recall(ctx, \"agent\", \"how should I format this response?\")\n\u002F\u002F [\"user prefers bullet points, hates long intros\"]\n```\n\n---\n\n## Why\n\nEvery AI agent is **stateless by default**. Each run re-injects the full\nconversation history — and that history grows linearly. Two prompts in and you've already burned half of your daily quota.\n\nThat's not just a memory problem. That's a money and performance problem.\n\n\n**Mem0, Zep, Supermemory** solve this — but they're Python\u002FTypeScript-only\nand require a running server. The Go ecosystem has no production-ready,\nembeddable, zero-dependency memory layer for agents.\n\nThat gap is GrayMatter.\n\n\u003Cp align=\"center\">\n  \u003Cimg src=\".github\u002Fassets\u002Ftoken-reduction-chart1.jpg\" alt=\"GrayMatter-Chart1\" width=\"800px\" style=\"max-width: 900px;\">\n\u003C\u002Fp>\n\n\n\u003Cp align=\"center\">\n\u003Cstrong>~97% reduction in context tokens\u003C\u002Fstrong> — versus full-history injection.\u003Cbr>\nContext quality \u003Cem>improves\u003C\u002Fem> over time as consolidation surfaces only what matters.\u003Cbr>\nNo Docker. No Redis. No API key required for storage.\u003Cbr>\u003Cbr>\nDrop it in once. It auto-connects to \u003Cstrong>Claude Code, Cursor, Codex, OpenCode, Antigravity\u003C\u002Fstrong> — any MCP-compatible client picks it up automatically.\n\u003C\u002Fp>\n\n---\n\n## Observability\n\nYou can't improve what you can't see.\n\n`graymatter tui` opens a live terminal dashboard with everything your\nagent memory is doing — no extra setup required.\n\n\u003Cp align=\"center\">\n  \u003Cimg src=\".github\u002Fassets\u002Ftui-graymatter.jpg\" alt=\"GrayMatter-TUI\" width=\"900px\" style=\"max-width: 900px;\">\n\u003C\u002Fp>\n\n**What you get at a glance:**\n\n- **Facts** — total stored, distributed across agents\n- **Memory cost** — KB on disk (text + embeddings), not tokens\n- **Recalls** — cumulative access count across all sessions\n- **Health** — percentage of facts above relevance threshold (weight > 0.5)\n- **Token cost (30d)** — real spend breakdown by model, with cache hit rate\n- **Agent activity** — facts vs recalls per agent, side by side\n- **Weight distribution** — how consolidated your memory is over time\n- **Activity timeline** — facts created per day, last 30 days\n\nThe dashboard auto-refreshes every 5 seconds. Press `1–4` to switch tabs,\n`r` to force refresh, `q` to quit.\n\n---\n\n\n## Install\n\n**Binary (recommended):**\n\n```bash\n# Linux (x86_64)\ncurl -sSL -o graymatter.tar.gz https:\u002F\u002Fgithub.com\u002Fangelnicolasc\u002Fgraymatter\u002Freleases\u002Fdownload\u002Fv0.5.1\u002Fgraymatter_0.5.1_linux_amd64.tar.gz\ntar -xzf graymatter.tar.gz\nsudo mv graymatter \u002Fusr\u002Flocal\u002Fbin\u002F\n\n# Linux (ARM64)\ncurl -sSL -o graymatter.tar.gz https:\u002F\u002Fgithub.com\u002Fangelnicolasc\u002Fgraymatter\u002Freleases\u002Fdownload\u002Fv0.5.1\u002Fgraymatter_0.5.1_linux_arm64.tar.gz\ntar -xzf graymatter.tar.gz\nsudo mv graymatter \u002Fusr\u002Flocal\u002Fbin\u002F\n\n# macOS (Apple Silicon)\ncurl -sSL -o graymatter.tar.gz https:\u002F\u002Fgithub.com\u002Fangelnicolasc\u002Fgraymatter\u002Freleases\u002Fdownload\u002Fv0.5.1\u002Fgraymatter_0.5.1_darwin_arm64.tar.gz\ntar -xzf graymatter.tar.gz\nsudo mv graymatter \u002Fusr\u002Flocal\u002Fbin\u002F\n\n# Windows (PowerShell)\niwr https:\u002F\u002Fgithub.com\u002Fangelnicolasc\u002Fgraymatter\u002Freleases\u002Fdownload\u002Fv0.5.1\u002Fgraymatter_0.5.1_windows_amd64.zip -OutFile graymatter.zip\nExpand-Archive graymatter.zip -DestinationPath .\\graymatter_cli\n```\n\n**Go install:**\n\n```bash\ngo install github.com\u002Fangelnicolasc\u002Fgraymatter\u002Fcmd\u002Fgraymatter@latest\n```\n\n**Library:**\n\n```bash\ngo get github.com\u002Fangelnicolasc\u002Fgraymatter\n```\n---\n\n## MCP clients (drop-in)\n\n```bash\ngraymatter init\n```\n\nOne command auto-wires GrayMatter into every supported client at once.\nExisting entries from other MCP servers are **merged, not overwritten** —\nsafe to run in any repo.\n\n| Client | Config file auto-wired | Scope |\n|--------|------------------------|-------|\n| Claude Code | `.mcp.json` | project |\n| Cursor | `.cursor\u002Fmcp.json` | project |\n| Codex (OpenAI) | `~\u002F.codex\u002Fconfig.toml` | home |\n| OpenCode | `opencode.jsonc` | project |\n| Antigravity (Google) | `mcp_config.json` | project (opt-in: `--with-antigravity`) |\n\nNarrow down what gets wired:\n\n```bash\ngraymatter init --only claudecode,cursor     # whitelist\ngraymatter init --skip-codex --skip-opencode # blacklist\ngraymatter init --with-antigravity           # include opt-in clients\n```\n\nThen **restart your editor** (or toggle the MCP server off\u002Fon in its\nsettings). Five tools become available:\n\n| Tool | What it does |\n|------|-------------|\n| `memory_search` | Recall facts for a query |\n| `memory_add` | Store a new fact |\n| `checkpoint_save` | Snapshot current session |\n| `checkpoint_resume` | Restore last checkpoint |\n| `memory_reflect` | Add \u002F update \u002F forget \u002F link memories (agent self-edit) |\n\n> Agents using these tools should read **[docs\u002FAGENTS.md](docs\u002FAGENTS.md)** —\n> when to store vs. checkpoint, query patterns, anti-patterns, and the exact\n> per-tool parameter names (heads-up: `memory_reflect` uses `agent`, the\n> other four use `agent_id`).\n\n### Any other MCP-compatible client\n\nGrayMatter speaks plain MCP. If your client isn't on the table above,\npoint it at the binary:\n\n```bash\ngraymatter mcp serve              # stdio transport\ngraymatter mcp serve --http :8080 # HTTP transport\n```\n\nThe schema is identical to every other MCP server — `command` +\n`args: [\"mcp\", \"serve\"]`. No proprietary glue.\n\n### Global install (all projects)\n\nIf you'd rather not run `graymatter init` in every repo, drop the same\nJSON into the editor's global config — `~\u002F.cursor\u002Fmcp.json` for Cursor,\n`~\u002F.claude\u002Fmcp.json` for Claude Code:\n\n```json\n{\n  \"mcpServers\": {\n    \"graymatter\": {\n      \"command\": \"graymatter\",\n      \"args\": [\"mcp\", \"serve\"]\n    }\n  }\n}\n```\n\n`graymatter` must be on `PATH`. The `init` command handles this\nautomatically on Windows via the User `PATH` registry; on macOS \u002F Linux\nthe recommended install path `\u002Fusr\u002Flocal\u002Fbin` is already on `PATH`.\n\n---\n\n## How memories get stored\n\nThere are **four** ways a fact ends up in the store. You don't have to pick one — they compose:\n\n| Path | Who calls it | When to use |\n|------|--------------|-------------|\n| `mem.Remember(ctx, agent, text)` | Your code, explicitly | You already know the exact string worth keeping. |\n| `mem.RememberExtracted(ctx, agent, llmResponse)` | Your code, on raw LLM output | You want GrayMatter to pull atomic facts out of a full response for you (LLM-assisted; falls back to storing the raw text if no API key is set). |\n| `memory_reflect` (MCP tool) | The LLM itself, mid-session | Claude Code \u002F Cursor agents self-curate: add, update, forget, or link memories when they notice a contradiction, finish a task, or learn a preference. |\n| `Consolidate` (async, on by default) | Background goroutine | Summarises, decays, and prunes over time. Runs automatically after writes once `ConsolidateThreshold` is hit. |\n\n**Forgetting a single `Remember` call is not fatal.** `memory_reflect` lets the\nagent fix its own memory as it works, and `Consolidate` curates the store\nover time. That's why long interactive sessions in **Claude Code Desktop**\nand **Cursor** are a sweet spot for GrayMatter — not only 24\u002F7 autonomous\nagents. The LLM maintains its own memory through MCP.\n\n---\n\n## Library usage\n\nThree functions cover 95% of use cases. All methods accept `context.Context` as the first argument so timeouts and cancellation propagate end-to-end — no wrappers needed.\n\n```go\nimport \"github.com\u002Fangelnicolasc\u002Fgraymatter\"\n\nctx := context.Background()\n\n\u002F\u002F Open (or create) a memory store in the given directory.\nmem := graymatter.New(\".graymatter\")\ndefer mem.Close()\n\n\u002F\u002F Always check health in production — New() never panics, but it may degrade\n\u002F\u002F to no-op mode if the data dir is unwritable or bbolt fails to open.\nif !mem.Healthy() {\n    log.Fatalf(\"graymatter: %v\", mem.Status().InitError)\n}\n\n\u002F\u002F Store an observation.\nmem.Remember(ctx, \"sales-closer\", \"Maria didn't reply Wednesday. Third touchpoint due Friday.\")\n\n\u002F\u002F Retrieve relevant context for a query.\nfacts, _ := mem.Recall(ctx, \"sales-closer\", \"follow up Maria\")\n\u002F\u002F [\"Maria didn't reply Wednesday. Third touchpoint due Friday.\"]\n```\n\nContext propagates everywhere — timeouts and traces work as expected:\n\n```go\nctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)\ndefer cancel()\n\nif err := mem.Remember(ctx, \"agent\", \"observation\"); err != nil { ... }\nresults, err := mem.Recall(ctx, \"agent\", \"query\")\n```\n\n### Full agent pattern\n\n```go\nctx := context.Background()\nmem := graymatter.New(project.Root + \"\u002F.graymatter\")\ndefer mem.Close()\nif !mem.Healthy() {\n    log.Fatalf(\"graymatter: %v\", mem.Status().InitError)\n}\n\n\u002F\u002F 1. Recall before calling the LLM.\nmemCtx, _ := mem.Recall(ctx, skill.Name, task.Description)\n\nmessages := []anthropic.MessageParam{\n    {Role: \"system\", Content: skill.Identity + \"\\n\\n## Memory\\n\" + strings.Join(memCtx, \"\\n\")},\n    {Role: \"user\",   Content: task.Description},\n}\n\n\u002F\u002F 2. Call your LLM.\nresponse, _ := client.Messages.New(ctx, anthropic.MessageNewParams{...})\n\n\u002F\u002F 3a. If you already have a clean string worth keeping, store it directly.\nmem.Remember(ctx, skill.Name, \"Maria prefers Slack over email; replies within 2h.\")\n\n\u002F\u002F 3b. Or let GrayMatter pull atomic facts out of the raw response for you.\n\u002F\u002F     Uses ANTHROPIC_API_KEY if set; otherwise stores the raw text as a single fact.\nmem.RememberExtracted(ctx, skill.Name, responseText)\n```\n\n> Inside Claude Code \u002F Cursor you don't need either call — the LLM uses the\n> `memory_reflect` MCP tool to self-curate. See\n> [Claude Code \u002F Cursor (MCP)](#claude-code--cursor-mcp) below.\n\n### Config\n\n```go\nmem, err := graymatter.NewWithConfig(graymatter.Config{\n    DataDir:          \".graymatter\",\n    TopK:             8,\n    EmbeddingMode:    graymatter.EmbeddingAuto,  \u002F\u002F Ollama → OpenAI → Anthropic → keyword\n    OllamaURL:        \"http:\u002F\u002Flocalhost:11434\",\n    OllamaModel:      \"nomic-embed-text\",\n    AnthropicAPIKey:  os.Getenv(\"ANTHROPIC_API_KEY\"),\n    OpenAIAPIKey:     os.Getenv(\"OPENAI_API_KEY\"),\n    DecayHalfLife:    30 * 24 * time.Hour,        \u002F\u002F 30 days\n    AsyncConsolidate: true,\n})\n```\n\n---\n\n## CLI\n\n```bash\ngraymatter init                                    # create .graymatter\u002F + .mcp.json\ngraymatter remember \"agent\" \"text to remember\"    # store a fact\ngraymatter remember --shared \"text\"               # store in shared namespace (all agents)\ngraymatter recall   \"agent\" \"query\"               # print context\ngraymatter recall   --all \"agent\" \"query\"         # merge agent + shared memory\ngraymatter checkpoint list    \"agent\"             # show saved checkpoints\ngraymatter checkpoint resume  \"agent\"             # print latest checkpoint as JSON\ngraymatter mcp serve                              # start MCP server (Claude Code \u002F Cursor)\ngraymatter mcp serve --http :8080                 # HTTP transport\ngraymatter export --format obsidian --out ~\u002Fvault # dump to Obsidian vault\ngraymatter tui                                    # 4-view terminal UI\ngraymatter run agent.md [--background]            # run a SKILL.md agent file\ngraymatter sessions list                          # list managed agent sessions\ngraymatter plugin install manifest.json           # install a plugin\ngraymatter server --addr :8080                    # REST API server\n```\n\nGlobal flags: `--dir` (data dir), `--quiet`, `--json`\n\n---\n\n\n\n## Memory lifecycle\n\n```\nRecall(agent, task)          ← hybrid: vector + keyword + recency → top-8 facts\n    ↓\nInject into system prompt    ← your 3 lines of code\n    ↓\nAgent runs\n    ↓\nRemember(agent, observation) ← store key facts during\u002Fafter run\n    ↓\nConsolidate() [async]        ← summarise + decay + prune (LLM optional)\n```\n\nConsolidation is the only \"smart\" step. Everything else is deterministic.\nWithout consolidation, GrayMatter still works — it just doesn't compress over time.\n\nConsolidation auto-enables when `ANTHROPIC_API_KEY` is set. To use Ollama:\n\n```go\ncfg := graymatter.DefaultConfig()\ncfg.ConsolidateLLM = \"ollama\"\n```\n\n---\n\n\n## Token efficiency\n\nNumbers produced by `go run .\u002Fbenchmarks\u002Ftoken_count` — real Recall calls,\nkeyword embedder, no LLM required:\n\n| Sessions | Full injection | GrayMatter | Reduction |\n|----------|---------------|------------|-----------|\n| 1        | ~80 tokens    | ~80 tokens | 0% |\n| 10       | ~630 tokens   | ~550 tokens | 12% |\n| 30       | ~1,880 tokens | ~550 tokens | 71% |\n| 100      | ~6,960 tokens | ~670 tokens | **90%** |\n\nEach \"session\" = one paragraph-length agent observation (~60 words).\nGrayMatter always injects only the top-8 most relevant observations for the query.\nWith vector embeddings the recall precision improves, maintaining similar reduction ratios.\n\nReproduce locally:\n\n```bash\ngo run .\u002Fbenchmarks\u002Ftoken_count\n```\n\n\n---\n\n## Storage\n\n| Layer | Tech | What it holds |\n|-------|------|--------------|\n| KV store | bbolt (pure Go, ACID) | Sessions, checkpoints, facts, metadata, KG |\n| Vector index | chromem-go (pure Go) | Semantic embeddings, hybrid retrieval |\n| Export | Markdown files | Human-readable, git-friendly, Obsidian-compatible |\n\nSingle file: `~\u002F.graymatter\u002Fgray.db`  \nSingle folder: `.graymatter\u002Fvectors\u002F`\n\nNo migrations. No schema versions. Append-only with decay-based eviction.\n\n---\n\n## Embeddings\n\nGrayMatter degrades gracefully. It works without any embedding model.\n\n| Mode | When |\n|------|------|\n| **Ollama** (default) | Machine has Ollama running with `nomic-embed-text` |\n| **OpenAI** | `OPENAI_API_KEY` set, Ollama not available |\n| **Anthropic** | `ANTHROPIC_API_KEY` set, Ollama and OpenAI not available |\n| **Keyword-only** | No embedding available — TF-IDF + recency, zero deps |\n\nAuto-detection order in `EmbeddingAuto` mode: Ollama → OpenAI → Anthropic → keyword.\n\n```bash\n# Pull the embedding model once (Ollama):\nollama pull nomic-embed-text\n\n# Or set an API key (OpenAI or Anthropic):\nexport OPENAI_API_KEY=sk-...\nexport ANTHROPIC_API_KEY=sk-ant-...\n```\n\n\n\n---\n\n## Testing\n\nThe full test suite requires no LLM and no network — every test uses\n`t.TempDir()` with a keyword embedder or injected stubs. Runs clean on\nLinux, macOS, and Windows in CI.\n\n```bash\n# Core library\ngo test -count=1 -timeout=120s .\u002Fpkg\u002Fmemory\u002F...\n\n# CLI \u002F server \u002F plugins\ncd cmd\u002Fgraymatter && go test -count=1 -timeout=120s .\u002Finternal\u002F...\n```\n\n| Package | Tests | What's covered |\n|---------|-------|----------------|\n| `pkg\u002Fmemory` | 42 unit tests + 3 fuzz targets | Store lifecycle, hybrid recall, RRF fusion, decay math, semaphore, concurrent writes, vector paths, dimension guard |\n| `internal\u002Fharness` | 21 | Agent file parsing, retry\u002Fbackoff, session recovery |\n| `internal\u002Fkg` | 21 | Graph CRUD, entity extraction, weight decay, Obsidian export |\n| `internal\u002Fserver` | 11 | All REST endpoints, concurrent remember\u002Frecall, cancelled-context requests |\n| `internal\u002Fplugin` | 10 | Install, list, remove, E2E echo plugin binary |\n\n**Fuzz targets** (`pkg\u002Fmemory`): `FuzzTokenize`, `FuzzUnmarshalFact`, `FuzzKeywordScore` — each with a seeded corpus so they run deterministically in CI and can be extended with `go test -fuzz`.\n\n**Core library coverage: 73.5%** (CI gate: ≥ 70%). Measured without mocks — real bbolt + chromem-go instances in a temp directory.\n\nToken-reduction benchmark (also zero deps):\n\n```bash\ngo run .\u002Fbenchmarks\u002Ftoken_count\n```\n\n---\n\n## Build from source\n\n```bash\ngit clone https:\u002F\u002Fgithub.com\u002Fangelnicolasc\u002Fgraymatter\ncd graymatter\nCGO_ENABLED=0 go build -ldflags=\"-s -w -X main.version=dev\" -o graymatter .\u002Fcmd\u002Fgraymatter\n```\n\nOutput: single static binary, ~10 MB, no runtime dependencies.\n\n---\n\n## Metrics & APM hooks\n\n\nThe REST server (`graymatter server`) exposes a `\u002Fmetrics` endpoint powered by Go's standard `expvar` package — zero extra dependencies.\n\n```\nGET \u002Fmetrics\n```\n\n```json\n{\n  \"requests_total\":     {\"remember\": 120, \"recall\": 340, \"healthz\": 5},\n  \"request_latency_us\": {\"remember\": 4200, \"recall\": 1800},\n  \"facts_total\":        {\"stored\": 120},\n  \"recall_total\":       {\"served\": 340}\n}\n```\n\nFor library users, `memory.StoreConfig` exposes hooks for APM integration:\n\n```go\nstore, err := memory.Open(memory.StoreConfig{\n    DataDir:       \".graymatter\",\n    DecayHalfLife: 30 * 24 * time.Hour,\n\n    \u002F\u002F Called after every Recall with agent ID, query, result count, and latency.\n    OnRecall: func(agentID, query string, n int, d time.Duration) {\n        metrics.RecordHistogram(\"graymatter.recall.latency\", d.Seconds())\n    },\n\n    \u002F\u002F Called after every successful Put with agent ID, fact ID, and latency.\n    OnPut: func(agentID, factID string, d time.Duration) {\n        metrics.Increment(\"graymatter.facts.stored\")\n    },\n\n    \u002F\u002F Called when a vector upsert fails after the bbolt write succeeded.\n    \u002F\u002F The fact is durably queued and retried on the next reconcile tick.\n    OnVectorIndexError: func(agentID, factID string, err error) {\n        log.Printf(\"vector index lag: agent=%s fact=%s err=%v\", agentID, factID, err)\n    },\n\n    \u002F\u002F How often to drain the pending-vector queue (default 30s, 0 disables).\n    VectorReconcileInterval: 30 * time.Second,\n\n    \u002F\u002F Routes internal log events to any standard logger.\n    Logger: slog.NewLogLogger(slog.Default().Handler(), slog.LevelDebug),\n\n    \u002F\u002F Swap the vector backend entirely — bring your own Qdrant, pgvector, etc.\n    VectorBackend: myQdrantAdapter,\n})\n```\n\n---\n\n\n## What GrayMatter is NOT\n\n- Not tied to any vendor. It's an MCP server + Go library — not a Claude-Code-only or Cursor-only tool.\n- Not a framework. Not an agent runner. Not a replacement for your existing tooling.\n- Not a hosted service. Not a SaaS. Not a cloud product.\n- Not a knowledge base UI. Not Notion. Not Obsidian.\n- Not trying to win the enterprise memory market.\n\nIt is exactly one thing: **the missing stateful layer for Go CLI agents**,\npackaged as a library you import in three lines.\n\n---\n\n## Roadmap\n\n- [x] Library: `Remember` \u002F `Recall` \u002F `Consolidate`\n- [x] bbolt + chromem-go storage\n- [x] Ollama + OpenAI + Anthropic + keyword-only embedding\n- [x] Hybrid retrieval (vector + keyword + recency, RRF fusion)\n- [x] CLI: `init remember recall checkpoint export run sessions plugin server`\n- [x] MCP server (Claude Code \u002F Cursor) + `memory_reflect` self-edit tool\n- [x] Knowledge graph (entity extraction, node\u002Fedge linking, Obsidian export)\n- [x] Shared memory across agents (`--shared`, `--all` flags, `__shared__` namespace)\n- [x] REST API server mode (`graymatter server --addr :8080`)\n- [x] Plugin system (JSON line protocol, `graymatter plugin install\u002Flist\u002Fremove`)\n- [x] 4-view Bubble Tea TUI (Memory \u002F Sessions \u002F Knowledge Graph \u002F Stats)\n- [x] Context-propagation API — all public methods accept `context.Context` (ctx-first, uniform)\n- [x] `Healthy()` \u002F `Status()` — observable no-op mode; production callers detect init failures\n- [x] Durable vector reconciliation — `bucketPendingVector` closes the crash window; background reconcile loop (configurable interval); `PendingVectorCount()` for health introspection\n- [x] `AdvancedStore` interface — narrow, stable public surface for CLI\u002FMCP\u002FTUI; internal refactors no longer break public API\n- [x] `ConsolidateThreshold` default lowered to 20 — consolidation fires in demos and first-week production use\n- [x] `OnVectorIndexError` \u002F `VectorReconcileInterval` hooks for durable vector retry observability\n- [x] Pluggable `VectorStore` interface (swap chromem-go for Qdrant, pgvector, etc.)\n- [x] expvar `\u002Fmetrics` endpoint — zero-dep, stdlib-only observability\n- [x] `OnRecall` \u002F `OnPut` \u002F `Logger` hooks for APM integration\n- [x] Embedding dimension guard — warns on provider switch instead of silent corruption\n- [x] go.work workspace — core library imports zero TUI\u002FCLI dependencies\n- [x] Three-platform CI (Linux, macOS, Windows) + 73.5% coverage gate\n- [x] Fuzz testing: `FuzzTokenize`, `FuzzUnmarshalFact`, `FuzzKeywordScore`\n- [ ] Ollama-backed consolidation LLM (Ollama as summariser, not just embedder)\n- [ ] WebSocket streaming for REST API\n\n---\n\n*GrayMatter — v0.5.1 — April 2026*\n","GrayMatter 是一个为AI代理提供持久内存的工具，通过三行代码即可实现，并能减少90%的token消耗。其核心功能包括使用Go语言编写，无需Docker、数据库或配置文件等额外依赖，支持离线运行且不锁定特定供应商。该工具特别适用于需要降低API调用成本同时保持高质量上下文的应用场景，如基于Claude Code、Cursor、Codex等MCP兼容客户端构建的AI助手项目。此外，它也可以作为一个独立的Go库被集成到其他应用中，非常适合追求高效简洁解决方案的开发者。","2026-06-11 02:41:43","CREATED_QUERY"]