HNHacker News
TopNewBestAskShowJobs

vshulcz

63 karma · joined June 9, 2026

Python/backend developer interested in distributed systems, Kubernetes, service architecture, testing, and small developer tools.

GitHub: https://github.com/vshulcz

submissionscomments
vshulcz··on Saving up to 70% of an agent's tokens on work it already did
The corpus is LongMemEval chat logs rather than real coding sessions, because I needed a set where exactly one session answers each question, and no real laptop gives you that. It's the weakest part of the setup. Drivers and the corpus builder are in the repo if anyone wants to rerun a row.
vshulcz··on OKF Agent Memory – Git-native persistent memory for AI coding agents
https://github.com/vshulcz/deja-vu
vshulcz··on OKF Agent Memory – Git-native persistent memory for AI coding agents
Benched cold start recently: 19k LongMemEval sessions laid down in the real ~/.claude and ~/.codex layouts, 100 questions whose answer sits in exactly one session, scored by whether that session comes back (bias: I built deja, one of the six)

deja: 29s to index, 24ms query, 18/100 hit@1, 67 found@50. Plain BM25, no vectors

agentmemory: 95s import, 14 hit@1, 65 found@50, plus a worker and engine on four ports

MemPalace: ~3h mining, 2.6s query, 14 hit@1

CASS: 56m index, every NL query fails with "query fuel exhausted" on the release build (fixed on their main)

claude-mem: no-op out of the box, only records forward from install

funes: the documented 1 min first pass indexed 189 of 19k sessions (0/100); full index still embedding, ~3 sessions/s

Numbers look low because 19k sessions is brutal; on the standard 500-session LongMemEval-S the same BM25 gets ~85% hit@1

The funny thing is BM25 basically ties embeddings here at 1/100th the cost. The real cliff is reranking (found@50 67 vs hit@5 35) and staleness. Vector search has zero concept of "superseded info" only fix I found was letting explicit user corrections outrank the transcript.

Repro scripts and corpus: https://vshulcz.github.io/deja-vu/guide/day-zero.html

@skeledrew: cross-agent across 23 harnesses, but yeah, it's an index over logs, not a source of truth :)

vshulcz··on Open-source memory for coding agents, synced over SSH
That's why I ended up creating two forced paths for this.

First, the SessionStart hook aggressively introduces context immediately after loading, completely bypassing the agent's requirements.

Secondly, I'm forcibly changing the behavior by using a fragment of the system query in CLAUDE.md or AGENTS.md. I'm literally just copypaste this block from the README:

    Before debugging or re-implementing anything, run deja "<query>" (or the MCP revocation tool) — the agent's past sessions in Claude Code, Codex, and opencode will be indexed locally. Specify what you are reusing.
vshulcz··on Open-source memory for coding agents, synced over SSH
I intentionally used a dictionary for this. When I dig into old sessions, I'm usually looking for a very specific token (the exact error string, the unique function name, a specific cli flag) rather than a generic value.

Adding semantic search usually means sending hundreds of megabytes of embedded models and working with non-deterministic results which completely violates the promise of zero dependency within 7-9 ms. In fact, I encounter some uncertainty when expanding substrings (e.g. when requesting the code, the open source code will be displayed).

I won't completely abandon semantic search if there is a really easy and fast way to do it locally, but at the moment deterministic search is the priority. And I completely agree with your first comment, the verbatim approach provides just such a verification opportunity.

vshulcz··on Open-source memory for coding agents, synced over SSH
This is a fair point of view and ofc it should be taken into account. When called automatically, a small overview (~2 KB) of the imported sessions is included in the context.

However, when syncing, only data records are moved not instructions. Nothing is done and known secrets are deleted at the indexing stage even before they leave the source computer. In addition, the context here is your own computers and your terminal's output data, if an attacker can control your agent's transcripts, the source computer has already been completely compromised.

Nevertheless, you make a perfectly reasonable case for strict isolation. I just opened the issue to add an opt-out checkbox where imported sessions are only displayed when manually searched using the CLI/MCP, not when automatically implemented: https://github.com/vshulcz/deja-vu/issues/39

vshulcz··on Open-source memory for coding agents, synced over SSH
My particular interest in deja was that I found a combination that I couldn't find on this list: zero dependencies, zero LLM calls, verbatim text search, proactive secret cleanup and P2P synchronization over SSH.
vshulcz··on Open-source memory for coding agents, synced over SSH
Yes, ctx is the closest cousin here. I actually linked it in my top comment.

This causes a very similar itch, but we went a slightly different route regarding some implementation details

