Community程式設計與開發github.com

expo/expo-skill-eval

Evaluate Expo skills in this repo end-to-end - trigger accuracy, generated code quality, and runtime screenshots on iOS simulator and Android emulator via Expo Go (web optional). Use when the user wants to eval an Expo skill, test that a skill produces working code, benchmark a skill with device screenshots, or verify a skill's output renders correctly.

expo-skill-eval 是什麼?

expo-skill-eval is a Claude Code agent skill that evaluate Expo skills in this repo end-to-end - trigger accuracy, generated code quality, and runtime screenshots on iOS simulator and Android emulator via Expo Go (web optional). Use when the user wants to eval an Expo skill, test that a skill produces working code, benchmark a skill with device screenshots, or verify a skill's output renders correctly.

相容平台Claude Code~Codex CLI~Cursor
npx skills add https://github.com/expo/skills/tree/main/.claude/skills/expo-skill-eval

Installed? Explore more 程式設計與開發 skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

在你喜歡的 AI 中提問

開啟一個已預先載入此 Agent Skill 的新對話。

說明文件

Expo Skill Eval

Evaluates skills in plugins/expo/skills/ for trigger accuracy, generated code quality, and/or runtime rendering in Expo Go.

Requirements: macOS with Xcode (iOS simulators), Android SDK with at least one AVD, and bun. No other device tooling is assumed.

Workspace root: /private/tmp/expo-skill-eval-<skill-name>/iteration-N/ (e.g. /private/tmp/expo-skill-eval-expo-ui/iteration-4/).

Before starting — clarify scope

