Asked Before finds the how-to questions you keep asking a chatbot, puts each on a card with every time you asked it, and won't mark a card Proven until you answer a fresh problem on it without AI.

Live Demo → · Demo Video · Source Code

Cloudflare Workers React Tailwind CSS shadcn/ui Bun Claude

Asked Before banner

Try it

  1. Open asked-before.m1kuorg.workers.dev and choose Try it as a guest. There's no account or password. Every guest shares one workspace, with the demo shelf already sorted, and it's wiped daily at midnight UTC.
  2. On Home, choose Start a cold rep beside Undo a git commit, which the demo shelf asked 9 times.
  3. Answer in the box, in your own words, without AI. Pass, and the card is Proven with today's date. Not yet, and you get the reason, the lesson again, and a different problem.

The app runs on Cloudflare's free plan, so it has one shared daily budget: 20 model requests a day for the whole app, guests included. A cold rep takes 2, one to write the problem and one to check the answer, so the day holds at most about 10 cold reps for everyone, fewer when uploads and agents spend requests too. Browsing cards, Search, Analysis, and the Roadmap take none. Once the day's requests are used, the app says so, and they open again at midnight UTC.

Inspiration

On the demo shelf, "Undo a git commit" comes back 9 times, from March 2025 to September 2026. A chatbot answers each one, and the answer works, so nothing ever says the question came back. Your chat history knows, but it shows what you talked about, never whether you can now do it alone. Asked Before reads that history for the questions that come back, then tests each one the only way that counts: a new problem, and no AI.

Who it's for

Anyone who keeps asking a chatbot the same how-to, and developers learning on the job first of all: the Roadmap places cards on a path of 13 developer areas, from the terminal and Git to deploying. Getting a data export takes a request and a wait, so an upload is a monthly checkup. For questions asked to a coding agent, the MCP server records them as they're asked.

What it does

You upload a ChatGPT or Claude data export onto a shelf. Your browser keeps only your own messages, the model groups the ones that ask the same thing onto one card per skill, and code counts and dates every ask. Each card then waits for a cold rep: a fresh problem of the same skill, answered in a plain box without AI.

Features

  • Shelves: one per part of your life, such as Work or Bootcamp. Shelves never share cards.
  • Upload: the zip ChatGPT or Claude emailed you, or the conversations.json inside it. Your browser reads it and previews what will be sent before anything leaves. Next month's export onto the same shelf skips the messages already there.
  • Cards: every ask, word for word, with its date, and an ask strip with a dot for each time you asked. Cards are listed most asked first.
  • Cold reps: the first rep brings a lesson. A card is To prove until you pass, then Proven. Ask it again after that and it's Asked since proven, waiting for a fresh rep.
  • Home: the cards that need a cold rep across your shelves, what you proved last, and your latest asks.
  • Search: "Have you asked this before?" finds every message of yours with all the words you type, and the cards they're on.
  • Analysis: how often you asked something you'd asked before, how long until you asked again, and cold reps passed and not yet, by month.
  • Roadmap: your cards on 13 developer areas, and the next one to prove.
  • Connect your AI: an MCP server for Claude Code, with or without the plugin, Claude Desktop, and Cursor. Its tools record a how-to question as you ask your agent, and run a cold rep in the chat.
  • Your data: download everything as one JSON file, or delete your account.

How it works

1. Upload a chat export. The browser parses it and shows exactly what will leave your computer.

Upload a chat export

2. See what you keep asking. Each card holds every ask of one skill, most asked first.

See what you keep asking

3. Prove it without AI. A fresh problem of the same skill, answered in a plain box.

Prove it without AI

4. Fail, and try again. Not yet comes with the reason, the lesson again, and a different problem.

Fail and try again

5. Proven, until you ask again. Ask a proven card again, and it needs a fresh cold rep.

Proven until you ask again

Screenshots

Screen What it shows
Home The cards that need a cold rep, then what you proved last
Search Every message with the words you typed, and its card
Analysis Re-asks and cold reps, month by month
Roadmap Your cards on 13 developer areas
Connect your AI Setup for your agent, with a key
Playground One real API request, beside the same request as code
Privacy What leaves your computer, what the model sees, what's kept

