DEV Community

Cover image for I built a local-first AI prompt manager — here is why offline-first was worth the extra complexity
Willy Nelson
Willy Nelson

Posted on • Edited on

I built a local-first AI prompt manager — here is why offline-first was worth the extra complexity

PromptVault by WILxAI
Every developer I know who uses AI tools daily has the same problem.

You write a prompt that works perfectly. You get exactly the output you needed from Claude or Cursor or ChatGPT. You close the tab.

Next week the same problem comes up. You spend 20 minutes trying to reconstruct that prompt from memory.

I got fed up with this and built PromptVault — a local-first prompt manager that works offline, has a browser extension for any AI tool, and includes AI features to help you write better prompts.

Why local-first instead of just building a simple cloud app

The obvious approach was to build a database, a backend, user accounts, and store everything server-side. I chose not to for two reasons.

First — developers do not trust tools that store their prompts in someone else's database. Your prompts contain your entire thinking process, your preferred approaches, your project context. That is sensitive.

Second — a local-first app is faster. There is no round trip to a server. Search is instant. The vault opens immediately. It works on a plane.

The trade-off is complexity. Offline-first means you have to handle sync conflicts, queued operations, and cases where the same data gets modified on two devices.

How the sync works

Every write operation (create, update, delete) gets added to a local sync queue stored in IndexedDB. Each entry records the operation type, the entity ID, and the full payload.

When the app comes online it flushes the queue to Supabase in order. If a sync operation fails it retries up to 3 times with exponential backoff. After 3 failures it removes the entry and logs the error.

Conflict resolution is Last-Write-Wins based on the updatedAt timestamp. This is simple and correct for this use case — you are almost never editing the same prompt on two devices at the exact same moment.

The browser extension

The extension uses Chrome MV3. It injects a small ⚡ button into text fields on claude.ai, chatgpt.com, and any other page with a textarea or contenteditable element.

Clicking the button opens a mini search popup that reads from PromptVault's local IndexedDB. You search, click a prompt, it pastes directly into the active field. No switching windows.

The AI features

Everything AI-related uses BYOK — Bring Your Own Key. You configure your provider (Claude, GPT-5, Gemini, Groq, or any OpenAI-compatible endpoint) in Settings. The app calls your provider directly. No data goes through PromptVault's infrastructure.

The main AI features: Vibe Mode (rough idea to structured prompt), Prompt Improver (4 modes), Variant Generator (Concise, Detailed, Reframed versions), and a Debugger that runs 5 checks on any prompt.

Try it

https://promptvault-wilxai.vercel.app — free tier is 100 prompts with all features. No account required to start.

Pro is $9/month for unlimited prompts and cross-device sync.

Happy to answer questions about the architecture in the comments.

Top comments (10)

Collapse
 
crisiscoresystems profile image
CrisisCore-Systems

Really solid direction. I think the strongest choice here is not just offline support, but treating local first as a trust and latency decision instead of a feature add on. For a prompt tool, that makes a lot of sense because the data is sensitive and the fast path should belong to the user.

One thing I would probably revisit is dropping failed sync entries after 3 retries. For this kind of product, silent loss feels riskier than indefinite pending plus visible user resolution. Last Write Wins also seems reasonable here as long as the conflict cost stays low, but the failure path is where trust usually gets decided. Nice build.

Collapse
 
wilxai profile image
Willy Nelson

Thank you for this — and you are right on the failure path.

The 3-retry-then-drop decision was a pragmatic first pass but
you have identified exactly why it is the wrong call for this
specific product. Silent data loss in a tool built around the
promise that your prompts are safe is a contradiction I should
not have shipped.

The better pattern is what you are describing — move failed
entries to a visible "sync failed" state rather than discarding
them. Give the user a conflict resolution panel that shows what
failed, what the local version looks like, and a manual "retry
or discard" decision. That way the failure path becomes a trust
moment instead of a trust violation.

On LWW — agreed it holds for the current use case. The conflict
cost stays low because the typical user is one person across two
devices, rarely editing the same prompt simultaneously. Where it
would break down is a future team/shared vault feature where
two people could genuinely diverge on the same prompt. Worth
documenting that as a known boundary condition now.

Your framing of local-first as a trust and latency decision
rather than a feature is something I want to carry into how I
talk about the product. I had been describing it as a privacy
feature which is accurate but narrower than what you said.
The fast path belonging to the user is the cleaner way to say
what local-first actually means for someone using this daily.

I am going to revisit the sync failure handling this week.
Appreciate you taking the time to go deep on this.

Collapse
 
crisiscoresystems profile image
CrisisCore-Systems

I am glad that framing landed for you. In local first systems the UI is a promise of capture while the sync engine is just the logistics. If the logistics fail then silence is a breach of contract. Moving to a visible failed sync state treats the user as a sovereign participant rather than a passive consumer. It turns a technical failure into a moment of transparency.