Confirm all of the following up front, before any pipeline work — don't skip any (only skip a given item if the request already states that choice). Batch them into AskUserQuestion calls of ≤4 questions each, in this order:

  1. Which skill to eval (if not clear from the request).
  2. Prompts — which prompts drive the eval. Built-in prompts (from the skill's eval cases) are pre-selected all; drop any, add a custom text prompt, or build from an uploaded screenshot (a target UI the skill must reproduce). See Prompts below.
  3. What to verify — one multi-select of three options: Runtime + screenshots / Trigger accuracy / Code checks (no device). See What to verify below.
  4. Expo SDK — latest (default, auto-detected) or a pinned version.
  5. Runner — Expo Go (default) or development build.
  6. Platforms — iOS / Android / web (always offer all three).
  7. Permission flag for claude -p — skip-permissions (default) or accept-edits.
  8. Viewer delivery — local only (default) or publish a shareable Artifact.
  9. If trigger accuracy is selected — confirm the published expo plugin is disabled (or not installed).

Each is detailed below. Items 4–6 (SDK, runner, platforms) fit naturally in one AskUserQuestion call.

If the skill to eval is not clear from the request, list available skills from plugins/expo/skills/ and ask which one to evaluate.

How the skill under test is loaded — two mechanisms, one per phase (don't pick one globally): executor runs reference it by file path (SKILL_PATH = plugins/expo/skills/<skill>/SKILL.md, read explicitly), while the trigger eval loads it as a plugin (--plugin-dir plugins/expo, so the model can auto-select it from its description). Both point at the local, in-repo version — that's what you're evaluating. You do not need any special flag to launch the harness session itself (the harness finds the skill by repo path); the mechanisms apply to the claude -p subprocesses it spawns. See steps 1 and 3 for why each phase differs. One pre-run check (required when the trigger eval is in scope): if the published expo plugin is installed/enabled, disable it (via /plugin) before launching the harness and re-enable after. A single disable is a global-config change that both this session and the spawned claude -p subprocesses inherit. Why it's required for the trigger eval: that phase loads the local skill via --plugin-dir, and a second installed expo collides with it — the model may trigger the published expo:expo-ui, and since detection only sees the tool-call name you'd silently score the published description instead of your local edits (the collision could also just error). The executor / runtime / static phases are not affected — they read the skill under test by its local SKILL_PATH with no --plugin-dir — so a run with no trigger eval can skip the disable. Disabling expo does not disable expo-skill-eval (a standalone project skill, not part of the expo plugin), so the harness stays available.

Surface this to the user as an explicit up-front confirmation — the same way you confirm which skill to eval. When the trigger eval is in scope, ask the user to confirm the published expo plugin is disabled (or not installed) before you start step 1; if it's still enabled, pause and have them disable it via /plugin. Don't run the trigger eval until they confirm — the harness can't reliably detect installed plugins on its own (reading the global plugin config or claude plugin list would prompt), so this is a manual confirmation, not an auto-check.

Pick the prompts — built-in, custom, or a target screenshot. The prompts are the inputs that drive the executor (with-skill and without-skill); they are separate from what you verify. Confirm them with AskUserQuestion (skip if the request already names a prompt):

  • Built-in prompts — representative prompts you generate by reading the skill under test (its SKILL.md + references/) and references/runtime-matrix.md, covering the skill's standard use cases. (If the skill already ships eval cases under evals/evals.json, fold their prompt fields in too — but most skills don't, so you usually derive them.) Pre-select all so the default run exercises the skill's standard cases; let the user deselect any.
  • Custom text prompt — a one-off prompt the user types. Don't spend a dedicated option slot on this: AskUserQuestion auto-adds a "Type something" / Other entry, and anything typed there becomes a custom text case.
  • Build from an uploaded screenshot — the user gives the path to a target screenshot (a UI to reproduce). The executor is told to open it — claude -p reads PNGs with its Read tool — and build an app matching it; the case records the path as reference_image, and grading compares the generated app to that target (step 6). This is the strongest visual test for a UI skill: "build this."

Respect AskUserQuestion's 4-option-per-question cap with this priority (the bug to avoid: the upload option silently dropped once the four slots fill up):

  1. Always reserve a slot for "Build from an uploaded screenshot." It's the whole point of the visual eval and must never be the option that gets dropped.
  2. Don't add an explicit "Custom text prompt" option — the auto "Type something" / Other entry already covers it.
  3. Fill the remaining ≤3 slots with the built-in/representative prompts, pre-selected. If there are more than 3, collapse them into one pre-selected "All built-in prompts (default)" option and offer subset-picking in a short follow-up, so the upload option still fits.

Present it as a multi-select. When "Build from an uploaded screenshot" is picked, ask for the target image path in a follow-up. Each selected prompt (built-in, typed, or image) becomes one eval case (run with-skill and without-skill).

Always confirm what to verify unless the request makes it unambiguous. Present these options and let the user pick one or more (defaults in bold based on the skill's references/runtime-matrix.md entry):

OptionWhat it doesWhen to suggest as default
Runtime + screenshotsFull pipeline: fixture → executor → static gate → run the app on iOS/Android and screenshot it. The runner (Expo Go or dev build) is a separate question — don't name it here.Default for any skill that renders an app screen (the expo-go/dev-build rows in references/runtime-matrix.md). Requires a booted simulator/emulator.
Trigger accuracyRun realistic prompts via claude -p, check whether the skill is read. Measures recall (should-trigger queries only).Always useful as a standalone check.
Code checks (no device)tsc --noEmit + diff-aware lint + expo export, plus the grader checks the generated code against any custom expectations you provide. No device.Default for static-only and n/a skills, and whenever you want to verify code patterns (correct import path, a Host wrapper, …) without running the app.
Present these as ONE multi-select question — "What do you want to verify?" These are grading dimensions (how to judge what gets built) — distinct from the Prompts phase (what to build). The user may pick any combination. When a prompt is an uploaded screenshot (see Prompts), include "Runtime + screenshots" so the harness captures the generated app and the grader can score it against the target.

Read references/runtime-matrix.md to find the skill's default mode before suggesting. If the request already specifies a mode (e.g. "just check if it triggers", "run it on device"), skip the question and proceed.

Pick the Expo SDK version — once, up front. Detect the latest with bash /abs/path/expo-skill-eval/scripts/latest-sdk.sh (it prints the major, e.g. 56; internally it uses bun to run npm view expo dist-tags --json and read the major via JSON.parse/semver, and it's covered by the bash-scripts rule — so don't run the registry query inline yourself, which would prompt). Then confirm with AskUserQuestion: default to that latest SDK, or let the user pin an older one (e.g. to reproduce a version-specific issue). Use the chosen version everywhere the fixture is built — pass it as the <sdk> arg to make-fixture.sh and write it into each eval case's runtime.sdk. If the request already names a version ("eval on SDK 54"), skip detection and use it.

Default to the latest — it stays compatible with the Expo Go that expo start installs on the device. Pinning an SDK older than the device's installed Expo Go makes expo start try to prompt "Install the recommended Expo Go version?"; with no TTY (the snapshot scripts read stdin from /dev/null) it dies with Input is required, but 'npx expo' is in non-interactive mode and every snapshot fails. So only pin an older SDK when you also pre-install a matching Expo Go on the simulator/emulator — otherwise stick with latest.

Pick the runner — Expo Go (default) or a development build. Ask with AskUserQuestion (skip if the request already says which):

  • Expo Go (default) — the snapshot scripts run the app with expo start --ios / expo start --android as-is. Fast (no native compile), and it runs anything Expo Go bundles (including @expo/ui on SDK 56+). Cannot run custom native code (expo-modules, config plugins, native deps not in Expo Go).
  • Development build — the snapshot scripts run expo run:ios / expo run:android instead, compiling a native dev client per fixture. Use this for skills whose output needs custom native code (the cases that would otherwise be static-only). Much slower — expo run prebuilds and natively compiles each fixture (minutes, especially the first), and needs the full iOS/Android build toolchain — so only choose it when the skill actually requires native code. Disk-heavy: each fixture's native build is multi-GB. The snapshot phase runs clean-fixture.sh after each fixture to keep peak usage to ~one build, but still prefer fewer eval cases and a single platform for dev-build runs, and keep a few GB free. clean-fixture.sh removes the per-fixture build output (node_modules, ios, android, .expo, dist, and the fixture's iOS DerivedData) and keeps the app source + git. The lever for dev-build disk is fewer eval cases + one platform — it only reclaims per-fixture build output and never touches shared dependency caches, so nothing gets re-downloaded.

Pass the choice to the snapshot scripts via the EXPO_SKILL_EVAL_RUNNER env var (expo-go default, or dev-build), and reflect it in each eval case's runtime.mode (expo-go or dev-build). See step 5.

Pick the platforms — always ask, regardless of skill. Offer iOS / Android / web (multi-select) with AskUserQuestion; default to iOS + Android, but always present web as an option — don't pre-filter by skill. Web is a valid choice for most skills: @expo/ui's universal components (Host, Row, Column, Button, List, …) render on web, as do expo-dom, NativeWind/Tailwind, API routes, and plain React Native. The only thing that won't show on web is a platform-specific native tree (@expo/ui/swift-ui or @expo/ui/jetpack-compose), which renders blank there — and that blank is itself a useful signal, so it's still the user's call. Web runs via snapshot-web.sh (expo start --web + Playwright/Chromium) regardless of the runner (expo run is native-only; there's no web dev build), and it's the least-exercised path. Write the chosen set into each eval case's runtime.platforms and have run_snapshots.py loop them.