Built with the Devpost Learn Skill Pack

The Skill Pack's six skills, 1-start through 6-ship, are pinned in skills-lock.json. The entrant stepped through them by hand: at each stage the Skill Pack interviewed the entrant (flipped interaction) and wrote the planning document from the answers, which the entrant approved. The documents are in the repo's devpost/ folder:

Stage Document What it fixed
Scope scope.md The problem, the user, and the proof of concept's boundary
Requirements prd.md The core journey, the screens, each feature's behavior, and the non-goals
Spec spec.md The stack, where it runs, and each component and route
Build checklist.md 6 build slices, each verified against the merged code, 2 hands-on checkpoints, a final review, and a code tour

The proof of concept the Skill Pack planned ends at commit b1d7e64: a Bun server with routes to sort, write a cold rep, and check it, and one plain page, with no database. The hosted app then went past the plan, which had cut accounts, a database, hosting, Claude exports, and in-the-moment capture. It grew from tickets in the entrant's private planning repository: accounts, the ChatGPT and Claude readers, the move to Cloudflare, the MCP server and plugin, the guest workspace, Search, Analysis, and the Roadmap.

How we built it

Architecture

Asked Before architecture

Made with Archify.

  • One Worker serves the React pages as static assets and answers the API under /api/v1. The data is in D1.
  • A Sorter Durable Object per shelf sorts an upload one batch of 300 messages per alarm, so a sort carries on after a restart. A cron every 5 minutes restarts a stalled sort and wipes the guest workspace once a day.
  • One client: the pages reach the server only through the same TypeScript SDK that scripts use, and the MCP server is built on that SDK too.

Code counts, the model reads

Every request goes to claude-haiku-5-5 with structured outputs, and code checks every reply:

Request Effort What the model returns What code checks
Sort an upload Medium, then high for a last pass Which card each message joins, and new skills Every id was sent, none is placed twice, a new skill has 2 or more messages, a name is one line of at most 60 characters
Place a recorded question Low The card it joins The same, after an exact match that needs no model
Write a cold rep Medium A hidden case, then the problem, and a lesson on the first rep The case is thrown away; only the lesson and the problem are shown
Check an answer Medium A reason, then the verdict Pass or fail only

The model sees message text cut to 300 characters and short ids, never a date. A reply that fails a check is asked again with the error, up to 3 tries in all, and then the sort fails rather than make a bad card. A refusal or a cut-off reply is an error, never a result. The counts, dates, statuses, Search, Analysis, and the Roadmap are all code.

Tech stack

  • Runtime: Cloudflare Workers, D1, Durable Objects, Cron Triggers
  • Pages: React 19, Tailwind CSS 4, shadcn/ui on Base UI, bundled by Bun
  • Model: Anthropic SDK, claude-haiku-5-5, Zod schemas for structured outputs
  • Agents: MCP server on @modelcontextprotocol/server, a Claude Code plugin
  • Checks: 36 check:* scripts and an end-to-end journey, with Playwright and the official MCP client

Challenges we ran into

  • 10 ms of CPU per request. Workers Free gives a request 10 ms of CPU, too little for a slow password hash. So the SDK derives a key from the password with scrypt in the browser, and the server keeps only a salted SHA-256 of that key. The password never reaches the server.
  • A problem that names its own fix isn't a test. The cold rep prompt now has the model write a hidden case first, holding only the facts that decide it, so the problem states those facts instead of the answer. check:reps runs five live reps and flags any problem that names the fix, with a give-away rule first proven on 692 hand-labeled problems of one skill, 85 of which give the answer away.
  • Sorting real histories, not just the demo. The demo export is small and clean, so the sort is also checked on a made-up history of 469 labeled messages: 74 skills, many of them near misses of each other, and 257 one-offs. A sentence on a task's variants, with each batch ordered by shared words, raised the share of same-skill pairs found on one card from 59% to 71%. A last pass at high effort over the messages left off a card raised it from 69.2% to 82.4%, while precision fell from 95.3% to 89.2% (check:sort-recall, means of 10 runs).

