Build an App

SkillProductivity

Lets your agent build a working web app, like a tracker, dashboard, or booking tool, from a short request.

Then ask your AI: use the Build an App skill

Details

Instructions available. Your AI can read the instructions. Execution depends on the setup they require.

Add it as a step in a pipeline you build on Ahel, or connect Ahel to Claude, ChatGPT, Cursor, Claude Code or Codex and ask your AI to use it.

About this skill

Fast end-to-end workflow for turning this starter into a working domain app from a short or vague request. Use when asked to build, make, create, or prototype an app, tool, tracker, dashboard, booking, inventory, or CRM system with Agent-Native. Read before the first edit.

What this skill tells your AI

The instructions your AI receives, as published by builderio/agent-native in .agents/skills/build-an-app/SKILL.md and read by Ahel’s review.

Done means one useful domain workflow with a designed route, shared UI/agent actions, correct access, agent context, and a smoke run that proves its key rule. Finish a narrow slice before expanding it.

This skill edits source files. If this session cannot write files (a deployed app's runtime agent), say the change needs a code session and stop.

The four-area parity rule from adding-a-feature (UI, action, instructions, application state) applies to everything you add. This skill supplies the order and decisions; use the edit-point reference for the scaffold you have.

1. Orient

Read AGENTS.md, this skill, and only the files you will edit. Skip broad repo surveys, README.md, and DESIGN.md. Use the bundled docs/source commands in AGENTS.md only to answer a concrete API question; do not web-research framework APIs.

2. Decide

Choose these six things before writing code:

  1. Slice: the one workflow that proves the app. For a booking app: create a booking, reject an overlap, cancel, weekly grid per room. Name what you are deferring (recurrence, notifications, roles, integrations) unless requested.
  2. Who sees each table: pick one row of the access table in references/data-and-access.md: private, team-visible, shared on invite, or a child that inherits its parent's access. Making a team resource private per user is the usual wrong turn. Everyone then sees an empty list, and no conflict check can see anyone else's rows. Give each table owner_email and/or org_id, directly or via ownableColumns(); org_id alone passes [db-tool-scoping]. Doctor checks the schema marker, not query authorization; never denylist or opt out rooms or bookings.
  3. Invariants: rules that must hold under concurrent requests (no overlap, stock never negative, unique slug). Enforce them in a write action inside a transaction or with a database constraint; the UI alone is insufficient.
  4. Actions: list-<things>, create-<thing>, a single update-<thing> patch per resource, and the domain verbs (cancel-booking). Fewer, orthogonal tools make the agent choose better.
  5. Route: give the domain its own route (for example, /rooms). Preserve existing routes and set app.homePath so sign-in lands on the domain when that is the requested home screen.
  6. Agent context: URL ids and filters (for example roomId, week) that belong in navigation.

3. Build: write everything, then verify once

In a generated app, read package.json at ["agent-native"].scaffold.template and choose the matching reference:

  • chat: references/edit-points-chat.md
  • default: references/edit-points-default.md
  • workspace root: use adding-workspace-apps to create an app, then follow the new app's own AGENTS.md and scaffold reference.
  • another domain template: use its AGENTS.md and adding-a-feature; do not apply Chat or default shell instructions to it.

If metadata is absent, inspect the actual route, layout, plugin, and navigation files before choosing an edit point. Do not infer a shell from the app name.

Write the UI, actions, instructions, and state before running the verification steps below. Do not leave placeholder or comment-only screens.

references/rooms-example.md gives a booking slice to adapt. It was verified against the Chat starter; use the selected edit-point reference for the shell. Use frontend-design for a deliberate visual direction and finish the requested screens and interactions.

Data

  • Use PostgreSQL only, and keep migrations additive: new columns are nullable or have a default, and nothing is dropped or renamed. Never edit an applied entry; append a new named one.
  • Set ownerEmail from ctx.userEmail and orgId from ctx.orgId on inserts where those columns exist. Never rely on a development identity fallback.
  • Ids are text from crypto.randomUUID().
  • Bound every list (a time range or .limit()), select only the columns it renders, and index the columns it filters and sorts on.
  • Store normalized UTC ISO timestamps; collect/display local times in the user's timezone. Validate offsets at the action boundary before parsing.

The access table, the concurrency pattern, and the security rules are in references/data-and-access.md.

Actions

  • schema is zod, with .describe() on every field stating its units and format (for example, "ISO 8601 date-time with offset").
  • Reads use http: { method: "GET" } and readOnly: true. Leave writes on the default POST so useActionMutation needs no method.
  • Reject with fail(message, { errorCode, statusCode }) from @agent-native/core/action: 409 for a conflict, 404 for not found, 403 for forbidden. A returned { error } is counted as success everywhere, and a bare throw reaches the browser as a generic 500.
  • Return plain objects that include the id. Keep LLM calls out of actions; AI-shaped work belongs in the agent chat.
  • When the request is batch-shaped ("add these 20"), write one action that takes an array and writes it in one transaction.

UI

  • Load and write data with useActionQuery and useActionMutation from @agent-native/core/client/hooks. No fetch, and no /api/* routes for app data. Those routes are only for uploads, webhooks, OAuth callbacks, and streaming.
  • While data loads, show a Skeleton in the shape of the final layout, not a spinner or "Loading…".
  • Writes should feel instant. In onMutate, update ["action", "<name>", params] with setQueryData; in onError, roll back and show toast.error(actionErrorMessage(error) ?? "<fallback>").
  • Import controls from @/components/ui/*. When one is missing (dialog, select, skeleton, alert-dialog), add a one-line re-export like button.tsx: export * from "@agent-native/toolkit/ui/dialog";.
  • Use Tabler icons only. Confirm with AlertDialog; never use window.confirm, alert, or prompt.
  • Don't add a page title that repeats the nav item, a subtitle under a title, or a stats strip. The data is the page. For visual direction, see frontend-design.
  • Follow the app's existing localization pattern for user-facing copy.
  • Add a pure link builder with buildDeepLink() to actions that create or list navigable resources; see adding-a-feature for the shape.

Agent instructions

Append one section of about 1,500 characters at most to AGENTS.md. The runtime prompt cuts the file off at 6,000 characters. Cover:

  • a one-line purpose with the routes
  • the invariants, and how to report a rejection
  • time and format conventions
  • the new navigation fields
  • an action table with unpadded cells

Keep the action table aligned with the scaffold's primary tool list when one exists; other actions should remain discoverable through tool-search. Leave unrelated AGENTS.md sections, README, and DESIGN.md as they are.

Real-time

Agent writes need no wiring, because the chat run refreshes action queries after each tool. Add useDbSync({ realtime: { reason } }) to a page only when other people change its data while it is open and the user must see the change before refreshing. A booking grid doesn't qualify: the server rejects bookings made from a stale view. See real-time-sync.

4. Verify, once, in this order

  1. Start pnpm exec agent-native dev in the background, without --open, and wait for the ready URL. Startup regenerates .generated/action-types.d.ts and the actions registry, so a typecheck run before this is meaningless. On loopback, automatic dev sign-in works when the local database has no real users. If the sign-in page appears, use an existing local account or switch to a separate disposable local database; do not create accounts or set AUTH_DISABLED.

  2. Run pnpm typecheck, then pnpm agent-native:doctor. Plain pnpm doctor runs pnpm's own command and exits 0 without running Agent-Native Doctor. Fix every finding, then rerun only what failed.

  3. Check the agent path. Call a guarded write twice with the same input via pnpm action <name> --key value. The second call must fail with the expected message; read back the saved state through the matching list/get action. The CLI prints the failure message, not its structured errorCode, so also verify any code-specific behavior in the action contract or browser.

  4. Run one browser smoke on the domain route:

    • create a record
    • trigger the rejection: a toast appears and nothing is written
    • cancel or undo, repeat the previously rejected write, and confirm it succeeds

    Inspect console and network failures. Fix errors from the new route or actions and identify existing scaffold messages separately. The only expected 4xx/5xx is the rejection being tested. Capture the finished workflow.

  5. Stop the dev server and close the tab.

Skip the production build, unrelated test suites, and README or DESIGN rewrites. To triage failures, see references/verification.md.

5. Report

State what works and the proof, what you deferred, and anything the user still has to do (for example, set DATABASE_URL before deploying). Never report a check you didn't run.

Related

  • adding-a-feature: the parity checklist for each later feature in this app.
  • frontend-design, actions, sharing, real-time-sync, and security: go deeper on one area when the references aren't enough.

Signals

GitHub stars
7k
Forks
645
Last commit
Oct 2026
Advanced
Item type
skill
Key
build-an-app
Source
github.com/builderio/agent-native