Confirm how claude -p subprocesses run — once, before starting. Ask with AskUserQuestion whether they may run with --dangerously-skip-permissions, then apply the same answer to every subprocess this run (never re-prompt mid-run):

  • Skip permissions (recommended) — pass --dangerously-skip-permissions. Each subprocess runs unattended inside a throwaway fixture under /private/tmp/expo-skill-eval-* and can write files and run setup commands without prompting.
  • Accept edits only — pass --permission-mode acceptEdits instead. Bash/installs are auto-denied (no TTY), so some evals may produce partial output.

A bare claude -p with neither flag can't write files at all. If the request already states a preference ("skip permissions", "don't use the dangerous flag"), skip the question.

Confirm how to deliver the results viewer — once, up front. Publishing to claude.ai is outward-facing, so never do it mid-run by surprise; ask in the same up-front AskUserQuestion (alongside the permission flag):

  • Local only (default)generate_viewer.py writes viewer.html and opens it in the local browser. Nothing leaves the machine.
  • Publish a shareable Artifact — additionally render the viewer to a claude.ai Artifact (a default-private web page the user can share with teammates) at the very end. Only do this if the user opts in here.

If the request already says whether to share/publish, skip the question. See the Viewer section for the publish mechanics.

Eval case schema

You generate the run's eval cases — one per chosen prompt — and write them to <workspace>/iteration-N/evals.json (the viewer reads them from there). Each case extends the standard skill-creator eval-case shape with a runtime block and visual expectations:

{
  "id": 1,
  "prompt": "Build me a settings screen with a dark mode toggle and a list of options",
  "expected_output": "Working Expo Router screen",
  "expectations": [
    "Uses Expo Router file-based routing",
    "TypeScript compiles with no errors"
  ],
  "runtime": {
    "mode": "expo-go",
    "platforms": ["ios", "android"],
    "sdk": "56"
  },
  "visual_expectations": [
    "No red error screen or Expo Go error overlay on any platform",
    "A settings screen with a visible toggle control is rendered"
  ]
}
  • runtime.mode: how the eval runs after the static gate —

    • "expo-go": run in Expo Go (expo start --<platform>) and screenshot. Fast, JS-only. Default.
    • "dev-build": build a native dev client (expo run:<platform>) and screenshot. For skills whose output uses custom native code; much slower (native compile per fixture).
    • "static-only": stop after the static gate — for skills that produce no UI, or when you don't want to run a device at all (CI).

    Consult references/runtime-matrix.md for which repo skills support which mode. (dev-build lets you actually run skills that previously had to be static-only for needing native code.)

  • runtime.platforms: subset of ios, android, web — chosen up front (always offered, not gated on the skill; see Before starting). Defaults to ["ios", "android"].

  • runtime.sdk: Expo SDK major for the fixture app — set it to the version chosen up front (see Before starting — clarify scope). Omit to use the latest template.

  • reference_image (optional — image prompt): absolute path to a target screenshot the skill must reproduce. When set, the executor is told to open it (via its Read tool) and build a matching app, and the grader scores how closely the generated app reproduces it (step 6) on top of the usual expectations. Set in the Prompts phase via "build from an uploaded screenshot."

