Your personal prompt library — organized automatically, always within reach.
Deja is a Manifest V3 Chrome extension that passively captures every prompt you send to ChatGPT, Claude, Gemini, DeepSeek, and Grok and stores it in a local, searchable library. No copy‑paste, no accounts, no cloud — everything lives in IndexedDB on your machine.
The prompts you write are work. Most of them vanish into a scrolled‑away chat the moment you hit Enter. Deja keeps them, makes them findable, and quietly resurfaces the right one while you're typing the next.
Status: v0.4.1 — trustworthy capture plus the resurface moment, now with selective capture (short throwaways are filtered but recoverable), capture controls (one‑click pause, per‑site switches, filter strength, blocklist test + dry‑run), on‑device PII redaction, capture deduplication (exact + near‑duplicate), and a built‑in feedback path. Capture is credential‑safe and self‑monitoring, the library is fully featured, and the "you've been here before" in‑context tooltip is live. See ROADMAP.md for the phased plan.
- Why Deja
- Supported sites
- Features
- Install (load unpacked)
- Using Deja
- Keyboard shortcuts
- Architecture & how it works
- Privacy & security
- Development
- Extending Deja
- Roadmap & future landscape
- Deploying & launching
- Tech stack
- Project documents
- License
Every frequent AI user hits the prompt graveyard: you craft a perfect prompt, get a great result, and then it's gone — buried in a history you'll never scroll back through. Deja fixes that with two ideas:
- The library is plumbing. Capture happens automatically, on every supported site, with zero effort. You never click "save."
- The resurface moment is the product. The instant you start re‑asking something, your older, better version floats up in‑context — like a password manager appearing at the login box, not a notebook you have to remember to open.
Why it's defensible: any single lab could ship a "save prompt" button tomorrow — but none will let your prompt history follow you out of its walled garden into a competitor's. Deja is the prompt layer that rides above whichever model wins: your accumulated craft, portable, private, and yours. The one‑liner: your prompts, every AI, one library.
| Platform | Host(s) |
|---|---|
| ChatGPT | chatgpt.com, chat.openai.com |
| Claude | claude.ai |
| Gemini | gemini.google.com |
| DeepSeek | chat.deepseek.com |
| Grok | grok.com |
Site DOMs change often, so each platform uses an ordered list of selector fallbacks, and a per‑site capture‑health signal flags silent breakage in the library before you'd ever notice missing prompts.
- Passive capture on Enter and on send‑button clicks; duplicate submits within ~2 s are de‑duplicated.
- Never captures credentials — password / OTP / payment / non‑composer fields are refused, and stored URLs are minimized to origin + path.
- Capture‑health per platform, surfaced in the library and settings, so a broken selector is visible to you and never leaks to the host page.
- PII redaction (on by default) — detected personal info (emails, phone numbers, credit cards, SSNs, IPs, API keys/secrets) is replaced with labels like
[email]before the prompt is stored, so raw values never touch your library or exports. Per‑category toggles, a live test box, and a scan‑and‑redact for already‑captured prompts live in settings. Redaction is local, deterministic, and turns prompts into reusable templates (names/addresses need on‑device NER — deferred). - Multi‑line prompts and code blocks keep their formatting.
- As you type (debounced ~400 ms), a gentle tooltip floats above the input when you've asked something similar before.
- Powered by IDF‑weighted trigram similarity with a length‑aware threshold — distinctive words count more than boilerplate, and short queries are held to a higher bar.
- Shows why it matched ("matched on …") and lets you step through multiple candidates; offers "see all" in the library when there are more.
- Click to copy by default — or opt into insert‑at‑cursor in settings. It never silently auto‑fills, and never overwrites what you've typed. Dismissible per query (× / Esc suppresses that prompt only); it never nags.
- A local, zero‑LLM classifier flags "minor" prompts (bare follow‑ups like "yes" / "continue", tiny fragments with no code, URL, structure, or length).
- Soft capture, never a silent drop: minor prompts are still stored, just hidden from the library and resurface by default — surfaced under
filtered (N)with a one‑click keep to promote any back. - Filter strength is a setting:
off(keep everything) ·balanced(default — only obvious throwaways) ·strict(only longer / structured prompts).
- Pause capture from the popup: for 1 hour or until you resume, with a live countdown and a toolbar badge. Capture resumes on its own.
- Per‑site switches folded into the capture‑health view — turn capture off on any site.
- Auto‑pause in incognito by default.
- Blocklist of domains and regex patterns that are never captured — with a live "test a prompt against your rules" box and a "preview impact on saved prompts" dry‑run, so a too‑broad rule is visible before you trust it.
- Fuzzy search (MiniSearch) in both the popup and the full library page.
- Platform filter, manual tags with multi‑tag filtering, pin to top, a favorites view, and bulk select / delete.
- Sorts: newest · most useful (usage × recency) · most used · longest unseen.
- Copy‑to‑clipboard with usage tracking; soft‑delete with undo.
- Export / import JSON (round‑trips), plus Markdown export.
- Settings: clear all data, purge soft‑deleted rows, and the blocklist above.
- An in‑extension privacy page stating exactly what is and isn't collected.
Clone from github.com/mehdirt/Deja, then:
npm install
npm run build # production build → dist/Then in Chrome:
- Open
chrome://extensions - Enable Developer mode (top‑right)
- Click Load unpacked and select the
dist/directory
Send a prompt on any supported site, then click the Deja toolbar icon (or press ⌘⇧K / Ctrl+Shift+K) to see it land.
For live development use
npm run devinstead ofnpm run build— Vite rebuildsdist/on save (reload the extension atchrome://extensionsto pick up changes).Chrome Web Store install will replace this path once the listing is Public; until then the listing stays Unlisted for testers.
- Just work. Prompt on any supported site as you normally do. Each prompt is saved the moment you hit Enter — a brief "remembered · undo" toast confirms it.
- Reuse in‑context. Start typing something you've asked before; when the tooltip appears, click it to copy your earlier version (or step through
1/3matches with›). - Browse the popup. The toolbar icon opens a search box + recent prompts (pinned first). Hit
library →for the full page. - Curate the library. Search, filter by platform/tag, pin favorites, tag prompts, bulk‑delete, and sort by usefulness. Deleted prompts are undoable.
- Control capture. Use ⏸ pause in the popup before a private session; switch off a site or set filter strength in settings; add blocklist rules for anything secret.
- Take your data. Export JSON or Markdown anytime; import a JSON export back; clear everything from settings.
| Shortcut | Action |
|---|---|
⌘⇧K / Ctrl+Shift+K |
Open Deja (global) |
⌘K / Ctrl+K or / |
Focus search (library) |
↑ / ↓ |
Move selection (library) |
Enter |
Copy selected prompt |
⌫ / Delete |
Soft‑delete selected |
Esc |
Clear search / dismiss tooltip |
The global
⌘⇧Kis a suggested binding; Chrome occasionally leaves it unassigned to avoid conflicts. Confirm or rebind it atchrome://extensions/shortcuts.
Deja runs entirely on the client across four execution contexts, all TypeScript, bundled by Vite via @crxjs/vite-plugin. There is no backend.
┌──────────────────── host page (chatgpt.com, claude.ai, …) ─────────────────────┐
│ content scripts (isolated world) │
you ─► │ capture.ts ──PROMPT_CAPTURED──►┐ resurface.ts ──SIMILAR_QUERY──►┐ │
type │ captureGate.ts · blocklist cache · health probe (read-only, debounced)│ │
└────────────────────────────────────────┼────────────────────────────────-─┼────┘
│ chrome.runtime messaging │
▼ ▼
background service worker (src/background/index.ts)
• classify + savePrompt ──────────────► IndexedDB (Dexie)
• findSimilar over the prompt pool ◄──── IndexedDB
• pause badge via chrome.action + alarms
▲
│ Dexie (same extension origin)
popup & options (React) ──┘ search · tag · pin · export · settings
- Content scripts —
src/content/<platform>/index.ts, one per site. Each resolves the prompt composer via its selector fallbacks and wires up four shared helpers fromsrc/content/shared/:capture.ts— watches Enter / send clicks, debounces duplicates, checks the gate + blocklist, and sendsPROMPT_CAPTURED.resurface.ts— debounced similarity queries, the Shadow‑DOM tooltip, copy / insert.captureGate.ts— a synchronous, fail‑open snapshot of pause / per‑site / incognito state for the hot path.health.ts— the capture‑health probe. They fail silently and never block the host page.
- Background service worker —
src/background/index.ts. The only writer to IndexedDB from outside the UI. HandlesPROMPT_CAPTURED(redact PII → classify → store),SIMILAR_QUERY(score the pool → top matches),OPEN_LIBRARY, andUNDO_CAPTURE, and paints the pause badge. MV3 workers are short‑lived, so it keeps no state in module scope — everything persists through Dexie /chrome.storage. - Popup —
src/popup/. Search + recent prompts (pinned first) + pause control. - Options / Library —
src/options/. The full library, settings, and privacy page.
Shared core lives in src/lib/ (pure, unit‑tested):
| Module | Responsibility |
|---|---|
db.ts |
Dexie schema (single prompts table) + all CRUD; soft‑delete, import/export |
types.ts |
Prompt, Platform, runtime message shapes, PLATFORM_LABEL / PLATFORM_COLOR, FilterStrength |
search.ts |
MiniSearch fuzzy index, rebuilt in‑memory |
similarity.ts |
IDF‑weighted trigram similarity + length‑aware threshold (resurface) |
classify.ts |
Selective‑capture classifier (minor vs keep, by strength) |
pii.ts |
Local PII detection + redaction (regex + Luhn) applied before storage |
ranking.ts |
"Most useful" score (usage × recency) |
sensitive.ts |
Capture‑eligibility: refuse credential / OTP / payment / non‑composer fields; URL minimization |
blocklist.ts |
User blocklist (domains + regex) storage + matching |
health.ts |
Per‑platform capture‑health storage |
prefs.ts |
Preferences: resurface click, filter strength, pause, per‑site, incognito, PII redaction |
markdown.ts · format.ts |
Markdown export · text/time formatting |
Path alias @/ → src/ (kept in sync in tsconfig.json and vite.config.ts).
Selectors are deliberately confined to
src/content/<platform>/index.tsso a DOM change is a one‑file fix. See CLAUDE.md for the full contributor map and DESIGN.md for the visual system.
Local‑first is the product, not a footnote: no network calls, no telemetry, no accounts, no cloud. Only the prompt text you type is stored — never the AI's replies, never credentials — and detected PII is redacted before storage so raw values never land on disk or in exports. Prompts live in IndexedDB; settings/blocklist/health live in chrome.storage.local; both stay on your machine. You can export, blocklist, pause, redact, or wipe everything at any time.
Permissions requested: storage (save your library and settings locally), alarms (clear the capture‑pause badge when its timer ends), and host access only on the five supported sites. Details in the in‑extension privacy page and SECURITY.md.
| Command | What it does |
|---|---|
npm run dev |
Vite dev build with HMR; load dist/ as an unpacked extension |
npm run build |
Production build to dist/ (runs tsc --noEmit first) |
npm run typecheck |
tsc --noEmit only |
npm run lint |
ESLint over src |
npm run test |
Vitest run (one‑shot) — 101 unit tests |
npm run test:watch |
Vitest in watch mode |
npm run format |
Prettier over src |
npm run release -- <version> |
Bump the version, build, and zip dist/ for the Web Store |
Run a single test file: npx vitest run src/lib/similarity.test.ts
Project layout
src/
background/ service worker (DB writes, similarity, pause badge)
content/
<platform>/ one entry per site — selectors + wiring only
shared/ capture, resurface, captureGate, health, blocklist cache, toast
lib/ pure core logic + colocated *.test.ts
options/ library page (React): Library, Settings, Privacy
popup/ popup (React)
ui/ shared React components (PromptCard, PauseControl, …)
styles/ globals.css (design tokens + component primitives)
assets/ toolbar icons (generated from icon.svg)
manifest.json MV3 manifest (version injected from package.json at build)
site/ the marketing landing page (single self-contained file)
store/ Chrome Web Store listing copy + asset/launch plan
Tests run on Vitest + happy‑dom. CI (.github/workflows/ci.yml) runs typecheck + lint + test + build on every push/PR and uploads the built dist/ as an artifact. Users can send feedback from settings → Feedback (opens a prefilled GitHub issue they review and submit — no telemetry); override with a hosted form via FEEDBACK_URL in src/lib/feedback.ts.
Add a new site:
- Create
src/content/<platform>/index.ts— define an orderedSELECTORSlist and agetInput(), then wirestartBlocklistSync(),startCaptureGate(),attachSubmitHook(),startHealthProbe(), andattachResurface()(copy an existing entry). - Register the content script (matches + js) in
src/manifest.json. - Add the platform to
Platform,PLATFORM_LABEL, andPLATFORM_COLORinsrc/lib/types.ts(per‑site switches and health pick it up automatically).
Tune behavior (centralized, provisional constants meant to be set from real usage):
- Resurface matching —
src/lib/similarity.ts(MIN_QUERY_LEN, threshold, IDF strength). - Selective capture —
src/lib/classify.ts(SHORT_CHARS,RICH_WORDS, the trivial‑phrase set).
Respect the guardrails: never add network calls/telemetry without an explicit user‑facing opt‑in; never block the host page; capture only prompt text (not responses); keep selectors in the per‑platform file. Follow the design tokens in DESIGN.md — compose the .dj-* primitives, don't hard‑code hex. Add a *.test.ts next to any new lib module.
Deja ships in usable phases (see ROADMAP.md). The big upgrades on the horizon — to be decided from real usage, not guessed:
- Semantic resurface via on‑device embeddings — catch paraphrases trigram similarity can't ("write a poem about cats" ↔ "compose verse about felines"), as a hybrid that keeps today's instant lexical path and falls back to embeddings only when it finds nothing. Still fully local.
- Optional, bring‑your‑own‑key LLM helpers — an on‑demand "improve this prompt" and one‑tap tag suggestions, gated behind a settings toggle, never in the capture path.
- Smarter ranking & storage — reuse/recency‑aware resurface ranking, and a worker‑side trigram/inverted‑index cache for very large libraries. (Exact + near‑duplicate capture collapsing already ships in v0.4.1.)
- Activity heatmap, prompt chaining, encrypted (E2EE) cloud sync, and team/shared vaults — in rough order of value.
Deliberately excluded, on principle: 0–100 prompt "scores", streaks/gamification, AI‑generated "prompt of the day", and anything that requires reading your prompts on a server. See CONCEPT.md for the reasoning.
Deja is local‑first, so there's nothing server‑side to deploy — "launching" means two independent things: publishing the extension, and (optionally) hosting the landing page.
- Bump the version in
package.json(it's injected into the manifest at build). - Build:
npm run build. - Zip the build output — the contents of
dist/, not the folder:cd dist && zip -r ../deja-<version>.zip . && cd ..
- Create a developer account at the Chrome Web Store Developer Dashboard (one‑time US$5 fee).
- Create the listing and upload the zip. Fill in the description, permission justifications, single‑purpose statement, and data‑safety answers — all drafted in
store/listing.md. Add the1280×800screenshots already instore/(shot list + remaining promo/video notes instore/assets.md), and link a hosted privacy‑policy URL (site/privacy.htmlvia Netlify Drop — see the GTM plan). - Submit for review as Unlisted first. Expect a review wait; ship updates by repeating steps 1–3 and uploading a new zip. The repo is open source (MIT); leave the listing Unlisted until the Week 2 go/no-go.
site/index.html (+ site/privacy.html) is self‑contained with no build step and no third‑party requests. Host on any static host — Netlify Drop is the GTM plan default (drag the site/ folder); GitHub Pages / Vercel / Cloudflare Pages also work. Before going live, replace REPLACE_EXTENSION_ID (store URL). Source links already point at the public GitHub repo.
Per the roadmap: invite ~50 users from communities you're already in, watch how the resurface moment lands, and tune the thresholds before any broad launch. No analytics by design — listen, don't measure.
Firefox/Edge are not targeted yet (this is an MV3 Chrome build); both are plausible later with minor manifest work.
Vite · React 18 · TypeScript · Tailwind CSS · Dexie (IndexedDB) · MiniSearch · @crxjs/vite-plugin · Vitest + happy‑dom. No web fonts, no runtime third‑party services.
| Doc | What's in it |
|---|---|
| CONCEPT.md | The product thesis, principles, and what's in/out of v1 |
| ROADMAP.md | The phased plan and operating notes |
| DESIGN.md | Visual identity — tokens, type, components, voice |
| SECURITY.md | Security & privacy posture and threat model |
| CLAUDE.md | Contributor guidance and the full module map |
| store/ | Web Store listing copy + asset/launch plan |
MIT — © 2026 mehdirt.