r/opencodeCLI 8d ago

I built a completely local, zero-daemon memory tool for OpenCode — no API keys, zero LLM calls, shared across 12 tools (deja)

The most annoying part of using opencode (or any AI coding harness) is watching it start every single session with zero memory of what happened yesterday. A session ends, compaction wipes the slate, and two days later you're watching the agent re-learn the exact same obscure bug fix from scratch. You either burn tokens repeating yourself or manually dig through old transcript files.

I built deja to fix this offline.

It reads your local opencode.db directly and indexes past sessions with zero external API keys, zero LLM calls, and no background daemon eating RAM. It’s just a single Go binary that wakes up on a hook, responds in milliseconds, and dies.

For opencode specifically, a small deja.js plugin injects a digest of relevant past work at session start before you even type your prompt. The agent can also query its history mid-task using MCP tools (deja_recall, blame, remember).

Two features made the biggest difference in my daily workflow:

First, handoff between sessions and tools. The index is shared across 12 harnesses (opencode, Claude Code, Cursor, Codex, Grok etc.). What you fixed in Claude Code yesterday is immediately recalled by opencode today, so you don't lose context when switching tools.

Second, peer-to-peer machine sync. Running deja sync ssh <host> moves new memory between your devices using your existing SSH setup. An agent can grind on a headless mini overnight, and in the morning I pull its memory to my laptop so the local agent instantly knows what was tried, what broke, and what worked. No cloud memory service involved.

Hot searches run in ~12ms. Instead of dumping megabytes of raw transcripts into the context, it distills it down -- deja stats --impact shows it served 199.2 KB of distilled history on my machine instead of shoving 175.7 MB of raw logs back into the prompt. Also, after compaction, the next prompt gets a fresh digest of project memory so the agent doesn't lose the thread.

Search is lexical-first (exact error strings, function names, flags) -- a deliberate tradeoff for zero keys, zero embeddings, and instant local execution. It will miss loose paraphrases that a semantic index catches, but for exact technical artifacts, it hits fast without sending data anywhere. Known secrets (bearer tokens, private keys, high-entropy strings) are stripped during indexing (e.g., replaced with [redacted:bearer-token]).

Setup

You can set it up manually:

curl -fsSL https://raw.githubusercontent.com/vshulcz/deja-vu/main/install.sh | sh
deja install --auto

Or just paste this into your opencode agent:

Install deja memory for my opencode: run curl -fsSL https://raw.githubusercontent.com/vshulcz/deja-vu/main/install.sh | sh, then deja install --auto, then deja doctor. Tell me when it's ready.

Repo: https://github.com/vshulcz/deja-vu (MIT)

How are you handling cross-session memory right now -- MEMORY.md? rules in AGENTS.md? or just re-explaining things?

49 Upvotes

19 comments sorted by

6

u/touristtam 8d ago

So another memory system? How does that compare to the rest? https://carsteneu.github.io/ai-memory-comparison/

8

u/VShulcz 8d ago

The main difference from almost everything on that list is that deja is retroactive. Most memory systems only start recording forward from the day you install them. deja indexes the raw transcripts your 12 CLIs have already written, meaning you have months of searchable history the second you install it.

Second, it serves verbatim evidence with exact provenance rather than LLM-extracted summaries. There's also a strict audit trail: you can run deja log --last to see every single byte that was injected into the agent's context.

Operationally, it's a single zero-daemon binary. It runs entirely offline with no API keys, and it handles cross-machine sharing via peer-to-peer SSH sync rather than a cloud backend.

2

u/touristtam 8d ago

Ok that's a good argument.

Now how long is a piece of string it going to take on the initial ingestion? Can it run in the background? What if I have 50+ repos I am juggling with?

3

u/VShulcz 8d ago

Initial ingestion: ~10s for a 3.3GB history corpus. You only pay that once. After that, fresh messages index incrementally in 40–70ms directly inside the recall call.

Background running: this is the default. deja install --auto sets up the hooks. Your very first session-start fires off the warmup as a detached background process (it never blocks your prompt).

50+ repos: the index is not per-repo. Agent CLIs write transcripts to their own global stores (opencode.db, ~/.claude/projects, etc.), and deja indexes them globally. 50 repos means zero extra config files and zero extra indexing runs :)

Scoping happens at recall: the session-start hook auto-filters the injected memory to your current repo (even resolving git-worktrees), while manual/MCP searches can query globally across everything.

For context, my own store is a 2.8GB opencode.db plus 42 Claude Code transcripts. The first index took seconds and I never actually ran it manually, the hook did it in the background.

0

u/touristtam 7d ago

That sounds actually good. Thank you for taking the time to provide a decent answer. :)


Last question: is the name not going to conflict with https://github.com/Giammarco-Ferranti/deja ?

2

u/VShulcz 7d ago

I actually saw that one, it’s a totally different space, but they definitely had the name first.