An image-prompt case is a normal case with reference_image set; enable "Runtime + screenshots" so the harness captures the result to compare against the target:

{
  "prompt": "Build an app whose UI matches the attached reference screenshot.",
  "reference_image": "/abs/path/to/target.png",
  "runtime": { "mode": "expo-go", "platforms": ["ios"], "sdk": "56" },
  "visual_expectations": ["Matches the reference's layout, components, and color treatment"]
}

Pipeline per eval case

Orchestration model — on the main thread you run python3 <orchestrator> and almost nothing else. Every phase is driven by a small Python orchestrator you Write into the workspace and run with python3 /private/tmp/expo-skill-eval-<skill>/<phase>.py (covered by the python3 rule). The orchestrators are the only place the scripts/*.sh files are invoked — always via subprocess.run(["bash", "<scripts>/<name>.sh", …]), which runs as a child of python3 and needs no rule of its own — and the only place parallelism, logging, and directory creation live. So on the main thread you only ever: Write orchestrators, run them with python3, inspect outputs with the Read/Glob/Grep tools, and spawn the grader subagent. Never put a command inside a chained/backgrounded/piped shell construct, and never run ad-hoc mkdir/ls/cat/tail/echo — that is what prompts. (A single standalone bash …/scripts/<name>.sh … is fine for one-off manual debugging, e.g. re-running one flaky snapshot, but the pipeline itself goes through the orchestrators.) Run each orchestrator in the foreground — let the tool call block until it finishes; the orchestrators already parallelize within a phase, so you don't need to overlap phases. Do not shell-background a phase with … & echo "$!" / wait (the &, echo, and wait segments have no rule and prompt). If you genuinely must run a phase while continuing other work, use the Bash tool's run_in_background parameter on a plain python3 <orchestrator> 2>&1 | tee <ws>/…log call — never hand-rolled shell &. Expect exactly one permission prompt at the very start: the first Write into the workspace. allowed-tools can suppress Bash/Read but not Write/Edit, so choose "allow all edits in this directory for the session" on that first prompt — it covers every orchestrator, evals.json, and viewer file for the whole run.

0. Workspace setup

Create the run's directory tree once, with the workspace script — never with ad-hoc mkdir (a raw mkdir prompts: there is no mkdir rule, and a "$WORKSPACE/…" variable can't match a path glob anyway):

bash /abs/path/expo-skill-eval/scripts/make-workspace.sh /private/tmp/expo-skill-eval-<skill> iteration-N <num-evals>

This creates trigger-evals/scratch and iteration-N/eval-<i>/{with_skill,without_skill}/outputs for every eval. It is covered by Bash(bash *expo-skill-eval/scripts/*), and the mkdirs inside run as children of the script (no rule of their ow

Individual skills in this repo

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

expo/add-app-clip

Add an iOS App Clip target to an Expo app. Use when the user mentions App Clip, AASA, apple-app-site-association, appclips, smart app banner, or wants to ship a lightweight iOS Clip invoked from a URL alongside their parent app.

expo/building-native-ui

Complete guide for building beautiful apps with Expo Router. Covers fundamentals, styling, components, navigation, animations, patterns, and native tabs.

expo/eas-update-insights

Check the health of published EAS Updates: crash rates, install/launch counts, unique users, payload size, and the split between embedded and OTA users per channel. Use when the user asks how an update is performing, whether a rollout is healthy, how many users are on the embedded build vs OTA, or wants to gate CI on update health.

expo/expo-api-routes

Guidelines for creating API routes in Expo Router with EAS Hosting

expo/expo-brownfield

Integrate Expo and React Native into an existing native iOS or Android app. Use when the user mentions brownfield, embedding React Native in a native app, AAR/XCFramework, or adding Expo to an existing Kotlin/Swift project. Covers both the isolated approach and the integrated approach.

expo/expo-cicd-workflows

Helps understand and write EAS workflow YAML files for Expo projects. Use this skill when the user asks about CI/CD or workflows in an Expo or EAS context, mentions .eas/workflows/, or wants help with EAS build pipelines or deployment automation.

expo/expo-deployment

Deploying Expo apps to iOS App Store, Android Play Store, web hosting, and API routes

expo/expo-dev-client

Build and distribute Expo development clients locally or via TestFlight

expo/expo-examples

Framework (OSS). Expo's official example projects - the expo/examples repo of ~70 `with-*` integrations (Stripe, Clerk, Supabase, OpenAI, maps, Reanimated, SQLite, Skia, NativeWind, and more). Use when integrating a third-party library or service into an existing Expo app and you want the canonical, version-matched pattern to adapt, or when scaffolding a new project from one with `npx create-expo --example`.

expo/expo-module

Guide for creating and writing Expo native modules and views using the Expo Modules API (Swift, Kotlin, TypeScript). Covers module definition DSL, native views, shared objects, config plugins, lifecycle hooks, autolinking, and type system. Use when building or modifying native modules for Expo.

expo/expo-observe

Use for anything related to EAS Observe — adding `expo-observe` to an Expo project (AppMetricsRoot/ObserveRoot HOC, markInteractive, the useObserve hook, and the Expo Router / React Navigation integrations for per-route metrics), querying via the EAS CLI (`eas observe:metrics-summary`, `observe:metrics`, `observe:routes`, `observe:events`, `observe:versions`), or interpreting the resulting metrics (cold/warm launch, TTR, TTI, navigation cold/warm TTR, update download, and the TTI frameRate params for triaging slow startups).

expo/expo-overview

Framework (OSS). Entry point and router for every Expo or EAS task. Load this skill first — before writing code and before choosing another expo-* / eas-* skill — when the request, PRD, or spec mentions Expo, EAS, Expo Go, or an expo-* package, or the project has an `expo` dependency in `package.json`. Within that gate it also covers app specs and designs to implement (tabs, stacks, maps, lists, navigation, building from a screenshot), and phrasings like 'implement a mobile app', 'make my app look native', 'add navigation', 'fetch some data', 'upgrade my SDK', 'add Expo to my existing native app', 'ship to the App Store', or 'I'm new to Expo, where do I start'. A fully specified request (SDK pinned, libraries named, layout given) still routes through here — the shared setup rules still apply. Do NOT load it when neither signal is present: a bare React Native project with no `expo` dependency is not Expo work. Detects the real goal, routes to the right expo-* / eas-* skill, and owns the shared setup rules.

expo/expo-router

Framework (OSS). Navigation and routing for Expo Router. Covers file-based routes, groups and dynamic routes, folder organization, Link with previews and context menus, native Stack, page titles, modals and form sheets, NativeTabs, headers and toolbars, and header search bars.

expo/expo-tailwind-setup

Set up Tailwind CSS v4 in Expo with react-native-css and NativeWind v5 for universal styling

expo/expo-ui

Framework (OSS). Build native UI with the @expo/ui package: real SwiftUI on iOS and Jetpack Compose on Android. Default to @expo/ui for sheets (BottomSheet), pickers, sliders, toggles, menus, and grouped-form sections — do NOT reach for Reanimated, @gorhom/bottom-sheet, or RN built-in Picker/Switch; use @expo/ui instead. Fall back to RN built-ins only when @expo/ui is missing the component. NOTE: @expo/ui List renders native grouped rows like an iOS Settings screen — it is NOT a virtualized list; use FlatList/FlashList for large datasets. Covers universal components (Host, Column, Row, Button, Text, List, BottomSheet, FieldGroup, Switch, Slider, Picker, Menu), drop-in replacements for RN community libraries, and platform-specific SwiftUI/Jetpack Compose trees. Not for Expo Router navigation, Reanimated, or data fetching.

expo/expo-ui-jetpack-compose

`@expo/ui/jetpack-compose` package lets you use Jetpack Compose Views and modifiers in your app.

expo/expo-ui-swiftui

`@expo/ui/swift-ui` package lets you use SwiftUI Views and modifiers in your app.

expo/expo-upgrade

Framework (OSS). Guidelines for upgrading Expo SDK versions and fixing dependency issues

expo/native-data-fetching

Use when implementing or debugging ANY network request, API call, or data fetching. Covers fetch API, React Query, SWR, error handling, caching, offline support, and Expo Router data loaders (`useLoaderData`).

expo/upgrading-expo

Guidelines for upgrading Expo SDK versions and fixing dependency issues

相關技能