| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Name | Name | Last commit date | ||
|---|---|---|---|---|
Durable memory for daily AI coding · 1-second recall · Trustworthy confidence levels · Anchor-based freshness · Optional token-goat seam
Local-first, defense-in-depth memory that remembers what your AI coding agent keeps forgetting between sessions.
You tell your AI "we use pnpm not npm" and it forgets. Every session. Then it runs npm install and corrupts the lockfile. You re-explain that you prefer 2-space indentation, and the next day it defaults to tabs. These are not oversights — the agent genuinely does not see a record of these decisions after a compaction.
Mem stores them. Locally, in your own SQLite database. Each fact carries a trust level and an anchor (a read-only predicate that tests whether the fact is still true). On recall, Mem re-validates anchors and surfaces only the facts that are fresh and trustworthy, with a confidence caveat so your agent never treats a hint as ground truth when it should not.
Works with Claude Code, Copilot CLI, Copilot in VS Code, Codex, and any agent that can run a shell command — integration guides for the first four live in docs/integrations/. There is also an optional seam -- a one-way integration point, where the other tool calls mem and mem knows nothing about it -- with token-goat, a sister CLI that gives agents narrow-slice code/doc reads to cut context burn. Token-goat reads mem epoch (a cache-invalidation key) and optionally consumes memory via the published TGMEM/2 wire format, which a future host tool can adopt.
Install:
npm install -g token-goat-mem mem --help
Building from source, requirements, and verifying the install: Install.
Built and maintained by DFK Helper. Free under PolyForm Noncommercial. If it saves your tokens, or your sanity, drop a star at the top of this page.
Install · CLI · Walkthrough · How it works · Anchors · Token-goat integration · Disclaimer & License
Your AI coding agent accumulates durable knowledge that keeps evaporating:
Today this knowledge is lost at each session boundary. The agent re-asks, re-derives, or — worst — forgets and does the wrong thing. A confident wrong memory is worse than no memory at all. If Mem surfaces a stale preference as ground truth ("you use npm" three months after you switched to pnpm), your agent acts on it and corrupts your lockfile.
The defining engineering problem is not retrieval — it is correctness and staleness. Mem solves both.
| Before | After |
|---|---|
| Agent re-reads the same preference every session | stored pref (verify): uses pnpm, not npm — mem show <id> — one-line hint with confidence |
| Agent does the wrong thing because it forgot a decision | mem remember "Postgres chosen over Mongo for relational JOIN queries" --kind decision persists it; mem recall surfaces it with provenance and age |
| Mixed signals on project setup (old README says npm, lockfile says pnpm) | Anchor predicates test the actual state (which lockfile is newer, git history); anchor-contradicted facts are excluded from recall and flagged in mem review for human resolution, never surfaced as ground truth |
| Every session starts cold | mem recall --hint-format embeds prior facts into your AI context at startup (~5-10 lines per session) |
| Session compaction forgets preferences | Facts live in SQLite, outside any context window; mem pin <id> additionally exempts a fact from time-decay. Unpinned preferences decay in confidence over time if not re-affirmed |
| Stale facts invisible until they cause damage | mem review flags anchor-contradicted facts before they become silent bugs |
Requirements: Node.js 18 or later
npm install -g token-goat-mem
Or from source:
git clone https://github.com/DFKHelper/token-goat-mem.git cd token-goat-mem npm install npm run build npm link
No daemon, no tray icon, no setup wizard. Mem is a short-lived CLI process.
mem --version mem epoch
mem --version prints the installed version; mem epoch prints a number (creating the database on first run), which confirms the CLI and the SQLite store are wired correctly.
Everything is stored in a single SQLite database at ~/.mem/mem.db. Set TOKEN_GOAT_MEM_HOME to relocate it (the test suite uses this to isolate itself from your real data). No network calls, ever.
On POSIX systems the directory is created 0700 and the database 0600, so the store is readable only by you even on a shared machine -- and an existing home created by an earlier version is tightened on the next run. Windows relies on the ACL ~/.mem inherits from your user profile, which restricts it the same way.
| Command | What it does |
|---|---|
| mem remember <text> | Store a new fact. --kind preference|decision|fact|correction (required), --subject <key> + --value <value> (paired, for contradiction detection), --anchor <predicate> (optional), --scope global|project|path (default global), --source-ref <ref>, --root <path>, --path <file> (required when --scope path, rejected otherwise — the file/directory the fact binds to, resolved against --root; without it a --scope path fact would bind to --root itself, i.e. behave exactly like --scope project). |
| mem suggest <text> | Same flags as mem remember, but always stores the fact pending (captureSuggested, not captureExplicit) — never auto-promoted, confirm via mem review --promote <id>. |
| mem scan-session | Scan a session transcript for durable-statement sentences and file each as pending — the capture half of the Claude Code seam, installed by mem init as both a Stop and a PreCompact hook (the same command under two triggers -- Stop never fires for a session that is killed or interrupted rather than ending a turn, and by the time it does fire on a long session everything before the last compaction boundary has been summarized away; re-scanning the overlap files nothing twice, since a candidate whose text is already stored is skipped before capture -- except when the match is still pending, where the repeat is recorded as a sighting instead of discarded: see mem review's row for what that means). Deterministic sentence matching against a fixed opener table (remember that, from now on, always/never, don't, we decided, we should/let's + always/never, decision:, rule:, correction:, that's wrong, that is wrong, that's outdated, that's no longer true), no model, so the same transcript always yields the same candidates. An opener must still be the first thing said, past a closed list of discourse fillers (please, also, so, ok, note that) that may precede it — enough to catch "Please always run the linter", not enough to let "I never got that to work" through. The stored text is the whole sentence, filler included. Nothing reaches active without mem review --promote <id>, and the dedup matches on fact text so a rejected suggestion is never re-filed by a later scan. Only the human's own text is read: tool results, <system-reminder> injections, slash-command payloads, relayed subagent reports, and compaction summaries are all stored under the user role and are all excluded. --hook-stdin (take transcript_path from a hook envelope), --transcript <path> (scan a named file instead), --root <path>, --scope global|project|path (default project), --quiet (emit nothing on success — a Stop hook's stdout would otherwise land in the session it just read). Each fact is dated from the transcript entry's own timestamp when it carries one, not from the moment of the scan -- re-scanning an archived transcript would otherwise back-date nothing and mis-age everything, skewing preference decay, the --stale cutoff, recall's recency tie-break and the pending review order by the transcript's whole age. An entry with no timestamp, or one that is malformed or future-dated, falls back to now without failing the scan. The sources row's stored_at stays the scan time: when a thing was said and when mem stored it are different columns. |
| mem export | Writes stored facts to stdout. --format json|md (default json): json is the full-fidelity envelope ({ schemaVersion, exportedAt, facts }) mem import --from-json reads back, preserving id, status, confidence, anchor, subject, and value -- pair it with that command for backup/restore or full-fidelity migration between stores, and treat it as the stable machine-readable surface; a superseded fact additionally carries superseded_by (the winning fact's id, or null when this store itself has no recorded winner), so mem import --from-json can restore the edge without re-running contradiction resolution; md is a shareable, git-reviewable rendering for humans, not a backup -- mem import --from-md reads it back, but every bullet lands pending and only the fact text survives, id/status/confidence/anchor/subject/value are all lost. --kind, --status (comma-separated), --subject, --scope filter which facts are exported (default: every fact, any status). |
| mem import --from-md <path> | Advisory only. Parses a markdown file (CLAUDE.md-style) for -/* bullet lines that look like preference/decision statements and imports each as a pending, source_type: "derived" fact — the same trust path as any other suggested candidate; never auto-promoted, no bulk-promote shortcut. Confirm each import via mem review --promote <id>. --dry-run (report candidates without writing), --root <path>, --scope global|project|path (default project), --kind (default preference), --path <file> (required when --scope path, rejected otherwise — every bullet in this run binds to the same file/directory). Re-importing the same file skips bullets already imported at the same file:line + text, reported as skipped (duplicate). A candidate whose text already matches a fact in the store (the same check mem scan-session uses) is reported as skipped (already known) instead -- --dry-run cannot predict that kind of skip without opening the store, and says so. When that matched fact is still pending, the same sighting mem scan-session records is recorded here too (see mem review's row). --captured-at <iso> back-dates every fact in the run to when the file was actually written (--captured-at "$(git log -1 --format=%aI -- CLAUDE.md)") instead of now; a malformed or future value fails the whole run with exit 1 rather than being reported per bullet. |
| mem import --from-json <path> | Full-fidelity. Imports a mem export file, preserving each fact's original id, status, confidence, and captured_at exactly — unlike --from-md, an imported fact keeps whatever status it was exported with (including already-active), not forced to pending. A superseded fact's superseded_by (when the export recorded one) is restored as a fresh audit-log edge once every fact in the file has landed, so a winner named later in the same file, or already present in the target store, still resolves; a winner id that resolves nowhere is left alone rather than asserted. Still runs the same secret screening before writing. Idempotent: a fact whose id already exists in the target store is skipped as a duplicate, safe to re-run. --dry-run (report candidates without writing -- applies the same root-bounding and secret screening the real import does, and reports would-skip with the real refusal reason; duplicate-id conflicts are the one check it cannot make, since that needs the store, and the output says so), --root <path> (resolves .mem/allowlist for the secret screening, and bounds which scopeRoot values an imported fact may carry -- a fact bound outside it is skipped). Exactly one of --from-md/--from-json is required. |
| mem recall [query] | Retrieve facts by relevance with trust levels and freshness verdicts. Caps non-withheld results at 20 by default (a trailing showing N of M line appears when truncated) — pending/contested/contradicted facts are never subject to this cap, so a fact needing attention is never silently hidden. --kind, --subject, --scope, --hint-format (TGMEM/2 wire format for token-goat), --context-files <a,b> (scope=path matching, --hint-format only), --age-days <n>, --limit <n> (overrides the default 20), --root <path> (facts are bound to the project they were captured in: a project-scoped fact surfaces only from its own root, a path-scoped one only from a root containing its file, and global facts from anywhere), --stable (deterministic id-sorted output instead of relevance/recency order), --hint-style <full|terse> (default full; terse drops the CTA, shortens kind labels to pref/dec/fact/corr, and elides a body past 140 characters to ... (+N chars: mem show <id>) -- never mid-surrogate-pair, and full never elides), --since-epoch <n> (only include facts written after write-epoch n), --entity <value> (only facts carrying that extracted entity -- a file path, CLI flag, constant, version, or identifier -- matched case-insensitively; repeatable, and repeats AND together, so --entity src/cli.ts --entity --hint-format keeps only facts mentioning both; see mem facets --list-entities for what is available, and note it cannot be combined with --hint-format). Default (full) output ends with one trailing footer line (mem show <id> for detail; mem review to resolve contested/pending) instead of repeating a CTA per line. Ranking also consults the entity layer without any flag: identifiers in the query itself are matched against stored entities and fused as one more rank list, because BM25 stems src/retrieval.ts down to src/retriev/ts and cannot otherwise tell the fact naming that file from one merely using those three words. It is a vote and not an override -- a fact that only carries the identifier will not displace one that carries it and matches the rest of the query -- and it is silent for a query containing no identifier at all. |
| mem list | Fact IDs and one-line summaries. Caps at 20 by default (a trailing showing N of M line appears when truncated). --kind, --status (comma-separated), --subject, --scope, --limit (overrides the default 20), --json (machine-readable, unstable pre-1.0 -- shape may change; same fact shape as mem export minus embedding, plus total/truncated; use mem export for a stable machine-readable surface). |
| mem show <id> | One fact in full: text, provenance, anchor and its current freshness verdict. --root <path>, --related (also print the facts sharing the most entity/topic terms with this one, entity matches weighted above topic matches, target and superseded facts excluded, scope-contained to --root -- an association aid, not a ground-truth channel; pending/contested neighbours appear but stay labelled as such), --json (machine-readable, unstable pre-1.0; adds a freshness verdict that plain mem export/mem list --json do not, plus a sources array fed only by mem scan-session and mem import --from-md -- each writes one screened, truncated excerpt of the raw material (a user turn, a markdown line) the fact was pulled from; mem remember/mem suggest never write one, since the caller's own text already is the fact. [] therefore covers two cases: no source was recorded for this fact, or it came from a path that never records one). Also prints history: every audit row for the fact, oldest first. That is where an edited fact's previous text lives, in full -- mem edit overwrites in place, so nothing else in the store retains it. With --related also passed, --json additionally carries related: derived-at-read-time neighbours as above, each with its status and a shared-term score; [] means this fact shares no term with anything else in scope, not that the field was skipped. |
| mem review | Pending, contested, anchor-contradicted, and unanchored-but-checkable facts for human resolution. The pending bucket sorts most-restated first: mem scan-session/mem import --from-md record a sighting (a sightings counter plus its own screened source excerpt, both written together) whenever a candidate's text matches a fact already pending, instead of discarding the repeat -- a pending fact sighted again prints sighted again <n> time(s) since beneath its summary line. Sightings are evidence for the human reading this bucket only; they never promote a fact, change its status, or feed recall -- a pending fact promotes only through --promote, however many times it has been sighted. A pending fact with a recorded source prints its newest excerpt beneath the summary line; a pending fact that is a correction, or that carries a subject, also prints may contradict <id> "<text>" naming the single best-matching live (active/pinned) fact it may contradict once promoted -- the same entity/topic-weighted lookup mem show --related runs, narrowed to one match; a label only, it never supersedes, changes status, or promotes anything. An unanchored-but-checkable fact prints a paste-ready mem edit <id> --anchor "file-exists <path>" command (carrying --force when the fact is source_type=user, since mem edit refuses those without it) whenever the suggested predicate would already evaluate affirmed. --promote <id> / --reject <id> act on pending and contested facts — promoting a contested fact resolves the contradiction in its favor and supersedes its rivals, rejecting one reinstates the survivor; --undo <id> reverses a --reject, restoring the fact to the status it had before (rejections only -- mem forget is a considered decision, not a review slip, and is refused by name); promoting a pending or contested fact with no subject/value prints a caveat noting contradiction resolution keys on those, so an unkeyed fact can never be superseded; --root <path>; --summary (print per-bucket counts instead of full listings); --section <pending|contested|contradicted|pins|unanchored> (restrict output to one bucket); --since-epoch <n> (only include facts written after write-epoch n). |
| mem forget <id> | Soft-delete a fact (marks superseded, kept for audit) and audit-log it. Bumps epoch. |
| mem edit <id> | Change a fact's --text, --subject/--value (paired), --anchor, --scope, or (when rebinding to --scope path) --path <file> (required when --scope path is given, rejected otherwise). Bumps epoch. A source_type=user fact -- one you stated yourself -- requires --force, and the override is recorded in the audit log; a derived fact does not, so the guard is scoped rather than a blanket block. --undo reverses the most recent edit, restoring only the fields that edit touched (it takes no other option, refuses anything whose last recorded action was not an edit, and refuses an edit written before the prior value was recorded rather than restoring nothing and reporting success). |
| mem pin <id> | Exempt a fact from time-decay (still subject to contradiction/anchor suppression), and reserve it one of 2 guaranteed --hint-format slots -- past those two, extra pins compete on relevance like any other fact. Only an active (or already-pinned) fact can be pinned: pinning is a promotion to maximal trust, so a pending, contested, or superseded fact must be resolved through mem review first rather than pinned around it. |
| mem used <id...> --session-id <id> | Record that facts recalled in a session were actually useful. Feeds recall ranking as a third rank list fused alongside BM25 (rank-based, so the signal cannot drift as the store grows). --session-id is required and names the session the recall was surfaced under -- mem is a short-lived process with no notion of a current session. Naming a fact never surfaced in that session says so and exits 0 rather than failing. Idempotent: marking twice does not double-count. Bumps epoch when it actually stamps a row: the usefulness signal feeds recall ranking, so a cached recall computed before the stamp is out of date. Only reachable where a session id is known. A --hint-format response logged under a session (--session-id <id>, or --hook-stdin reading a hook envelope's session_id) carries a ready-to-run mem used ... invocation in its footer line naming that session and the facts just sent -- Claude Code's installed hooks pass --hook-stdin, so this is live wherever mem init claude-code ran. The shared markdown-style wiring for the other supported tools (codex, copilot-cli, copilot-vscode, copilot-visual-studio, copilot-jetbrains) invokes plain mem recall --hint-format --root . with no session id: no recall_log row is ever written there, the footer carries no invocation, and nothing can be marked useful until that wiring passes one too. |
| mem epoch | Print the current write epoch (monotonic, bumped on every write). --gc runs the retention pass first: persists contradiction resolutions, prunes superseded facts/sources/audit rows, applies preference decay. A rejection tombstone -- a superseded fact whose prior status was pending, i.e. the record of mem review --reject -- is never pruned by either bound: pruning it would let the same suggestion be filed pending again, silently reversing the rejection. This makes rejection-tombstone growth unbounded by age or count, bounded in practice only by how often facts get rejected. |
| mem consolidate | Report facts that restate each other, so a store that has accreted three phrasings of one preference can collapse to one. Deterministic and offline: clusters by Jaccard similarity over the fact_terms topic layer (mem facets), never a model call. Only facts of the same kind and the same scope binding are ever compared -- a deliberate blind spot the two passes below run alongside this one to close, without loosening the comparison itself. Dry run by default -- bare mem consolidate prints the clusters and changes nothing; --apply marks every loser superseded (soft-delete, audit-logged, kept in the store), keeping the pinned member, else the most confident, else the newest. A pinned fact is never superseded, only listed. Alongside the Jaccard pass, the default (non---stale, non---cross-project) run also reports any live project-scope fact whose text exactly matches a same-kind global fact word for word -- exact normalizeFactText equality, not Jaccard, since a scope-family comparison this loose would let unrelated same-kind facts merge across every project -- but only once their subject and value also agree: two facts can share exact wording ("the default branch name") while one is a global default and the other a project-scope override of a different value, and that pair is a correction, never a duplicate, so it is left alone by both this pass and --cross-project below. --apply supersedes the project-scope copy, keeping the global one (widening a fact's own scope is not something this pass invents on its own). --cross-project runs a third, report-only pass instead, with no --apply path of its own: same-kind, same-text facts (subject and value agreeing, per the same rule) restated under two or more distinct projects, printing a paste-ready mem edit <id> --scope global for the newest occurrence -- widening a fact out of its own project is the user's call, never this command's. --threshold <0-1> sets the Jaccard floor (default 0.5: at that floor two facts must share more of their combined topic vocabulary than they differ on, which separates restatements from facts that merely discuss the same subject; the measure is scale-free, so unlike a BM25 score the same number means the same thing however large the store grows). --stale runs the other pass instead: active facts captured before the --stale-days <n> cutoff (default 90) that have gone unsurfaced for at least that long and that no surviving recall_log row marks useful, same dry-run-then---apply contract. This is a windowed "unsurfaced for N days" question, not a lifetime "never surfaced" one -- a fact surfaced once, long before the window, becomes eligible again once it goes quiet for the window's length. The marked-useful exclusion is not bounded by --stale-days, but it is bounded by how long the evidence lives: mem epoch --gc rotates recall_log rows past its own retention regardless of the useful mark, after which the fact is governed by its last-surfaced date like any other. For a fact that must outlive its own recall history, mem pin is the durable answer -- pinned facts are excluded from this pass by construction. Neither pass ever hard-deletes anything. |
| mem dream | Report what a configured model thinks follows from several stored facts taken together -- the inference mem consolidate deliberately cannot do, since Jaccard over topic terms can see that two facts restate each other but never that a third thing follows from both. An evaluation surface, not a capture path: it writes nothing, and there is no flag that makes it -- the output is a report to read, and anything worth keeping is kept by typing mem remember. Off unless TOKEN_GOAT_MEM_DREAM_URL (an OpenAI-compatible chat-completions endpoint) and TOKEN_GOAT_MEM_DREAM_MODEL are set; TOKEN_GOAT_MEM_DREAM_API_KEY is optional, so a local endpoint that wants no auth is sent no header. With TOKEN_GOAT_MEM_DREAM_URL/_MODEL set, this command sends stored fact text to that endpoint -- point it at a local model if the store holds anything you would not paste into a hosted API. (Semantic recall/capture sends text too, when TOKEN_GOAT_MEM_EMBED_URL/_MODEL are configured; see the disclaimer below.) Sends active and pinned facts only, newest first, capped at 200 (a superseded fact is one the store has already decided is wrong, and an inference resting on it would carry the store's authority behind a retracted premise). Every returned candidate is checked before it is printed: it must cite at least two of the facts that were actually sent, by an index that resolves, and must not restate a fact already stored -- a candidate failing any of these is dropped rather than shown, so each printed from: id is one mem show can open. See Optional: mem dream. --timeout <ms> (default 60000), --json. Deliberately has no --root: dreaming reasons over the whole live store, and a flag that read as scoping while doing nothing would be worse than no flag. |
| mem facets | Extract, inspect, and list the structured entity/topic terms behind mem recall --entity. Terms are written automatically on capture and re-written on mem edit, so this command is for backfill and inspection, not routine use. No flags (or --backfill) extracts terms for facts that have none yet and reports the counts; --all re-extracts every fact -- the path after an extraction-rule change; --fact <id> shows one fact's entities and topics (short id prefixes work); --list-entities prints the distinct entities in the store with fact counts, most frequent first. Entities are stored with the spelling the fact used and matched case-insensitively, so --entity postgresql finds a fact that says PostgreSQL. The three modes are mutually exclusive. |
| mem embed | Compute and store embedding vectors for facts, so recall can rank semantically as well as lexically. Requires TOKEN_GOAT_MEM_EMBED_URL and TOKEN_GOAT_MEM_EMBED_MODEL (see Optional: semantic recall) and errors naming them when unset. Default: embeds only facts that have no vector yet, in batches, and reports embedded / skipped / failed. --all re-embeds every fact and rewrites the recorded model -- the migration path after changing models. --limit <n> bounds the work. A failed batch costs only that batch; a run in which nothing was embedded exits non-zero. |
| mem doctor | Read-only environment/DB health check: db path, WAL journal mode, foreign-key setting, schema tables, current epoch, fact counts by status, source/audit-log row counts, embedding configuration (endpoint host and model -- never the API key) and coverage, the hint budget (how many facts are recallable against the at-most-14 lines one --hint-format block carries, and how many are pinned against the 2 reserved slots), scope placement (project/path-scoped facts whose scope_root no longer exists on disk -- unreachable from any session, and healthy by every other line here), and Claude Code hook health (whichever mem binary PATH resolves hooks to, and whether it supports each installed hook's subcommand/flags -- checked against .claude/settings.json under the current directory, project- and user-level). No options. |
| mem init <tool> | Wires mem into a coding tool's config -- claude-code, codex, copilot-cli, copilot-vscode, copilot-visual-studio, or copilot-jetbrains -- automating what docs/integrations/*.md otherwise asks you to hand-copy. Idempotent: re-running upgrades mem's own entries in place, never duplicates them; an unstamped hand-written entry with the same identity aborts with a conflict error instead of being overwritten -- except a Claude Code hook whose command matches mem's own invocation shape for that event (an orphan left by an install that predates stamping), which is adopted in place, stamped, and reported in the install output, rather than treated as a conflict. --root <path> (project root, default current directory), --user (write the tool's user-level config instead of project-level, where it has both -- codex, copilot-cli, copilot-visual-studio, and copilot-jetbrains have no user-level target and reject --user; copilot-vscode --user writes only its user-level keybindings.json, skipping the project tasks.json and AGENTS.md block), --dry-run (print what would be written without touching disk), --force (claude-code only: skip the pre-flight check below). Installing both an AGENTS.md tool and a copilot-instructions.md tool writes mem's block into both files, which VS Code and Copilot CLI both read -- harmless, just redundant tokens. For claude-code, init first resolves the mem binary actually on PATH and checks it against the hooks it's about to write (by capability probe, not a version-number floor -- a stale binary rejecting a newer install's flags fails every hook silently otherwise); refuses and names the binary and what it can't run if the check fails, unless --force is given. |
| mem uninstall <tool|--all> | Removes exactly what mem init wrote for tool -- or every tool with --all -- leaving everything else untouched. A no-op (not an error) if there's nothing mem-authored to remove. --root <path>, --user, --dry-run. |
Every <id> argument (show, forget, pin, edit, used, review --promote/--reject) accepts a git-style short prefix — at least 4 characters — instead of the full id, as long as it uniquely identifies one fact. A prefix matching more than one fact errors and lists every match.
Every command supports --help for the authoritative flag list.
Retrieval is BM25 (lexical) by default and needs no configuration. Point these at any OpenAI-compatible embeddings endpoint -- OpenAI, Ollama, LM Studio, LiteLLM -- and recall additionally ranks by meaning, fusing the two lists with Reciprocal Rank Fusion:
| Variable | |
|---|---|
| TOKEN_GOAT_MEM_EMBED_URL | Full endpoint, e.g. http://localhost:11434/v1/embeddings. Setting it is what turns the feature on. |
| TOKEN_GOAT_MEM_EMBED_MODEL | Model name. Required whenever the URL is set. |
| TOKEN_GOAT_MEM_EMBED_API_KEY | Optional. Sent as Authorization: Bearer <key>; never logged, echoed, or printed by mem doctor. |
A localhost endpoint keeps mem's zero-network property; a hosted one does not, and sends your fact text to that provider. Nothing is sent until you set the URL.
Once configured, mem remember and mem suggest embed each new fact in the background of the same command -- after secret screening, never before -- and never fail, slow, or change their output if the endpoint is down. Run mem embed once to backfill facts captured earlier.
Vectors from two different models are not comparable, and cosine similarity cannot detect the mismatch: it happily compares them and returns a confident, meaningless number. Mem records which model produced the store's vectors and refuses to rank against them when the configured model differs, saying so on mem recall and mem doctor. mem embed --all is the migration.
mem consolidate can tell that two facts restate each other; it structurally cannot tell that a third thing follows from both, because Jaccard over topic terms has no notion of entailment. mem dream asks a model that question and prints the answer. Same shape as semantic recall above -- off until you set the URL, any OpenAI-compatible chat-completions endpoint:
| Variable | |
|---|---|
| TOKEN_GOAT_MEM_DREAM_URL | Full endpoint, e.g. http://localhost:11434/v1/chat/completions. Setting it is what turns the command on. |
| TOKEN_GOAT_MEM_DREAM_MODEL | Model name. Required whenever the URL is set. |
| TOKEN_GOAT_MEM_DREAM_API_KEY | Optional. Sent as Authorization: Bearer <key> only when set, so a local endpoint that wants no auth receives no header. Never logged or echoed; errors name the endpoint host, never its URL or key. |
It writes nothing, and no flag makes it. The output is a report; anything in it worth keeping is kept by typing mem remember, which runs the same secret screening, anchoring, and contradiction checks every other fact goes through. That is the whole design: a model is allowed to suggest what might follow from the store, and is never allowed to add to it.
It sends stored fact text to a chat model, when configured. Semantic recall/capture sends fact (and query) text to an embeddings endpoint too, when TOKEN_GOAT_MEM_EMBED_URL/_MODEL are set -- dreaming is not unique in this, it is just the other opt-in path that reaches off this machine. Point either at a local model if the store holds anything you would not paste into a hosted API.
Only active and pinned facts are sent, newest first, capped at 200 -- a superseded fact is one the store has already decided is wrong, and an inference resting on it would carry the store's authority behind a retracted premise. The reply is treated as untrusted input rather than as an answer: a candidate must cite at least two of the facts actually sent, by an index that resolves, and must not restate a fact already stored. One that fails any check is dropped rather than printed, so every from: id is one mem show opens.
$ mem dream
dream: qwen3:8b via localhost facts_sent=34
2 candidate inference(s) -- nothing was written; this is a report
[fact] deployment is entirely manual end to end
from: 3f2a... 9c14...
mem import --from-json and scope_root: a scope="project"/scope="path" fact's scopeRoot is an absolute filesystem path from the machine it was exported on. mem import --from-json imports it verbatim (full fidelity), so re-importing an export from a different machine — or a different path on the same machine — leaves scopeRoot pointing at a path that may not exist there. For a scope="project" fact captured inside a git checkout with a remote, the scope_repo identity below covers that case and the fact still surfaces; a scope="path" fact, or a project fact from a checkout with no remote, still binds to the path alone.
Project identity (scope_repo): a scope="project" fact captured inside a git checkout also records <normalized remote>#<root relative to the working tree>, so it surfaces from a second clone of the same repository, from a git worktree, and after an export/import onto another machine. The subpath half is what keeps a monorepo honest: one remote, many project roots, so packages/a and packages/b are different projects and neither sees the other's decisions. Identity only ever widens recall — the absolute-path binding is checked first and unchanged, and a fact with no identity (captured before this existed, or in a checkout with no unambiguous remote) is matched by path exactly as before. Reading is local and read-only: git is never invoked, only .git's own files are parsed. Set TOKEN_GOAT_MEM_PROJECT_IDENTITY=path to opt out and keep the path-only binding at both capture and recall — the case for it is two clones that are deliberately not the same project, such as a fork kept for experiments.
Contradiction resolution deliberately still keys on scope_root, not scope_repo: that key decides the persisted superseded/contested transitions, there is no honest backfill for facts captured before identities existed, and widening it would rewrite facts across checkouts on the first mem epoch --gc after upgrade. The cost is stated plainly — two facts on one subject captured in two clones are both in scope and are not detected as rivals.
Paste this into a terminal (uses a throwaway home so it never touches your real data):
export TOKEN_GOAT_MEM_HOME=$(mktemp -d)
mem remember "uses pnpm, not npm" --kind preference --subject package-manager --value pnpm
# remembered preference fact 79bce136-679f-471b-8ccb-fd18df7d2b36
mem remember "switched to bun" --kind preference --subject package-manager --value bun
# remembered preference fact 21a1330e-95d3-453c-81f0-49c792e1488f
mem recall
# stored pref (unverified, 2026-07): switched to bun
# mem show <id> for detailBoth facts share the subject package-manager with different values — a contradiction. Recall already prefers the newer fact and hides the loser; epoch --gc persists that resolution:
mem epoch --gc
# epoch=3 contradictions_resolved=1 preferences_decayed_below_floor=0 pruned_superseded_facts=0 pruned_sources=0 pruned_audit_log_rows=0 pruned_recall_log_rows=0
mem list
# 21a1330e-... [preference/active] package-manager=bun switched to bun
# 79bce136-... [preference/superseded] package-manager=pnpm uses pnpm, not npmAnchored facts are re-validated on every recall. An anchor that tests false excludes the fact from ground truth and routes it to review:
mem remember "repo has a yarn.lock" --kind fact --anchor "file-exists yarn.lock" --scope project --root .
mem recall --root . --scope project
# fact (contradicted, excluded): repo has a yarn.lock
# mem show <id> for detail; mem review to resolve contested/pending
mem review --root .
# -- anchor-contradicted (suppressed from ground truth) (1) --
# 888ba2c0-... [fact/active] repo has a yarn.lock(IDs are random UUIDs; yours will differ.)
Anchors are pure, read-only filesystem/git predicates — no shell-out, no network, bounded I/O, and paths are confined to the given --root:
| Predicate | Affirms when |
|---|---|
| file-exists <path> | the file exists under root |
| file-absent <path> | the file does not exist |
| file-newer-than <a> <b> | a is the currently-active file relative to b (e.g. the newest lockfile is pnpm's) |
| file-contains <path> <substring> | the file contains the substring (bounded read) |
| file-not-contains <path> <substring> | the file does not contain the substring |
| newest-of <expected> <candidate...> | among expected plus every listed candidate, expected is the sole existing file with the greatest mtime (e.g. "the newest lockfile is pnpm-lock.yaml", not just "pnpm-lock.yaml exists") |
| glob-exists <pattern> | some file matches the glob (*, ?, and a recursive ** segment) |
| git-branch-is <branch> | the repo's current branch matches |
| git-tracked <path> | the path is tracked in the git index |
| valid-until <ISO date> | the date has not passed. The one predicate that reads no filesystem or git state -- for a fact that is true until a date rather than until a file changes ("until the v2 migration lands, keep the shim"). A bare YYYY-MM-DD is read as the end of that day in the machine's own local time zone, so valid-until 2026-12-31 is still affirmed during the 31st wherever mem runs; an unparseable date is rejected at capture rather than silently reading as expired |
| package-version <path> <name>@<version> | path (a package.json) declares name at version in dependencies/devDependencies -- declared-manifest check only, not the installed/lockfile-resolved version; comparison is exact string match or major-version-prefix match only (no semver-range-satisfaction), so a genuinely ambiguous comparison is unverified, never a guessed affirmed |
Each evaluation yields affirmed, unverified (missing file, no repo, malformed predicate — cannot confirm or deny), or contradicted. Only affirmed is ground-truth eligible.
Mem works standalone. When token-goat is on PATH, token-goat reads mem epoch (a monotonic integer) to invalidate its compaction cache. The TGMEM/2 wire format is published as an optional seam for host tools to adopt; token-goat does not consume it today. It is not unused, though: mem init claude-code installs hooks that run mem recall --hint-format on SessionStart and UserPromptSubmit, so the format is what mem emits into Claude Code on every session and every prompt.
The seam is one-directional (Mem reads nothing from token-goat), stateless (live calls, no caching), and self-caveating (display strings include their own trust caveats). Contested or low-trust facts are excluded from --hint-format entirely — only ground-truth-eligible or explicitly-caveated hints are emitted. Mem does not cache results; forget/edit reflect instantly. If mem is not on PATH or the call times out, token-goat falls back to no hints (fail-open).
--hint-format emits TGMEM/2 by default: a header line, then one line per fact (pref fresh=affirmed|unverified|contradicted id=<uuid> display="<caveated text>"), then at most one shared footer line naming what is worth following up on: footer mem show <id> for detail, extended with N more in scope, not sent when the per-kind caps dropped results (2 of the block's 14 lines are reserved for pinned facts before those caps see the list, so a pin always arrives even against a full set of better-matching facts), N withheld; mem review to resolve contested/pending when facts were held back from ground truth, and no match for this query -- showing recent facts instead when a non-empty query matched nothing at all -- no lexical hit, no embedding or usefulness signal either -- so every fact-line the response does carry is filler the caps swept in by recency, not an answer to the query. Those counts exist because their absence is unfalsifiable from the wire — a capped payload is byte-identical to a complete one, and a response with no fact lines is byte-identical to a project with no memory, even while a review queue fills behind it. The footer is prose for a reader, not a parseable field; a response with nothing to follow up on carries none. When the store itself could not be opened -- a permissions failure, a WAL lock, a schema mismatch -- the response is still TGMEM/2 with zero fact lines, but the footer says footer store could not be read; mem doctor shows the underlying error instead of carrying nothing at all: mem doctor runs the same call that just failed, but answers to a human on stderr instead of failing open onto the wire. The same reasoning covers the other two ways this call can go silent without there being nothing to say: a soft retrieval time budget cutoff returns zero fact lines (never a smaller slice -- TGMEM/2 has no way to mark a response partial, so a reduced set would be byte-identical to a complete one) with a footer saying footer hint set empty; retrieval ran out of its time budget, not out of facts -- mem recall shows them, and an internal error downstream of a successful store open carries footer hints unavailable; an internal error stopped retrieval -- mem doctor shows the store state.
TGMEM/2 moved the per-fact follow-up hint (mem show <id>, resolve via mem review, ...) out of every display string and into that one footer line, since repeating the same CTA on every line was pure overhead once a consumer already knows the pattern. display itself is unchanged otherwise — still self-caveating, still meant to be surfaced verbatim.
TGMEM/1 (the original format: per-line CTA baked into display, no footer line) remains fully supported for callers that still parse it — pass protocolVersion: 1 to buildHintFormat() when calling the programmatic seam directly. The CLI itself always emits the current default version.
Re-running mem recall --hint-format on every host-tool turn works, but it re-opens the DB and re-runs retrieval every time even when nothing changed. mem epoch is the cheap alternative: it prints a single monotonic integer that is bumped by every write that can change what a recall returns (remember, edit, forget, pin, review --promote/--reject, used, facets, the epoch --gc retention pass) and left untouched otherwise.
A host tool can use mem epoch to detect store changes and only re-run mem recall when the store actually changed. However, the epoch covers store writes only — it does not cover anchor verdicts (filesystem and git state, re-evaluated live on every recall) or preference decay (a function of time). A polling consumer should therefore either call mem recall on a time interval to refresh anchors and decay, or monitor for working-tree events (branch switches, dependency installs) that may invalidate anchors.
Store-only polling pattern (refreshes store state only):
last_epoch=$(mem epoch)
# ... later, on each turn ...
current_epoch=$(mem epoch)
if [ "$current_epoch" != "$last_epoch" ]; then
mem recall --hint-format --root "$project_root"
last_epoch="$current_epoch"
fiTime-interval pattern (refreshes all state including anchors and decay):
last_recall=$(date +%s)
# ... on each turn ...
now=$(date +%s)
if [ $((now - last_recall)) -gt 300 ]; then # 5 minutes
mem recall --hint-format --root "$project_root"
last_recall=$now
fimem epoch with no flags never mutates facts (no GC pass, no writes) — it is safe to call as often as you like as a cheap store-state check.
Integration guides in docs/integrations/:
Any other agent that can run a shell command (Cursor, Windsurf, Cline, Aider, ...) can use the same patterns: mem recall --hint-format at session start, mem remember as decisions land.
Token-Goat Mem runs on your machine and writes to your local SQLite database. The software is provided as-is, without warranty of any kind. DFK Helper LLC is not liable for any damages arising from use. Full terms, including the No Liability clause, are in the LICENSE file.
Mem stores facts you tell it to remember and suggests candidate facts for confirmation. Never persisted by default: secrets, credentials, PII, high-entropy tokens, full file contents. Enforcement layers: (a) only short extracted facts are stored; (b) secret-pattern and entropy screening; (c) suggested facts shown in mem review before surfacing. Local-only by default: with neither TOKEN_GOAT_MEM_EMBED_URL/_MODEL nor TOKEN_GOAT_MEM_DREAM_URL/_MODEL set, nothing leaves this machine. Set the former and mem remember/mem suggest/mem embed send fact text and mem recall sends the query text (including every prompt, if wired via a coding tool's UserPromptSubmit hook) to that endpoint; set the latter and mem dream sends stored fact text to it. Run mem review to audit all stored facts.
Token-Goat Mem is source-available under the PolyForm Noncommercial License 1.0.0.
Personal use: free. Includes hobby projects, individual productivity, personal study, and private coding. Individual developers may install and use the software on their own machines for individual productivity purposes without a commercial license.
Commercial use or shared infrastructure: requires a license. Contact token-goat@dfkhelper.com for details.
Patent Pending.
Full terms in LICENSE.
Built and maintained by DFK Helper. If it saves your tokens or your sanity, a star means the world.
| Back | FazBrowse Home | New Git URL |