Accomplishments that we're proud of

  • The whole journey runs with the real model and nothing stubbed. bun run journey signs up, uploads the demo export, fails a cold rep, passes the next, makes an API key, lists cards through the SDK, and records a question through the MCP server, in 7 model requests.
  • Every count and date on screen comes from code, and Search, Analysis, and the Roadmap ask the model nothing.
  • The privacy line held from a local page to a hosted app. The browser sends only your own messages, each with its time and the export's id for it, plus the file's name and which chatbot it came from. The chatbot's answers and your conversation titles never leave your computer, and your password never reaches the server.
  • It runs on Cloudflare's free plan, guests included.

What we learned

  • A hidden field shapes the visible one. Having the model write the case's facts before the problem gave the problem those facts to state, instead of the fix.
  • More effort isn't always better. On the first sort, high effort found no more than medium in twice the time and tokens; only the last pass over the leftovers gained from it.
  • A host's limits shape the design. The CPU limit moved password hashing into the browser, and the free plan's daily writes became the app's daily caps.
  • Let code own every number. With counts and dates out of the model's hands, the model's mistakes are in grouping and wording, and each card shows every message word for word, so a wrong merge is visible.

What's next

Known limitations

  • The daily budget is shared: at most about 10 cold reps a day for every visitor together.
  • The guest workspace is shared, so guests see each other's uploads and cold reps until the daily wipe.
  • The model sees the first 300 characters of a message, and messages over 2,000 characters are skipped.
  • Search matches words, and the Roadmap places cards by the words in their names.
  • Only ChatGPT and Claude exports are read.
  • There's no password reset or email check.

Roadmap

The limitations above are what's next; none has an issue filed yet. Proposed features and known issues will go in issues.

Getting started

Prerequisites

  • Bun, and Node.js 22 or later for Wrangler
  • An Anthropic API key
  • Playwright's Chromium, for the browser checks: bunx playwright install chromium

Installation

git clone https://github.com/m1kuorg/asked-before.git
cd asked-before
bun install
cp .env.example .env   # set ANTHROPIC_API_KEY
bun run dev

bun run dev builds the pages, applies the D1 migrations locally, and starts wrangler dev. Run bun run typecheck, bun run journey, or any bun run check:<area> to test it.

License

Apache-2.0. See LICENSE for more information. Third-party files keep their own licenses, listed under AI disclosure.

Acknowledgments

  • Devpost Learn Skill Pack
  • The landing page's three photographs and one newsreel, via Wikimedia Commons: "Nurmeksen yhteislyseo vuonna 1958. Ylioppilaskirjoitukset." by Topi Toivanen (CC BY 4.0); "A Pupil" by Bror Brandt (CC BY 4.0); "Card catalog" by Harris & Ewing (public domain); and the newsreel "Examen in de Friese taal" by Polygoon Hollands Nieuws (public domain)
  • Archify
  • Shields.io

AI disclosure

  • AI assistance: the entrant built Asked Before with coding assistants: Astra 6 via Codex, Claude 5.5 via Claude Code, Gemini models via Antigravity, and DeepSeek, GLM, and other models via OMP. Commits Claude co-wrote carry a Co-Authored-By trailer. An agent drafted this description from the source code.
  • When: inside the submission period, September 22 to October 26, 2026. Work began on October 15 in the entrant's private planning repository, which holds the tickets; this repository's commits run from October 16 to 26, and its history was rewritten on October 25.
  • Pre-existing work: no pre-existing code of the entrant's. Third-party files, under their own licenses: the Devpost Learn Skill Pack (MIT); the impeccable skill (Apache 2.0) in .claude/; shadcn/ui's components (MIT), the starting point of src/components/ui/; the MCP TypeScript SDK (Apache 2.0), Zod, and @noble/hashes (MIT), bundled into the plugin's server; Schibsted Grotesk and JetBrains Mono (SIL OFL 1.1); and the archival photographs and newsreel credited above.

Built With

  • anthropic-claude
  • anthropic-sdk
  • base-ui
  • bun
  • claude-code
  • claude-haiku
  • cloudflare-d1
  • cloudflare-workers
  • devpost-learn-skill-pack
  • durable-objects
  • fflate
  • model-context-protocol
  • noble-hashes
  • playwright
  • react
  • shadcn-ui
  • tailwindcss
  • typescript
  • wrangler
  • zod
Share this project:

Updates

Submission history