Loading the page.
Documentation
Journal
Frontend
Backend
Admin
CI/CD
Loading the page.
In the video, a free AI model built me a quiz website for a cleaning company. A client answers four questions, sees the cleaning price right away and leaves a phone number. A couple of seconds later, the lead shows up in a password-protected admin panel.
I didn't pay a cent and didn't write any code by hand. The agent is OpenCode, the model is a free one from its own store, and the project is built on the Spec-Driven Company starter. Below are the same steps, all three prompts and what to do when the free model hits its limit.
No card needed.
Open a terminal and paste the command:
curl -fsSL https://opencode.ai/install | bashWhen the install finishes, create an empty folder, for example quiz, go into it and start the agent with the opencode command.
In OpenCode, type /models and pick a model marked as free from the OpenCode Zen list.
The free model's limit is about 200 requests a day. The starter, the quiz and the admin panel use up a good share of that, so the model may hit the limit halfway through. That's normal; step 7 covers what to do then.
The first prompt puts the starter into the folder: rules for the agent, Next.js, Tailwind and a Convex database. The agent downloads the files, builds the project and sets up a password-protected admin panel on its own. Paste the whole prompt and send it:
Answer in the language of this prompt. Set up Spec-Driven Company in this folder with the Next.js and Convex stack. This folder is the project folder: create main/ and _wt/ in it. Download AGENTS.md, CLAUDE.md, CODEX.md, docs/README.md and docs/journal.md from https://raw.githubusercontent.com/eugeneshilow/spec-driven-company/main/ (copy the address exactly as written) and save them into main/ under the same paths. In this folder write two pointer files: AGENTS.md with the text "This is the project folder. The repository is main/; worktrees live in _wt/, one per task. Read main/AGENTS.md first and follow it. Nothing is edited in main/ by hand." and CLAUDE.md with the single line "@main/AGENTS.md". Read main/AGENTS.md and follow it from now on. Then build the app by the recipe below, inside main/. This is a local setup: no remote, no pull request, no deployment; git init -b main in main/ and end with one commit on main (AGENTS.md allows this for the first setup). Every task after this setup goes to a worktree under _wt/, as main/AGENTS.md says.
What is in it: Node.js and pnpm; Next.js with the App Router and TypeScript; Tailwind CSS; Convex for the database and server functions; Vitest for tests, ESLint and Prettier for the check. Deployment target is Vercel, not part of this setup. Take current stable releases, LTS where there is one, never a pre-release. Use the Node and pnpm already installed if they are compatible with this stack; if no compatible Node is present, install the current LTS. Install only what is missing, upgrade nothing that works (a peer dependency that a new package demands is not an upgrade).
Build, in this order:
0. Before the first command, fill in main/docs/README.md from this recipe: the project name is the folder name unless told otherwise, what it is made of, what is done when. Invent no product requirements. AGENTS.md wants the spec before the code; this recipe is the spec. After the build, correct it to what was actually built. Fill in the first entry that is already in main/docs/journal.md too: the date, the agent, and my words, the first sentence of this prompt.
1. Create the Next.js app in main/ with TypeScript, Tailwind, ESLint and the App Router. The generator refuses a folder that is not empty and writes its own AGENTS.md, so scaffold into a temporary subfolder main/scaffold-tmp with the agents file and git disabled, move the files up into main/, delete the subfolder, and drop exactly these three pieces of template art, files only, no folders: the generated README.md (the project's page is docs/README.md), the svg files inside public/, and the Google font wiring. AGENTS.md, CLAUDE.md, CODEX.md and docs/ stay as downloaded except the parts you are told to fill in: switch off both things that write Next's own rules into AGENTS.md, the generator flag (--no-agents-md, or whatever the current --help calls it) and the config option that lets next dev regenerate it (agentRules: false), and list AGENTS.md, CLAUDE.md and CODEX.md in .prettierignore.
2. Add Prettier and Vitest. Add one script "check" that runs the formatter check, the linter (call eslint directly; next lint is gone), the type check and the tests, in that order, the formatter and the linter over the files git tracks only (git ls-files), not the whole folder, so a build machine's caches do not count, and writes its result (green or red, with the time) to a small ignored file, so the admin page can show it. The type check needs Next's generated route types, so generate them inside the check before the type step.
3. Page /: the project name from docs/README.md, one sentence about what it is, and a link to /admin. Plain, readable, no template art.
4. Page /admin: the glass. It renders docs/README.md and docs/journal.md from the files as HTML (any small markdown library is fine), lists every decision (journal headings that carry βοΈ) with its date and name, and shows the result of the last check run from the file the check writes. Reading the files at request time is enough; no database is needed for this page. The page is behind a sign-in page, not the browser's own password prompt (the browser forgets that prompt when it closes and asks again on every address): Next's request-level check (the middleware or proxy file, whatever the current version calls it) sends a GET without a session to /login?next=<path> and answers any other request without a session with 401. /login checks the password against the ADMIN_PASSWORD variable and sets a session cookie for 90 days (HttpOnly, Secure except on localhost, SameSite=Lax, path /); its value is the expiry time plus an HMAC-SHA256 signature of it keyed with ADMIN_PASSWORD, so changing the password signs everyone out and no session table is needed. A visit in the last 30 days of the session renews it for another 90, /logout clears it, and the check still accepts HTTP basic auth with the same password, for scripts and tests. Without the variable the page answers "closed: set ADMIN_PASSWORD" instead of the content. Generate the password yourself (openssl rand -hex 16), write it into .env.local as ADMIN_PASSWORD (never committed) and list the name in .env.local.example; never print the value.
5. One test: /admin without a session redirects to /login with the path to return to, and with the password from .env.local sent as basic auth (or from the ADMIN_PASSWORD environment variable when that file is absent: the pull request check and the build machine have no .env.local) lists at least the first decision inside the decisions list (give that list a test id and look only inside it). Make the check green. The test runs in the node environment, no jsdom: render the page to a string; the page reads docs/ from disk, and build machines run a newer Node.
6. Install Convex. Add the schema file with no tables yet, a client provider that renders the app without Convex while its URL variable is empty, and an .env.local.example that names the variables the Convex CLI will fill (CONVEX_DEPLOYMENT and NEXT_PUBLIC_CONVEX_URL); make sure .gitignore does not hide the example file. Do not write server functions yet: their generated types appear only after a deployment exists. Do not run the Convex login: it creates a cloud deployment on the human's account, and that is an access decision. Prepare everything and stop with one line: "Convex is wired; run npx convex dev and log in when you want the database live."
7. Fill in the Stack section of AGENTS.md: what it is made of, the check command, the run command with the address. Bring docs/README.md in line with what was built. Add a second journal entry above the first, as a decision: Convex is wired but not logged in; what was installed, what was skipped, what the human does next.
8. Start the dev server from main/, make one commit on main, and report.
Stop before: the Convex login (access), any deployment (publishes outside), any key in a file (keys; the file with the keys is .env.local, never committed).
Done when http://localhost:3000 (or the port you chose, if 3000 is taken) answers with the project page, /admin with the password from .env.local shows the journal with the first decision, the check is green, no placeholders in square brackets are left in main/AGENTS.md, main/docs/README.md and main/docs/journal.md (links do not count), _wt/ exists and the two pointer files are here. Report the two addresses, the check result, the commit, where the admin password lives (the file and the line, never the value), and the one line about Convex.paste into your agent
The latest version of this prompt always lives in the starter README.
At some point the agent will stop and ask you to start the database with npx convex dev. This is the only place where I did anything by hand:
main folder and run npx convex dev.Keep this terminal open: while the command is running, the database accepts leads.
The second prompt describes the business: four questions, prices and a lead form. The agent first writes the description to docs/README.md as a spec, then builds the quiz from it.
New task. Build a quiz website for Sparkle Clean, a home cleaning company in Austin, Texas. Follow AGENTS.md: first write this description into docs/README.md as the project spec, then build from it.
What the customer sees, phone first:
- The home page / is a four-question quiz, one question per screen, with a Next button and a progress bar:
1. Type of cleaning: standard β $0.12 per sq ft, deep β $0.20 per sq ft, move-out β $0.28 per sq ft.
2. Home size in sq ft: a slider from 400 to 3,000.
3. Windows to clean: 0 to 10, $6 per window.
4. Extras: inside the fridge $35, inside the oven $35, inside the cabinets $45 β any number of them or none.
- After the fourth question: a big price with a line-by-line breakdown, a Name field, a Phone field and a "Book my cleaning" button.
- After sending: "Request received. We'll call you within 15 minutes."
Tech:
- The request is saved in Convex: a requests table (time, quiz answers, price, name, phone, status "new") and a "create request" function. The server recalculates the price from the same prices, not only the page.
- All prices live in one code file; docs/README.md holds a table of the same prices. When I change a price in the description, you update that file.
- Light background, big buttons, no images, no external fonts. All texts in English.
- Test: "deep, 1,200 sq ft, 4 windows, fridge" costs $299 (240 + 24 + 35).
- The site must open from a phone on the same Wi-Fi network.
Done when I go through the quiz on my phone, see the price, send a request and it is stored in Convex, and the check is green. In the report: the address for the phone, what was done, where you stopped.paste into your agent
The prices in the prompt are placeholders, set your own. At the end, the agent shows an address for your phone: open it on the phone and take the quiz as a client.
The third prompt builds the admin panel: today's numbers, a leads table and statuses from "new" to "done".
New task. Build the Sparkle Clean admin β the place where quiz requests arrive. First add it to docs/README.md, then build.
Where: /admin, the password sign-in already exists. Requests go at the top of the page; what /admin already shows (the project description and the journal) stays below.
What the owner sees:
- Three numbers at the top: requests today, their total, how many are still unhandled.
- A requests table, newest first: time, name, phone (tap to call from a phone), cleaning type, size, windows, extras, price, status.
- The status changes with buttons in the row: new β called β booked β done, or declined. A status filter above the table.
- A new request shows up in the table by itself within a couple of seconds, no reload: send the quiz from the phone and the row is already on screen.
- Works on a phone too: on a narrow screen each request row becomes a card.
Tech:
- The same requests table in Convex; functions "list requests", "today's numbers" and "change status".
- Phone numbers are personal data: the list is served only after the admin sign-in. The admin Convex functions check a secret key from an environment variable; the browser never sees that key.
- "Today" is counted in Austin time.
- Test: a status change is saved, and today's numbers count only today's requests.
Done when I send a request from my phone, watch it appear on /admin by itself, change its status, and the check is green. In the report: the admin address, what was done, where you stopped.paste into your agent
Open /admin on your computer and sign in with the password from the .env.local file. Submit a lead from your phone: a new row appears in the table on its own, no reload needed.
Free models sometimes respond slowly, hit their limit or disappear. That's no reason to quit: in OpenCode, type /models again, pick another free model and tell the agent "continue". The spec and the log live in the project folder, so the new model picks up right where the last one left off.
Which free model is responding right now, you can see in the live table of free AI models for coding. Every two hours we send the free models "hi" and note which ones replied.
If OpenCode's free models aren't enough, connect OpenRouter. It has its own list of free models marked :free and a limit of about 50 requests a day.
opencode auth login, choose OpenRouter and paste the key./models and pick a model marked :free.npx convex dev command must be running in the second terminal.