This is a submission for the Hacktoberfest Open-Source AI Challenge Week 3: Slow and Steady
I have written the same byte-size formatter four times. Same project, four files: two functions called formatBytes, a formatByteSize that rounds megabytes to one decimal, and a formatPreviewBytes that is character-for-character one of the formatBytes, renamed. I found this out this week. It is not the worst thing I found.
So for Sloth Week I built the laziest tool I could think of: one that makes me write less code by remembering what I already wrote.
What I Built
Code Déjà Vu is a git pre-commit hook. When you commit a function that already exists somewhere in your repos (any repo, any name, copied or rewritten from scratch), it tells you before the duplicate lands:
$ git commit -m "add helpers"
déjà vu: humanFileSize (src/files.ts:1)
already in arivu-agent/src/tools/registry.ts:1567 as formatByteSize (similarity 0.96)
It also lets you find your own code by describing it, because I never remember what I named things:
$ dejavu search "sort states by an indicator, lower is better"
0.88 ranked Bharath Choropleth/examples/observatory/react/src/App.tsx:109
0.85 ranked Bharath Choropleth/examples/observatory/js/app.js:75
0.79 trend Bharath Choropleth/examples/soc-dashboard/src/feeds.ts:260
(Yes, that's the same function in two example apps. It finds my duplicates even when I'm only searching.)
Everything runs on my laptop with EmbeddingGemma 2, an open-weight embedding model. No code leaves the machine, it works offline, and it costs nothing per commit.
The hook never blocks a commit. It warns and gets out of the way. A hook that blocks you once is a hook you uninstall.
Demo
A fresh scratch repo with the hook installed. I commit three small helpers: a file-size formatter, an abort-signal forwarder, and a leap-year check.
$ dejavu install-hook
installed …/dejavu-demo/.git/hooks/pre-commit
$ git add src
$ DEJAVU_SECOND_OPINION=1 DEJAVU_DECIDER_URL=http://127.0.0.1:11435 git commit -m "add helpers"
déjà vu: forwardCancellation (src/abort.ts:1)
already in arivu-agent/src/agent/OpenAICompatibleChatClient.ts:979 as linkAbortSignal (similarity 0.93, second opinion 97% same)
déjà vu: humanFileSize (src/files.ts:1)
already in arivu-agent/src/tools/registry.ts:1567 as formatByteSize (similarity 0.96)
[main 7db292a] add helpers
3 files changed, 24 insertions(+)
-
humanFileSizewould have been my fifth byte formatter. Caught with similarity alone. -
forwardCancellationis a from-scratch rewrite of a helper in my coding agent, Arivu. Similarity alone only rated it a "similar" hint (0.93). I'd switched on the optional second opinion (the env vars point it at a local Ollama running Perplexity's decision model), and that upgraded it. This commit took about 5 s because Ollama had to load the model first; warm, a check is well under a second. -
isLeapYearreally was new, so the hook stayed quiet. - The commit still went through. Warnings never block.
Then the boring commits, which matter just as much for a hook you leave on forever:
$ git commit -m "docs" # docs only: 0.20 s, silent
$ git commit -m "tweak isLeapYear" # edited an existing function: 0.38 s, silent (no self-match)
Code
shashankbudem
/
code-dejavu
A git pre-commit hook that warns when the function you're committing already exists in your repos. Runs locally on EmbeddingGemma 2.
Code Déjà Vu
The laziest code is code you've already written.
Code Déjà Vu is a git pre-commit hook that notices when the function you're about to commit already exists somewhere in your repos, under any name, in any of them. It also lets you find your own code by describing it. Everything runs on your laptop with EmbeddingGemma 2, an open-weight model. Your code never leaves the machine.
With the optional second opinion switched on (see below):
$ DEJAVU_SECOND_OPINION=1 DEJAVU_DECIDER_URL=http://127.0.0.1:11435 git commit -m "add helpers"
déjà vu: forwardCancellation (src/abort.ts:1)
already in arivu-agent/src/agent/OpenAICompatibleChatClient.ts:979 as linkAbortSignal (similarity 0.93, second opinion 97% same)
déjà vu: humanFileSize (src/files.ts:1)
already in arivu-agent/src/tools/registry.ts:1567 as formatByteSize (similarity 0.96)
[main 7db292a] add helpers
3 files changed, 24 insertions(+)
Without the second opinion, forwardCancellation (a from-scratch rewrite) shows as a gentler similar: hint at 0.93
instead. The third new function in that commit, isLeapYear, really was…
About 400 lines of Python for the tool itself, plus the evaluation scripts I used to tune it (eval/).
How I Built It
I built this with Claude Code as my pair programmer: I made the calls (what to build, which models to try, what to keep and what to cut), and the agent did most of the typing, the labelling and the benchmark runs.
1. Find every function. tree-sitter parses TypeScript, JavaScript, Python and Rust and pulls out every function of five or more lines. Across 10 of my repos that was 5,094 functions.
2. Catch exact copies without any AI. Each function body gets fingerprinted with comments and whitespace stripped. 2,451 of my 5,094 functions were verbatim copies of another one, mostly from forking my coding-agent project into experiments. A hash catches all of them instantly, so the model never has to.
3. Catch near-copies and rewrites with EmbeddingGemma 2. New or changed functions are embedded and compared with the index of everything else. Two details mattered:
- Matryoshka dimensions. EmbeddingGemma 2 is trained so the first 256 of its 768 numbers still work on their own. The index is a third of the size, and it did slightly better on my duplicate test than the full vector.
-
Task prompts. The same model embeds code for duplicate checks (
task: sentence similarity) and for search (task: code retrieval). For duplicates, the similarity prompt clearly beat a document-style prompt in my tests.
Similarity of 0.946 or above is a déjà vu warning. Between 0.91 and 0.946 is a gentler similar hint.
4. Check only what changed. Functions whose fingerprint is unchanged since the last commit, including moved ones, are skipped. The files being committed are excluded from the comparison, so editing a function never matches its own old version. A docs-only commit never even loads the model.
5. Stay out of the way. Importing PyTorch and loading the model takes about 13 seconds, which is fine once but terrible on every commit. So dejavu serve keeps the model warm in the background (local only). With it running, checking a commit takes about 0.3 s and a search about 0.2 s. Without it, everything still works, just slower.
Why EmbeddingGemma 2
I didn't pick it on vibes. I pulled the official MTEB results for six open embedding models on the same 12 code tasks. On the task that matters here, finding code that does the same thing as other code, EmbeddingGemma 2 scored 96.6, the best of all six, ahead of models 13× its size (Qwen3-Embedding 4B: 95.6, Perplexity's pplx-embed 4B: 92.3). It's also small enough to index thousands of functions on a fanless MacBook Air.
How I tuned it: on my own mess
Pairs from my own repos were labelled by hand (well, by my agent's hand: it read each pair in full before labelling it):
| What | Result |
|---|---|
| 19 copies that drifted apart | 18 warnings, 1 hint |
| 9 from-scratch rewrites | 5 warnings, 4 hints; a function doing the same job ranked first 9/9 times out of 2,643 |
| 25 look-alikes that do different things | 2 false warnings, 11 hints |
| Simulated hook over 381 functions from my standalone repos |
1 warning, and it was real: escapeHtml, written separately in two different projects |
| 22 "describe it" searches | right function first 18–19 times; keyword matching (grep-style): 2 |
Honest caveats: the cutoffs were picked on the same small set they're scored on, the rewrites were written while looking at the originals, and every repo is mine. It's a careful first measurement, not a benchmark.
The judge bake-off (and why the embedding won)
Similarity scores get fuzzy in the 0.91–0.95 range, where a real rewrite and a look-alike can score the same. So I asked: would a second model, a "judge" that answers "are these the same function?", do better?
I tested four open models as judges on the same 113 pairs. The keep rule was written down before the new ones ran, so nobody could move the goalposts afterwards.
| Judge | Borderline cases (0.5 = coin flip) | Calls random pairs "the same" |
|---|---|---|
| EmbeddingGemma 2 alone | 0.72 | — |
| Laya (decision model) | 0.40 | 98% |
| CoREB (code-search reranker) | 0.41 | 0% |
| Clef Flash (Cloudflare decision model) | 0.85 | 0% |
| pplx-decider-flash (Perplexity decision model) | 0.98 | 0% |
The borderline column rests on just 25 pairs, so its error bars are wide (the embedding's 95% range is 0.49–0.92). Read it as a direction, not a leaderboard.
None passed the keep rule. Two things surprised me:
- Decision models answer exactly the question you ask. pplx-decider-flash was brilliant at the borderline cases, but asked "do these implement the same behaviour?", it said no to a copy that had gained two extra options. Used as a veto, it threw away 13 of 19 real copies. Fair: they don't do exactly the same thing.
-
Always sanity-check a judge first. Before each full run, I fed every judge an identical pair and an unrelated pair. CoREB said
add(a, b)and an unrelated config reader were 99.8% the same. The cause was its model card: the example prompt leaves out an empty<think></think>block its base model expects. One missing line, and every answer was "yes".
pplx-decider-flash still earned a job: an optional second opinion that can only upgrade a borderline hint to a warning, never cancel one. In that role it upgraded all 4 hinted rewrites. Its one false upgrade was a test mock, and the tool doesn't index test files. (I designed that rule after seeing the bake-off, so treat it as promising, not proven.) Running it through Ollama's new MLX runner at 8-bit gave the same answers as the original on 113/113 pairs, about 3× faster.
Why Does Open Innovation Matter?
For this tool it isn't a nice-to-have. It's the whole thing.
- My code can't leave my laptop. Lots of developers can't send their employer's code, or their private repos, to a cloud API just to ask "have I written this before?". A local model can read all of it, offline, on every commit.
- It's free to run constantly. A hook runs on every commit, forever. Per-call pricing would turn "check every function" into "check sometimes".
- I could test instead of trust. Open weights let me run four judges on my own labelled code, catch a model-card bug, and verify that an 8-bit MLX build answers like the original. With a closed API, I'd have had to take the vendor's word for all of it.
- I could swap pieces freely. Embedder, judge and runtime (PyTorch, then Ollama MLX) all changed during the week, and nothing else had to.
The Lazy Payoff
The first run over my own 10 repos turned up:
- Four byte-size formatters inside Arivu alone, one of them an identical copy under a new name.
-
escapeHtmlwritten twice, separately, in two unrelated projects. -
The same
ranked()helper in both the React and plain-JS versions of my Bharat Choropleth examples. - 2,451 verbatim copies in total, mostly between my coding agent and its experimental forks.
Not all of them are mistakes: the forks and the standalone example apps are copies on purpose. But four byte formatters in one project definitely were, and each of those is a function I wrote, tested and now maintain more than once.
And the cost of being lazy this way:
| What | Time |
|---|---|
| Indexing 4,782 functions, once | ~11 min on a fanless MacBook Air (later runs only embed new code) |
| Commit that adds functions, model warm | ~0.3 s |
| Docs-only commit | ~0.2 s |
| Search | ~0.2 s |
Prize Categories
- Best Use of Gemma: EmbeddingGemma 2 runs locally and does all the work: duplicate detection, search, and the Matryoshka-shrunk index.
Top comments (0)