Communitygithub.com

BennyKok/omg.dev

Build and deliver a new website, web app, API, or Expo app created through omg.dev New Project or Quick Chat. Use for initial product creation and first deployment, not routine maintenance of an established project.

What is omg.dev?

omg.dev is a Claude Code agent skill that build and deliver a new website, web app, API, or Expo app created through omg.dev New Project or Quick Chat. Use for initial product creation and first deployment, not routine maintenance of an established project.

Works with✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/BennyKok/omg.dev/tree/HEAD/agents/skills/omg-app-builder

Ask in your favorite AI

Open a new chat with this agent skill pre-loaded.

Documentation

Build and deliver an omg.dev app

Turn the user's product description into a working, verified result. Keep setup work with the agent. Ask only about product choices that materially change the result.

Ask one question per question call, and put at most one decision in it. A second question in the same call is not shown to the user on every client. If the user has not said what the app should do, ask that in plain words and wait for the answer. Do not build a placeholder product in its place. If a question times out, ask again once in a normal message and end the turn.

Establish the project

  • In Quick Chat, call omg_create_project once with a short descriptive name. Set template to expo for an Expo or universal mobile app. The Expo template already contains a working todo flow with data stored on the phone, Expo Router, Liquid Glass, Lucide icons, web support, and EAS configuration. Use the returned repo.cwd for every command, edit, check, and deployment. Inspect omg_list_repos before retrying an uncertain create call.
  • In a New Project session, the current directory is already the project. It contains Git, a README, and this skill. Do not create a second project.
  • Preserve .git. Do not overwrite an existing folder or move the current Quick Chat into the new project.