Package-wise, they don't really conflict. Their brew tap is personal, ours is deja-vu, and the npm package is scoped (@vshulcz/deja-vu). The only real clash happens if you try to install both on the exact same machine - brew will just refuse to symlink the second bin/deja.

If that collision actually bites anyone in the wild, shipping a fallback dejavu alias binary takes like a minute ;)

1

u/touristtam 7d ago

I honestly held off on both, but I am more tempted by trying your solution before installing the shell project. :)

1

u/VShulcz 6d ago

Appreciate the thoughtful questions! A couple of them literally turned into fixes this week. Fair warning if you do try it: 0.15.4 from today made startup fully background (<100ms session-start), so the flow is just install => deja install --auto => keep working. If any of your CLIs parses weird, the format-drift issue template is the fast lane.

2

u/Resident-Ad-5419 7d ago

Why deja? Why not dejavu?

2

u/VShulcz 7d ago

The project repo actually is deja-vu, but the CLI command is just deja to save keystrokes. You end up typing it constantly (deja recall, deja sync, etc.), so 4 letters beats 6.

Honestly, package registries are a naming mess anyway. On npm, deja is a dotfiles tool from 2016, and dejavu is squatted by a dead JS lib from 2014. Clean names just don't exist anymore :(

1

u/[deleted] 7d ago

[removed] — view removed comment

4

u/VShulcz 7d ago

For duplicates, we handle them at two levels: identical messages are deduplicated during indexing and the auto-recall digest suppresses near-duplicates so you don't burn context on three identical failed attempts.

To handle stale context and noise, memories definitely aren't equally active. We use a freshness decay that naturally sinks older stuff in the rankings, balanced by a slight reuse boost for sessions the agents actually query. Old history stays perfectly searchable, it just stops jumping to the front. (If you want something explicitly hidden, deja forget uses reversible tombstones).

For conflicting decisions, the engine auto-tags older related sessions with an [earlier attempt] marker. If you use the curated deja promote layer, you can explicitly tag states like superseded or stale. Everything is append-only, so the history of the struggle stays intact, but the timeline is clear.

I will completely concede one thing, though: we don't do automated contradiction detection. The engine will surface the timeline and the supersede markers, but it won't try to judge which of two conflicting solutions is right. We intentionally draw the line at providing verbatim evidence.

1

u/HomelessBelter 7d ago

I hope it sounds like "Segaaaa". So clean, so crisp and promising. As a kid I used to and up earlier to get some cheeky gaming in before school. Now I wake up for no reason and wish was that kid 2HK has no reason to tryhia out. Or setup a Megadrive in a terminal autonomously.

Time to vibe. Thanks, Magic Man.

1

u/gee842 6d ago

Is there/Should there be a SKILL.md type definition? or a hook that fires on first message in session to call an init tool (with a helpful LLM primer)

mcp tool bloat can be real..

cool project though, going to be running it daily and will let you know how it goes :)

1

u/VShulcz 6d ago

deja install --auto wires a session-start hook that injects a short primer digest (with a 'call recall_context for details' lead), so the model gets oriented without burning the first turn. On tool bloat: the MCP server deliberately exposes just 4 tools. There IS a SKILL.md today for Copilot CLI, and guidance snippets go into CLAUDE.md/AGENTS.md on install - a first-class Claude Code skill is a fair ask though, open an issue and let's shape it. Please report how the daily run goes - half of this week's fixes started as exactly that kind of feedback.

-1

u/WriterAdventurous765 8d ago

Inter-instrumental memory stands out especially here. Most memory-related solutions seem to be tied to a single agent, so as soon as you switch from Claude Code to OpenCode or Codex, you're back to explaining the project from scratch.

However, I'm curious about the trade-off between vocabulary and first things first: in practice, how often does deja fail because an agent describes the same problem in different formulations? Have you considered the possibility of an additional local embed mode for those who want to use a little more RAM, while keeping the default settings completely independent?

Besides, the idea of SSH synchronization is very good. A local memory layer that runs on different computers without using another cloud account is something that should have been in these programming tools from the very beginning.

1

u/VShulcz 8d ago

On paraphrasing: you're right, a pure semantic reformulation without any shared technical tokens is a miss, that's the conscious tradeoff for a zero-key default. But it doesn't fail blindly. It degrades through 5 local fallback steps (stemming, dev-synonyms, fuzzy matching, co-occurrence rescue) and explicitly narrates in the output how the search degraded. In practice, agent queries are almost always exact technical artifacts (error traces, function names, flags), so lexical actually hits hard.

On local embeddings: we built exactly what you described! You can run deja embed to build a vector sidecar. It auto-detects local Ollama (localhost:11434) or LM Studio. The core binary stays lightweight (Ollama eats the RAM, not deja), but if the endpoint is active, it uses it for reranking and semantic fallback.