The fast path concept is the real win. When you remove the server from the critical path of thought you end that subtle need to wait for permission from a remote database. It turns the tool into an extension of user agency instead of a lease on it.

Refactoring the failure path is the right move because that is where the engineering posture really lives. I am looking forward to seeing how the vault evolves.

Thread Thread
 
wilxai profile image
Willy Nelson

This has been sitting with me since you posted it and I wanted to come back with something concrete rather than just acknowledgment.

You were right. I shipped it this week — the failure path is now a visible resolution state instead of a silent drop. Failed entries move to a 'failed' status in the queue, never auto-deleted. The user gets a panel showing exactly what failed, what the local data looks like, and a manual retry or discard choice. Discard is the only code path that ever removes an entry, and it only runs when the user explicitly triggers it.

Your framing of silence as a breach of contract was the clearest way I have heard that problem stated. It made the fix obvious once I stopped thinking of it as an edge case and started thinking of it as the trust boundary.

The sidebar also shows a yellow warning badge when failures exist so nothing sits invisible. Settings exposes the full resolution panel with error messages and payload previews so the user always knows what is pending their decision.

On the fast path framing — I updated how I describe local-first in the product copy this week. 'Privacy feature' is gone. The language is now about the critical path of thought not requiring permission from a remote database. It is a more accurate description of what the product actually does for the person using it daily.

Appreciate you taking the time to go this deep. The vault is better for it.

Thread Thread
 
crisiscoresystems profile image
CrisisCore-Systems

Silence was never just a sync bug. It was a governance bug.

What you changed here is bigger than queue behavior. You moved failure from hidden system discretion into explicit user jurisdiction. That is the difference between software acting on someone and software reporting to them.

Keeping failed entries durable until the user resolves them is exactly the right posture. It preserves auditability, preserves agency, and prevents the system from laundering uncertainty into disappearance.

The yellow badge is also the right kind of friction. Not punitive friction. Decision visibility. The system is saying something unresolved exists and it belongs in the user’s field of awareness.

The product copy change matters too. A lot of local first tools undersell themselves by describing the pattern as privacy alone. Privacy matters, but the deeper value is that thought capture does not sit behind network permission. That is an architectural freedom, not just a policy preference.

You pushed the vault in the right direction. What you have now is closer to a sovereign local system than a convenience app with offline support.

Thread Thread
 
wilxai profile image
Willy Nelson

Sovereign local system is the phrase I did not know I was building toward. I am keeping that.

The governance framing also reframed something for me — the failure path was not a reliability problem, it was a jurisdiction problem. That distinction changes how I will think about every trust boundary in this codebase going forward.

Four replies in and you have shaped the product more than most people who actually use it. Appreciate you.

Thread Thread
 
crisiscoresystems profile image
CrisisCore-Systems

That distinction changes everything, because once you see failure as a jurisdiction question, a lot of design decisions stop being “small UX choices” and start revealing who the system is actually allowed to act for.

A good trust boundary is not just about what the software can do. It is about what it is permitted to conclude, hide, discard, or retry without the user’s explicit say.

You corrected that in the right place. The system now reports uncertainty instead of consuming it on the user’s behalf. That is a much stronger foundation than “offline support.” It gives you a governing rule you can reuse across the rest of the codebase.

Thread Thread
 
wilxai profile image
Willy Nelson

"Permitted to conclude, hide, discard, or retry without the user's explicit say" is the clearest definition of a trust boundary I have read.

I am writing that down as a literal design rule for the codebase. Every time a decision gets made below the surface — a retry, a deletion, an assumption — that sentence becomes the test. Did the user permit this, or did the system just decide it was convenient?

The shift from "offline support" to "reports uncertainty instead of consuming it" is also the frame I was missing for how to talk about what this product actually does. It is not a capability description. It is a posture description.

This thread has given the vault a governing principle it will carry for the rest of its life. Thank you for that.

Thread Thread
 
crisiscoresystems profile image
CrisisCore-Systems

That is the right place to land it.

Once a system has a governing rule for what it may conclude, hide, discard, or retry without permission, a lot of future design decisions get easier and a lot fewer trust mistakes survive refactor.

You did the important part: you turned reliability from a background concern into an explicit constitutional rule for the product.

Thread Thread
 
wilxai profile image
Willy Nelson

Constitutional rule is exactly the right frame. It means the question stops being 'what should we do in this edge case' and becomes 'does this decision comply with what the system is allowed to do.' That distinction will survive every refactor, every new feature, every junior contributor who never read the original thread.

What started as a sync bug became a product constitution. I did not expect that when I posted this.

Thank you for pushing it that far.