Choose the delivery path

  • For a website, web app, or API, use the supported omg.dev runtime unless the user requests another platform. In a blank project, scaffold the official omg.dev React template before writing product code. The current directory already contains Git and omg.dev instructions, so generate into a temporary directory inside it, copy the scaffold into the project, and remove only that temporary directory:

    scaffold="$(mktemp -d .omg-web-scaffold-XXXXXX)"
    bunx --bun create-omg@latest "$scaffold" --template react-ts --no-install
    cp -a "$scaffold"/. .
    rm -rf "$scaffold"
    bun install
    

    This template supplies @omg-dev/vite-plugin, vibes-build, server functions, database support, and analytics. Do not substitute create-vite or a hand-written Vite scaffold. Read https://docs.omg.dev/llms.txt and only the relevant parts of https://docs.omg.dev/llms-full.txt before choosing additional packages or API contracts.

  • For a game (racing, arcade, puzzle, kids' learning, word, 3D), create the project with template: "web-game" and read .agents/skills/omg-web-game-builder/SKILL.md instead of designing an engine. Its step 1 comes before any code edit: bun run dev in the background on port 5174, then omg_expose_port with port 5174 and without expoGo, then tell the user the preview is ready in the card below. The Expo preview steps in this skill (port 8081, expoGo: true) do not apply to a web game. Use template: "expo-game" and .agents/skills/omg-game-builder/SKILL.md only when the user asks for a game to install on iPhone or Android, or names Expo or a native app.

  • For an Expo app, start from the managed expo template and follow "Expo app: the fast path" below. Adapt its screens and its data store instead of rebuilding configuration. Keep secrets in hosted server routes. A production native build needs any server deployed at a secure origin; do not treat a sandbox tunnel as production hosting.

  • Use one live Metro server for Expo Web and Expo Go in a Cloud Computer. Expose its port through omg_expose_port; do not assume a global Expo CLI, Xcode, a simulator, or machine-specific tools exist.

  • When the app is compatible with Expo Go, omg_expose_port puts the exps:// Expo Go link on a preview card in the omg.dev app. Tell the user the preview is ready and to open it from the card below. Do not paste the raw exps:// URL in your reply; the card already shows it. Paste it only where the user cannot see the card, such as a reply sent with omg_send_to_origin to a messaging channel, or when the user asks for the link. Do not use Expo tunnel, LAN exposure, exp.direct, or ngrok. If Expo Go cannot load a required native module, use a development build when authorized or report that limit clearly.

  • TestFlight, App Store submission, paid services, domains, and third-party production accounts are separate delivery actions. Do them only when the user requests them and the required account is available. TestFlight needs the user's Expo account and paid Apple Developer account, but an EAS cloud build does not need Xcode or a Mac in the sandbox. Use supported login flows and never ask for passwords or tokens in chat.

Expo app: the fast path

A new user is waiting. The first screen must reach their phone within about 3 minutes, before any feature work. Follow these steps in order:

  1. omg_create_project with template: "expo". If node_modules is missing, run bun install once.
  2. Preview before you write any code. Call omg_expose_port with port 8081 and expoGo: true. Then run bash scripts/start-expo-preview.sh <expoGo.proxyUrl> 8081 from the project directory, with a 240000 ms shell timeout. When it prints Sandbox proxy answers: HTTP 200, present the web preview first: "Your app is ready. The card below shows it running, and it works on any device." Then mention the phone in one sentence: "To try it on your own phone, tap Your phone on the card." When the omg_expose_port result has simulatorAvailable: true, add one more sentence: "You can also try it on an iPhone right in the card: tap Simulator." Do not promise the simulator when that field is false or missing. Do not paste expoGo.url; the card shows it. Metro reloads the app on every save, so the preview follows your edits from here on.
  3. Write the screens, in a few larger edits rather than one small edit per turn. Replace the template home screen in src/app/index.tsx; do not add a second index route.
  4. Run bun run typecheck and fix what it reports. It also checks the routes and fails on a second home screen or a root app/ folder. Then run bun run check:ui, open its PNGs, and fix what is wrong (see Build step 4). Do not write automated tests unless the user asks.
  5. Deploy once with omg_deploy (see below). The data stays on the phone, so this is a static web build.
  6. Finish with omg_ship.

Do not browse node_modules, fetch documentation, or read the template file by file before the first preview: the summary below is enough. Do not write your own browser test loops or screenshot scripts: bun run check:ui does that. If you cannot view images, read its text report only.

The template:

  • src/app/index.tsx: the home screen, and the only file for /. Replace its content with your home screen. Keep every screen in src/app/: Expo Router ignores a root app/ folder while src/app exists. For tabs, move the home screen to src/app/(tabs)/index.tsx and delete src/app/index.tsx, because both answer / and the old one wins. The template file has a header, an input with an add button, a list with toggle and delete, an error card, and a reload button. Styles are in one StyleSheet at the bottom; the theme constants are at the top (INK, CORAL, BACKGROUND, SURFACE, SURFACE_BORDER).
  • src/app/_layout.tsx: an Expo Router stack with no header, inside KeyboardProvider. A new screen is a new file in src/app/ (stats.tsx is /stats). Navigate with router.push("/stats") or <Link href="/stats"> from expo-router.
  • src/lib/tasks.ts: the data, stored on the phone with AsyncStorage (localStorage on the web). tasksApi.list(), create(title), update(id, patch), remove(id). Keep this file. For each new kind of item, copy its shape with its own storage key.
  • Icons: lucide-react-native, for example <Plus color={CORAL} size={22} />. Glass surfaces: GlassView from expo-glass-effect.
  • Colours: set every theme constant from the design before you style screens. Cards, rows and sheets use the design's own surface colour. The template's translucent white SURFACE suits only its light starter; left on a dark or coloured design, every card comes out white. Use GlassView only where the design shows frosted glass. On Android it draws no glass, so give it a background colour.
  • Safe areas: wrap each screen in SafeAreaView from react-native-safe-area-context with edges={["top", "bottom"]}, or use useSafeAreaInsets(). Never import SafeAreaView from react-native: it is iOS-only, so on Android the header goes under the status bar and the tab bar under the gesture bar.
  • Keyboard: put screens with a text input in KeyboardAwareScrollView from src/lib/keyboard (bottomOffset={24}, keyboardShouldPersistTaps="handled"), as the template does. For a field pinned to the bottom, use KeyboardStickyView from the same file. Import them from src/lib/keyboard, never from react-native-keyboard-controller directly: its .web.tsx twin keeps the static web export working. Do not use React Native's KeyboardAvoidingView: on iOS the screen moves after the keyboard, slowly.

Data: store it on the phone by default, with the tasks.ts pattern. Use hosted data only when the user asks for accounts or data shared between people. Then add sign-in with @omg-dev/sdk and .scoped("user") collections in schema.ts. Never call /api/... with a raw fetch: a hosted collection answers an anonymous phone with 401 Authentication required.

For hosted data, install the latest @omg-dev/sdk and deploy the backend before testing its shared data in Metro. Keep .omg/project.json in the project directory. The hosted web preview uses that registered app identity and signs its owner in through the SDK. Use <VibesAuthProvider> and SDK requests. Do not add a separate preview login, forward dashboard cookies, or replace the user's app permissions. A physical phone still needs the app's normal sign-in and the deployed backend origin.

Metro rules: use only ports 8081 to 8099, because Expo Go links work only in that range. Start Metro only with the script; npx and ss are not installed on every Computer.

Building blocks (Expo)

The template ships tested helpers in src/kit/. Import them; do not rewrite them. They work on iOS, Android and web. Each file starts with a comment that shows its use.

  • overlay.tsx: useConfirm() and useSheet(). <OverlayProvider> is already in _layout.tsx. Use if (await confirm({ title, destructive: true })) for every delete or confirm. Alert.alert with buttons does nothing on web. Use useSheet().open(node) for sheets and forms. Do not stack Modal components: the second one draws behind the first on web.
  • ActionRow.tsx: a pressable row with a delete or other action beside it. A button nested inside a pressable row also fires the row on web.
  • image.ts: await toPersistentImage(pickerAsset). Call the picker with base64: true, quality: 0.5 and store the returned string. A picker URI on web is a blob: URL that is gone after a reload.
  • use-timer.ts: usePersistedTimer(key, durationMs) with start, pause, resume, reset, and formatClock. It stores the end time, so a reload does not reset it. Use it for every timer or countdown.
  • use-viewport.ts: useViewport() and useIsWide(). In the static web export useWindowDimensions() can give width 0 on first render. Prefer flex and maxWidth; use the hook only when you need a number.

Shared data between people (a shared list, a trade, a group): use the hosted @omg-dev/sdk path in "Data" above. Do not write a local server. If a local server is unavoidable, answer OPTIONS and send these headers on every response: Access-Control-Allow-Origin with the request Origin (or *), Access-Control-Allow-Methods: GET,POST,PUT,PATCH,DELETE,OPTIONS, and Access-Control-Allow-Headers: content-type,authorization. A missing PUT or DELETE fails only in the browser.

Build

  1. Convert the request into a small observable done condition. Make reasonable visual and technical choices when the user did not specify them.
  2. Scaffold inside the project directory. For a managed Expo template, install its pinned dependencies and preserve .omg/template.json; do not replace it with another starter. For a blank project, install the current documented dependencies and configuration. Do not install a retired omg.dev CLI or ask the user for infrastructure tokens; runtime tools provide the Cloud credential.
  3. Implement the real path. If the product needs shared data, authentication, uploads, or server work, use the documented hosted APIs and verify persistence and access control. Do not replace requested shared behavior with mock data or browser-only storage.
  4. Do not write automated tests unless the user asks for them. Verify with the self-check instead: run bun run typecheck, then bun run check:ui. The check builds the web export, starts the app's server or backend script if there is one, opens every route at 390x844, and saves one PNG per route in .omg/ui-check/. It reports blank pages, error boundaries, console errors, failed requests, a crashed server, and buttons or links that another element covers (for example an "Add" button under a tab bar). Then open each PNG with the image viewer and compare it with the request and any design references. Fix what is wrong and run the check again until the report is clean. State which surface you checked; a web run is not proof of native behavior.
  5. Update the README with the product purpose, local run command, architecture, and delivery notes. Commit the finished source locally so later omg.dev sessions start from a complete baseline.

Sign-in for a hosted web app

omg.dev owns sign-in for apps at <slug>.omgs.app. Never build a sign-in form, a password field, or your own session store.

  • Private (the default): every visitor signs in to omg.dev before the app opens. The app needs no sign-in UI.
  • Public with sign-in: use this when the user wants a landing page for visitors. Set the app to public with omg_app_visibility. Show the landing page when useAuth().user from @omg-dev/sdk is empty, with a plain link to https://app.omg.dev/access?return=<encoded current URL>. The link returns to the same page signed in. A person already signed in to omg.dev returns at once with no form. Then useAuth().user has id, email, and name, and .scoped("user") collections hold only that person's data. After one Sign in, later visits sign the person in automatically before the page loads. Do not add redirects to the sign-in page or a "remember me" option.
  • Sign out only with useAuth().signOut(). Any other way, such as clearing app state or reloading, signs the person back in on the next visit.
  • On a custom domain there is no automatic sign-in. Keep the Sign in link.
  • Wrap the app in <VibesAuthProvider>. Do not put <VibesAuthGuard> around the landing page, and do not run user-scoped queries on it.
  • Verify the signed-out landing page and the Sign in link on the deployed URL. You cannot sign in as the user. Say that the signed-in path was not checked by you.

Deploy and prove it

  • For a new website, web app, or API, a working hosted preview is the default result unless the user asks for local-only work or publication needs new authority.
  • If the script exits non-zero, read the log tail it prints, fix the cause, and run it again. Do not retry with other flags such as --host 0.0.0.0.
  • The preview card has levels. "Web" (Expo Web from the same Metro server) is the default and shows the app inline on any device, with no account. "Simulator" (only when simulatorAvailable is true) streams the app running in Expo Go on an iPhone simulator into the card; it needs no Expo account and no install. "Your phone" opens the app in Expo Go. Present the web preview first, then mention the phone. Give the user expoGo.url, which starts with exps://, only where they cannot see the card.
  • Expo Go on a physical iPhone opens a dev server only when Expo CLI on the Computer is signed in to an Expo account and Expo Go is signed in to the same account. Otherwise it shows "You need to be signed in to Expo Go and Expo CLI". The proxy, a tunnel, and --offline do not change this; Android, the iOS Simulator, and Expo Web are not affected. The script prints WARNING: Expo CLI is not signed in or the account name. After the warning, still present the web preview first. When the user wants the app on their iPhone, offer it in these words: "Create a free Expo account". Your final message must also carry that offer while Expo CLI is not signed in: end it with "To open it on your iPhone, Create a free Expo account: tap "Your phone" on the preview card, then "Create free account" (or "I have one")." Never end with "try it on your phone via Expo Go" or a QR code alone, because Expo Go on an iPhone cannot open the app until the user has an account. Tell them to tap "Your phone" on the preview card, then "Create free account", or "I have one" if they already have an Expo account. "Create free account" opens the Computer view with expo.dev/signup (Google, Apple, or email). The user creates the account there, and the card then continues into the Expo CLI sign-in by itself. "I have one" opens the expo.dev sign-in page. After that the card shows three steps: get Expo Go, sign in to Expo Go as the account it names, and open in Expo Go. Metro restarts by itself when it must. Expo's terms forbid automated sign-ups: never create, fill in, or submit an Expo account form for the user. Do not sign in for the user and never ask for a password or token in chat. If the user asks you to start it instead, run ./node_modules/.bin/expo login --browser in the project: the page opens in the Computer browser, and the user signs in there.
  • A Computer that sleeps stops Metro. When the user asks to restart the preview, or the preview card says it stopped, call omg_expose_port again with expoGo: true and run the script with the new proxy URL.
  • Use only the URLs returned by omg_expose_port. Do not also try LAN addresses, localhost URLs, exp.direct, ngrok, or Expo's tunnel. Do not start competing development servers.
  • If omg_expose_port fails, confirm that the server is still running, is bound to 0.0.0.0, and answers on the same port. Report the exact tool error if it still fails. Do not silently switch the web preview to another exposure method.
  • If omg_expose_port is unavailable or the session is on a local computer, report that limit. Do not invent a public URL, expose credentials, or depend on this repository's simulator, SSH hosts, filesystem layout, or globally installed tools.
  • The Expo Go capability link is short-lived. If it expires, restart the preview the same way.
  • For a website, use omg_deploy when the user wants a durable hosted deployment. It stays private to omg.dev users. Make it public only when the user wants that or asks for a landing page for visitors (see "Sign-in for a hosted web app"). Publishing the app to TestFlight or the App Store is a separate step the user asks for.
  • Call omg_deploy with cwd set to the project directory and wait: true. It waits at most 45 seconds. If the result has pending: true, call omg_deploy_status with the returned slug until the build is ready or failed. Do not start a second deploy. Reuse .omg/project.json on later deploys. After success, commit this non-secret file with the source so the app keeps one identity.
  • Open the returned URL. Exercise the main user path and any relevant backend operation against the deployed app. A successful build or upload is not proof that the deployment works.
  • On a phone, the live URL shows the omg install bar below the app. The app becomes a card above the bar, so the bar covers nothing. Do not move or hide it. Local previews and bun run check:ui do not show it.
  • After you publish an app's web version, save its store screenshots. Open the live URL at phone size (390x844). Capture its 3 main screens with real-looking sample data, and no empty states, error screens or dev overlays. Put the images in the app's public folder under omg-store/ (served as /omg-store/1.png; png, jpg or webp, flat names). That folder is public even when the app needs sign-in. Publish again. Call omg_app_identity with screenshotUrls set to their live https links, in order. Its store page (omg.dev/apps/) shows them. Capture and publish them again when a later change visibly changes those screens.
  • Show the live result with omg_display_image when a screenshot is useful. Return the actual clickable URL and state which parts were verified.
  • If build, deployment, login, or verification fails, report the exact remaining state. Never describe a local build, artifact, or pending native submission as deployed.

Finish the assigned task with omg_ship only after the requested result is verified.

Individual skills in this repo

This repo contains 4 individual skills — each has its own dedicated page.

Related Skills