ShipAgent

Add a tool page

Ask the agent for a new paid AI feature - what to tell it, which files it creates, and how to test the sign-in wall, the credit wall, a run and a refund.

Outcome: a public page at /<tool> in every language. A signed-in user presses generate, the route charges a fixed number of credits, calls the AI provider, and returns the result. A failed call gives the credits back. The agent's checks print:

✓ app/api/<tool>/route.ts charges <N> credits per call

and pnpm lint and pnpm test pass.

The agent follows skills/add-tool-page/SKILL.md. It copies the admin-only demo under /ai-demo and turns the copy into a product page.

Before you start

  • The AI provider answers: the AI integration passed.
  • Users can sign in, and the database works.
  • Your test user has credits. Every new account gets 30 (signupBonusCredits in config/credits.ts); more come from a plan in config/pricing.ts.

What to tell the agent

Give it five things. The agent asks for any you leave out, one at a time.

Add a tool page.
- Name and URL: Background Remover, /bg-remover
- Input: one uploaded photo (PNG, JPG or WebP)
- Output: the same photo with a transparent background, as a PNG to download
- Provider and model: fal, <model id from config/ai-models.ts or the provider's docs>
- Price: 5 credits per run
  • Name and URL. The slug is lowercase with dashes. It becomes the page path and the name of the files.
  • Input. A prompt, an image, both, or something else. Say what a user must provide.
  • Output. An image, a video, a text. Say whether the user downloads it.
  • Provider and model. A model listed in config/ai-models.ts, or one the provider documents. If users may choose between models, say so and give a price per model.
  • Price in credits. One number per run, or one per model. It lives in one constant (PRICE) at the top of the route file.

A provider that answers within one request (chat, most image models) gives a sync tool. A provider that works for minutes and calls back (video) gives an async tool with a status route; it also needs the webhook address and Redis from the AI setup.

What you get

File What it is
app/[locale]/(basic-layout)/<tool>/page.tsx the page, with the site header and footer
app/api/<tool>/route.ts the API route: sign-in check, credit charge, provider call, refund on failure
app/api/<tool>/status/route.ts async tools only: lets the page poll for the result, only for the user who started it
components/<tool>/<Tool>Page.tsx the form and the result, with the sign-in dialog and the "not enough credits" message
i18n/messages/<locale>/<Tool>.json the page text, one file per language, each written in that language

It also edits: i18n/request.ts (registers the text file), i18n/messages/<locale>/common.json (header and footer links), app/sitemap.ts (the new path in every language), and for async tools lib/ai/video.ts and lib/ai/task-store.ts (the refund when a job fails later).

The /ai-demo pages stay admin-only. Your tool does not reuse them.

How to test it

Run the site locally (pnpm dev, see Run locally) and go through this list. The agent runs the same list; it is short enough to do yourself once.

Do Expect
Open /<tool>, then /de/<tool>, /ja/<tool> and the other languages the page loads, the title is translated, the link is in the header and footer
Login wall: signed out, press generate the sign-in dialog opens (or you go to /login?next=/<tool>, depending on NEXT_PUBLIC_LOGIN_MODE); no credits move
A run: sign in, generate once the result appears; the balance in the sidebar drops by the price, and Dashboard → Credit History shows the spend with the note <tool>
Credit wall: use an account with fewer credits than the price (or ask the agent to set the price to 999999 for one try) a "not enough credits" message with a button to the pricing page; the balance does not change
A failure and its refund: ask the agent to point the route at a model id that does not exist, then generate an error message with the provider's own words; Credit History shows the charge and a refund, and the balance is back where it was
Async tools: one real job the page polls until the result appears; a failed job is refunded when the provider reports the failure
Open /sitemap.xml it lists /<tool> for every language

Ask the agent to undo the test changes (the price, the model id) when you are done.

No limits of our own

The site does not refuse users with checks of its own. The provider decides what it generates.

  • No prompt length cap, image size cap, file format allow-list or content filter added by the site. The demo carries a few (a 5000-character prompt limit, a 5 MB image limit); the agent drops them when it copies the demo, or uses the limits the provider documents.
  • When the provider refuses a request, the user sees the provider's reason and gets the credits back.
  • The provider's own safety system stays on. The one block the site may add itself is sexual content involving minors.
  • Limits that are not about content stay: credits, rate limits, and checks that an uploaded file really is the type it claims.

If you want a different rule for your product, say so explicitly when you ask for the tool.

When it fails

What you see Cause Fix
✗ … never calls refundSpentCredits from scripts/check-ai.ts the route charges but does not refund on failure the agent adds the refund to every failure path
A ✗ line naming a route that generates without charging the route calls a provider but never calls consumeCredits the agent adds the charge, or makes the route admin-only if that was the intent
Generate does nothing for signed-out users the client does not handle the 401 answer the agent adds the sign-in dialog handling
Every run fails with an authentication error from the provider the provider key is missing or wrong in this environment rerun the AI integration check; in production, the key must be in the deployed env
A page in one language shows English text that language's <Tool>.json is missing a key; missing keys fall back to English ask the agent to translate the missing keys
Async job never finishes locally the provider cannot reach your machine the AI setup's tunnel and WEBHOOK_BASE_URL must be running