Secrets: The ctx README mentions saving the text as is and warns about the need to review the output data before publishing. deja explicitly removes known secret templates at the indexing stage (replacing them with tags like [redacted:aws-key]).

Syncing: It looks like ctx is getting ready for a cloud beta to be shared by the team. Actually, I just wanted something local and free that I had full control over, so deja does P2P syncing through your existing SSH settings.

Recall that deja has a session startup feature that automatically introduces a bit of context when a new agent is loaded, instead of relying solely on manual search.

Both applications solve the same annoying problem by simply optimizing for different workflows :)

vshulcz··on Open-source memory for coding agents, synced over SSH
Hi HN. I built deja after watching Claude Code and Codex debug the same problems more than once.

The annoying thing was that the answer usually already existed somewhere in my old sessions. My records were stored on the disk for months (~3.3 GB). It wasn't easy to find them manually and the new agent session had no idea what the other agent had already found out.

deja indexes the transcripts that Claude Code, Codex, and opencode already write. On my corpus, the initial index takes about 10 seconds and warm searches are 7-9 ms.

There are 3 ways to get the memory back: a normal CLI search, an MCP tool (agent can query it directly) and a SessionStart hook that automatically injects a bit of relevant project context.

The feature I built this for:

deja sync ssh <host>

It moves new memory between machines using the existing SSH setup. Secret data is deleted during indexing and checked again before exporting.

My setup is a laptop and a mac mini without an interface. The agent can work on the mini all night, and in the morning I extract its memory. Then the agent on my laptop will know what the mini tried, what broke, and what eventually worked.

There are other projects (cass, ctx, claude-mem):

https://github.com/Dicklesworthstone/coding_agent_session_se...

https://github.com/ctxrs/ctx

https://github.com/thedotmack/claude-mem

I wanted deja to intentionally use a small approach: one binary code with zero dependency, no LLM calls, searchable verbatim text instead of generated summaries, and synchronization via your own SSH. It does not implement its own network transport; it just connects to the system ssh/scp.

You can install it via curl, brew, npx or go install, then run:

deja install --all

vshulcz··on Show HN: Nimic – Pure Python as a systems language with AOT compilation
the variant-types-for-dispatch thing is a nice tradeoff, makes sense. honestly the strongest pitch in there is the "write the hot module in nimic instead of dropping to c/rust/cython" angle, which is basically the cython niche. so the question is what nimic adds over cython for that. my guess is dual-mode: a .pyx isn't valid python and won't run under plain cpython, but nimic stays importable and debuggable in cpython and you compile only when you want speed. if that's the edge i'd put it front and center, it's a way sharper sell than "python but fast".

one thing i'd worry about with "runs unmodified in cpython" though: python ints are arbitrary precision and nim's aren't. so the same code can give you a bignum under cpython and a wraparound under the compiled path. how do you handle that, or is matching cpython semantics explicitly a non-goal for the typed subset?

vshulcz··on Show HN: Nimic – Pure Python as a systems language with AOT compilation
The line I'd push on is "valid Python that runs unmodified in CPython." True at the syntax level, but the speedup isn't really coming from Nim as a backend, it comes from how much of Python's dynamism you can pin down at compile time. Refcounting semantics, __getattr__, values that change type at runtime, isinstance-based dispatch, monkeypatching in tests: the moment you statically commit to any of those, you've defined a subset, and the subset is the actual product, not the transpiler.

I've spent a fair bit of time generating specialized straight-line code for hot Python paths, killing the per-call attribute and dict lookups the interpreter does. The lesson was that dispatch-bound code claws back most of its overhead without ever leaving CPython. Where AOT-to-native actually pulls ahead is numeric and loop-bound work, where the interpreter loop and boxing dominate. Your 512x288 render is exactly that case, which is why it looks so strong.

So the benchmark I'd want isn't render time, it's what fraction of a real module transpiles with no rewrites. That number tells me whether this is a systems language or a fast path I have to hand-shape around. Codon and Shedskin both hit that wall. Curious where Nimic draws the line.

vshulcz··on The new HTTP QUERY method explained
Even past the TLS point (CDNs terminate TLS, so they can read the body) there's a harder problem nobody's solved: to cache a QUERY the cache has to fold the body into the cache key, and there's no standard way to canonicalize a request body. {"a":1,"b":2} and {"b":2,"a":1} are the same query and two different cache entries; whitespace, float formatting, unordered keys all fork the key. GET gets this for free because the URL is already a normalized string. So "cacheable in principle" is real, but "actually cached" needs every layer to agree on a canonical form first - the same coordination problem that killed GET-with-body. I want QUERY for the honest semantics; I just wouldn't budget for cache hits yet