Skip to content
Back to skills

App Variants And Environment

ASecurity

Wires APP_VARIANT builds: per-variant ids, names, icons, schemes, EXPO_PUBLIC_* as public, secrets in EAS/CI, test code out of store bundles. Use when adding a variant, env var, .env or secret. Not for signing (eas-build-and-submit).

  • 3 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 18, 2026
developmenttypescriptgoshellreactnodeexpresstestinggitapi

Works with

  • cli
  • api

Security analysis

A100/100

Pro scans all 19 files and shows the line behind each finding

Scanned October 18, 2026

npx -y skills add zakariaf/ReactNative-Skills --skill app-variants-and-environment --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of App Variants And Environment?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for App Variants And Environment
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/zakariaf-app-variants-and-environment/badge)](https://www.skillsdirectory.com/skills/zakariaf-app-variants-and-environment)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: app-variants-and-environment
description: "Wires APP_VARIANT builds: per-variant ids, names, icons, schemes, EXPO_PUBLIC_* as public, secrets in EAS/CI, test code out of store bundles. Use when adding a variant, env var, .env or secret. Not for signing (eas-build-and-submit)."
---

# App variants and environment

One Expo app builds as three variants, `development` (the dev client), `preview` (internal testing) and `production` (the stores), chosen by `APP_VARIANT`. Each variant installs beside the others and says what it is. Every `EXPO_PUBLIC_*` value is treated as public, secrets stay in EAS or CI, and test-only code never reaches a store bundle. Three checkers prove it: the config per variant, the source and files, and the exported bundle.

## Rules that must hold

1. **One resolver decides the variant.** `app.config.ts` takes it from `resolveAppVariant(process.env)` in `src/config/app-variant.ts`: only `development`, `preview` or `production`; an unknown name throws; `APP_VARIANT` and `EXPO_PUBLIC_APP_VARIANT` must be equal; both unset means development for local work and throws in a production bundle (`NODE_ENV=production`: `expo export`, `eas update`, `expo start --no-dev`) or on EAS Build (`EAS_BUILD=true`). The variant comes from the command, the eas.json profile or the EAS environment, never from a committed env file. Why: the config and the JS bundle are evaluated in different processes, so a typo or a lost variable otherwise ships the wrong app without an error, and Expo CLI loads a committed `.env` into every local `start`, `export` and `prebuild`.
2. **Every variant installs beside the others.** Production keeps the store id; development and preview append `.dev` and `.preview` to the iOS bundle identifier and the Android package, and every id is valid on both platforms. Why: a shared id overwrites the store app and its data on a tester's phone. Services keyed by app id (push, Sign in with Apple, IAP products, universal links, Firebase files) need each id registered or are tested on production-id builds, and ids built on the app id (App Groups, iCloud and keychain groups, extension bundle ids) derive from the variant's id.
3. **Every variant shows what it is.** Its own name (`Tasks (Dev)`, `Tasks (Preview)`, the store name for production), its own icon files on iOS and Android (which exist: production keeps the store icons in `assets/icons/production/`, development and preview get badged copies), its own URL scheme, and the dev client's generated `exp+<slug>` scheme only in development. Why: testers, screenshots and bug reports must name the build; a missing icon file stops prebuild and every EAS Build with ENOENT; with two installed apps on one scheme iOS opens either one (which is undefined) while Android asks the user to choose.
4. **Every EAS build profile names its variant twice and its environment once.** `env.APP_VARIANT` = `env.EXPO_PUBLIC_APP_VARIANT` = `environment` = the variant; only development sets `developmentClient`; extra profiles extend a variant profile; a shared `base` profile the variants extend names no variant, environment or `developmentClient` a child contradicts. The same two variables also exist as plain-text EAS environment variables in each environment, with equal values (EAS CLI warns that the profile's value wins). Why: eas.json `env` reaches builds only; `eas update --environment <name>` reads the EAS environment and nothing else.
5. **`EXPO_PUBLIC_*` is public.** Metro inlines each value into the JS bundle, where anyone with the app can read it, so no `EXPO_PUBLIC_` name or value is a secret: `SECRET`, `TOKEN`, `PASSWORD`, private keys, payment secret keys, and paid or LLM API keys (OpenAI, Anthropic, Gemini, ElevenLabs, Twilio, ...). The app calls a server that holds the key, such as an Expo Router API route. A key that is public by design (a Maps key restricted to the app id, a Stripe publishable key) goes into `publicEnvAllow` with a reason. Why: nothing that ships in the binary can be hidden, minified or bytecode alike, and a leaked LLM key bills the developer's account.
6. **App code reads the environment in one place, the way Metro inlines it.** Only `src/config/` reads `process.env` (except the literal comparison at an inclusion `require()`, rule 9), only as `process.env.EXPO_PUBLIC_NAME` with dot access; the variant comes from `currentAppVariant()`, never from `Constants.expoConfig.extra` or `__DEV__`. Server code is not app code: API routes (`src/app/**/*+api.ts`), server middleware and the helper folders named in `serverDirs` read their secrets from `process.env` at request time, and app code never imports them. Why: brackets, destructuring and non-public names are `undefined` in a release build while Jest still sees them, and a second source of truth drifts.
7. **Secrets live in EAS or CI, never in the repo or the app.** Build-time secrets (`SENTRY_AUTH_TOKEN`, store keys) are EAS variables with secret visibility or CI secrets; runtime secrets for API routes are EAS variables with sensitive visibility (EAS Hosting cannot deploy secret ones); eas.json `env` and committed env files hold none; no secret flows into the app config, whose public part (`extra`, plugin options) is embedded in every build. Why: committed and embedded values are readable for good; rotating the secret is the only cure.
8. **Secret files are git-ignored.** `.gitignore` covers `.env*.local`, `*.p8`, `*.p12`, `*.jks`, `*.keystore`, `*.mobileprovision`, `credentials.json` when a profile uses local credentials, and every key file eas.json points at. Why: one `git add .` publishes a signing key, and keys cannot be unpublished.
9. **Code is left out of a bundle only by a literal comparison at the `require()`** (this skill owns that rule; `debug-menu-and-test-hooks` owns the gate file, `architecture-and-boundaries` the import side). Test-only code sits in one ternary with `process.env.EXPO_PUBLIC_APP_VARIANT === 'production'`, never behind an imported constant, a helper call, `__DEV__`, `NODE_ENV` or an `if` block; any other inclusion flag works the same way (`process.env.EXPO_PUBLIC_STORYBOOK_ENABLED === 'true' ? require(...) : null`). Debug-bundle tooling (Reactotron, a devtools package) is not test-only code: its `__DEV__` gate is right, and other such modules go into `devOnlyRequires`. Why: verified on Expo SDK 57, Metro folds only literals in the same expression, so an imported `IS_TEST_BUILD` or `STORYBOOK_ENABLED` kept its module in the production bundle; `__DEV__` folds but also strips preview builds.
10. **Metro's cache is keyed on the public values.** `metro.config.js` sets `cacheVersion` from every `EXPO_PUBLIC_*` name and value, so it changes with any public value and differs per variant. Why: verified on Expo SDK 57, a production export made after a development export reused the development bundle byte for byte, test-only code included. It bites every `expo export` and every release build on a machine with `CI` set; only `eas update --environment` clears the cache by itself.
11. **The exported bundle proves the strip.** After a change to variants, gates, Metro or variables, export development and production back to back without `--clear` and check both: production holds no test-only sentinel and no secret; development holds every sentinel. Why: the gate, the cache key and the variables only work together, and only the artifact shows it.

## Workflow

1. **Decide the identities.** Read [references/variant-matrix.md](references/variant-matrix.md): the three variants, ids, names, icons, schemes, which services need each id registered, and the moments a variant enters the binary on iOS and Android.
2. **Merge the templates into the app** (line 1 names each repo path; fill `__APP_ID__`, `__APP_NAME__`, `__APP_SLUG__`, `__APP_SCHEME__`). Nothing the app already configures is lost:
   - First save the production config: `mkdir -p dist && APP_VARIANT=production EXPO_PUBLIC_APP_VARIANT=production npx expo config --type public --json | node -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>console.log(JSON.stringify(JSON.parse(s),null,2)))' > dist/config-before.json` (`dist/` is git-ignored).
   - Copy `templates/src/config/app-variant.ts` and its test (the canonical resolver, shared byte for byte with the build, release and ads skills), `templates/src/config/current-app-variant.ts` and its test, and `templates/src/env.d.ts`.
   - Build `app.config.ts` from `templates/app.config.ts` and move every value of the old `app.json` or app config into its returned object: `owner`, `version`, `updates.url`, `runtimeVersion`, `extra.eas.projectId` (the EAS project link), every plugin with its options, `ios.infoPlist` usage strings, `android.permissions`, colours, splash. Only the id, name, scheme, icons and dev-client scheme depend on the variant; an old `extra.appVariant` goes (the app asks `currentAppVariant()`).
   - Move the app's icon files into `assets/icons/production/`: `icon.png`, its Icon Composer folder (`expo.icon` in the SDK 57 template; set `STORE_IOS_ICON` to its name, or to `'icon.png'` when there is none), and the adaptive layers renamed `android-foreground.png`, `android-background.png`, `android-monochrome.png`. Then `node ${CLAUDE_SKILL_DIR}/scripts/make-variant-icons.mjs .` writes badged copies to `assets/icons/development/` and `assets/icons/preview/` (amber band, violet hatched band), so every variant builds; `icons-app-icon-and-splash` may redraw them at the same paths.
   - Metro: copy `templates/metro.config.js` with `templates/scripts/metro/public-env-cache-version.ts` and `compose-metro-config.ts` (each with its test). Its Metro slot composes the optional kits of `scripts/metro/metro-kits.ts` (crash reporting's Sentry config is one; app-startup-and-bootstrap's slot contract), so an old file's wrappers (Sentry, NativeWind, SVG) move into a `MetroKit` there (`baseConfig` for one that builds on Expo's default, else `transform`). An app that keeps its own `metro.config.js` adds `config.cacheVersion = publicEnvCacheVersion(process.env)` on the object it finally exports.
   - `templates/eas.json`: merge its three `build` profiles into the app's eas.json (a shared `base` they extend may stay). When there is no eas.json yet, leave it: `eas-build-and-submit` creates it with these profiles, and until then `check-variants` prints the expected eas.json SKIP.
   - `templates/dot-env.example` to `.env.example`, and add `!.env.example` to `.gitignore` when `.env*` is ignored.
   - `npx expo install expo-dev-client expo-splash-screen` (the config names both plugins; the development profile builds the dev client).
   - Save `dist/config-after.json` the same way and `diff dist/config-before.json dist/config-after.json`: only icon paths, the `expo-dev-client` plugin and an old `extra.appVariant` may change. Then delete `app.json`.
3. **Route every read through `src/config/`.** Replace variant checks with `currentAppVariant()`; give each other public value a typed reader like [examples/environment.ts](examples/environment.ts) and a line in `src/env.d.ts`. Read [references/environment-variables.md](references/environment-variables.md) for inlining, `.env` loading, EAS environments and CI.
4. **Place every secret.** Read [references/secrets.md](references/secrets.md), list the build-time and runtime secrets, move any paid or LLM API call behind an API route, and hand the developer the `eas env:set` commands (secret visibility for build-time secrets, sensitive for API-route secrets, plain text for `APP_VARIANT` and `EXPO_PUBLIC_APP_VARIANT` in all three environments). They run in the developer's Expo account: stop and ask before creating, changing or deleting any EAS variable, and before rotating a leaked key.
5. **Gate test-only code** with the literal comparison and export a sentinel from the test-only entry: [references/stripping-test-code.md](references/stripping-test-code.md). The gate file and the debug registry belong to `debug-menu-and-test-hooks`.
6. **Run the app's gates:** `npm run typecheck`, `npx eslint . --max-warnings 0`, `npx jest src/config scripts/metro --ci --maxWorkers=2`.
7. **Look at each variant:** `APP_VARIANT=preview EXPO_PUBLIC_APP_VARIANT=preview npx expo config --type public` (and development, production), then `APP_VARIANT=preview EXPO_PUBLIC_APP_VARIANT=preview npx expo prebuild --no-install --clean` proves the icons and plugins resolve. Before a local build after switching variants, run that prebuild with the build's two variables: `ios/` and `android/` keep the previous variant until regenerated.
8. **Check:** `node ${CLAUDE_SKILL_DIR}/scripts/check-variants.mjs .` and `node ${CLAUDE_SKILL_DIR}/scripts/check-env.mjs .`. Each `FAIL` line names the file, the rule and the fix; [references/pitfalls-and-tests.md](references/pitfalls-and-tests.md) explains every rule. Fix and rerun until both print `RESULT: PASS`.
9. **Prove the strip** when variants, gates, `metro.config.js` or variables changed: export both variants back to back into `dist/variant-proof/development` and `dist/variant-proof/production` (commands in [examples/variant-proof.md](examples/variant-proof.md); `dist/` is already git-ignored), then `node ${CLAUDE_SKILL_DIR}/scripts/check-variant-bundle.mjs . --bundle dist/variant-proof/development --variant development` and the same with `dist/variant-proof/production --variant production`. Delete `dist/variant-proof/` afterwards.
10. **Report** the three ids and names, the EAS variables the developer still has to create, the SKIP lines, and every `RESULT` line.

## Definition of done

- [ ] `src/config/app-variant.ts` is the canonical file, `app.config.ts` resolves the variant through it, the production config diff shows only the intended keys, and there is no `app.json`.
- [ ] `npx expo config --type public` shows three distinct ids, names, icons and schemes; an unknown or mismatched variant fails; every icon file exists and a preview prebuild succeeds.
- [ ] When eas.json exists, it has development, preview and production profiles with `APP_VARIANT`, `EXPO_PUBLIC_APP_VARIANT` and `environment` equal; the developer has the `eas env:set` list for the EAS environments.
- [ ] App code reads the variant only through `currentAppVariant()` and other public values only through `src/config/` (an inclusion flag's literal comparison at its `require()` excepted); no secret (an LLM or paid API key included) has an `EXPO_PUBLIC_` name or sits in a committed file; every plugin the config names is installed.
- [ ] `metro.config.js` keys `cacheVersion` on the `EXPO_PUBLIC_*` values; the app's typecheck, lint and Jest pass.
- [ ] When strip-relevant code changed: `node ${CLAUDE_SKILL_DIR}/scripts/check-variant-bundle.mjs . --bundle dist/variant-proof/production --variant production` and the development run print `RESULT: PASS`.
- [ ] Expected SKIP lines only: `SKIP eas.json [eas-profile] not yet created (eas-build-and-submit creates it)` before EAS is set up, and `SKIP src/test-only.ts [bundle-sentinel] not yet created (debug-menu-and-test-hooks creates it)` while the app has no test-only gate. A `note:` line about unpinned submit ids is a hand-off to `eas-build-and-submit`, not a failure.
- [ ] `node ${CLAUDE_SKILL_DIR}/scripts/check-env.mjs .` prints `RESULT: PASS`
- [ ] `node ${CLAUDE_SKILL_DIR}/scripts/check-variants.mjs .` prints `RESULT: PASS`

## Anti-patterns

- **Copying `templates/app.config.ts` over the app's config.** The EAS project link, `updates.url`, `runtimeVersion`, plugin options, usage strings and permissions vanish without an error; merge, then diff the production config.
- **`APP_VARIANT` only in eas.json.** `eas update --environment production` never reads eas.json: with this resolver the update fails, and with one that defaults it ships as development, test hooks included. Create the two variables in every EAS environment as well.
- **`eas update` without `--environment` in CI.** EAS CLI asks for an environment only outside CI, and even then the answer only labels the update: the export gets no EAS variables, loads the runner's `.env` files and reuses a warm Metro cache. Always pass `--environment <variant>`.
- **`const isDev = process.env.APP_VARIANT === 'development'` in app code.** `APP_VARIANT` is not inlined: it is `undefined` in every release build. Call `currentAppVariant()`.
- **`__DEV__` as "the development variant".** `__DEV__` says whether Metro served a debug bundle; preview and production are release builds, and a development build can load a release bundle. Ask the variant.
- **`.env.staging` or `NODE_ENV=staging` to pick an environment.** `npx expo export` forces `NODE_ENV=production`; variants and EAS environments are the switch.
- **A secret behind `EXPO_PUBLIC_` "because the bundle is minified".** Hermes bytecode keeps string literals readable. Call a server, or keep the secret in a build step.
- **`EXPO_PUBLIC_OPENAI_API_KEY` so the app can call the model directly.** Anyone can lift the key from the bundle and spend the developer's quota. Put the call in an API route (`src/app/api/<name>+api.ts`) that reads `OPENAI_API_KEY` from `process.env`, deployed with `eas deploy --environment production`.
- **A standalone `eas submit` with nothing exported.** It evaluates the config without the build profile env, so the resolver gives the development id. Export the production pair for it, or pin the submit ids (`eas-build-and-submit`).
- **One bundle id for every variant to keep push or IAP simple.** Every install replaces the store app. Register the extra ids, or test those services on production-id TestFlight or Play internal builds.
- **`IS_TEST_BUILD ? require('./debug-menu') : null`, or `STORYBOOK_ENABLED ? require(...)` with the flag read in `src/config/`.** Metro cannot fold an imported constant: the module ships (both verified on SDK 57). Write the literal comparison at the call.
- **`APP_VARIANT=production` in a committed `.env`.** Every developer's `expo start` and `prebuild` then builds the store identity; the variant belongs on the command line, in eas.json and in the EAS environments.
- **`npx expo export --clear` to fix a stale variant.** It hides the missing cache key until the next build; key `cacheVersion` on the public values.
- **Passing a token into `extra` or plugin options to use it later.** The public config is embedded in the app; tools read secrets from their own environment.
- **Exporting the variables for `expo prebuild` only, then building in a fresh shell.** The Constants step and Metro run again during the native build and need the same two variables.

## Files in this skill

| File | What it is | Read/run when |
|---|---|---|
| [references/variant-matrix.md](references/variant-matrix.md) | The three variants, the resolver contract per command, ids, names, icons, schemes, services and entitlements keyed by id, EAS commands outside a build, the moments a variant enters the binary | Workflow step 1 |
| [references/environment-variables.md](references/environment-variables.md) | How `EXPO_PUBLIC_*` inlining works (verified on SDK 57), `.env` loading, the readers in `src/config/`, server code (API routes), EAS environments, CI | Workflow step 3 |
| [references/secrets.md](references/secrets.md) | Public versus secret (LLM and paid API keys), where each secret lives (API routes for runtime secrets), what ships in the binary, the canary check, `.gitignore`, leak response | Workflow step 4 |
| [references/stripping-test-code.md](references/stripping-test-code.md) | The inclusion gate, the Metro cache trap, the SDK 57 experiments, sentinels, the bundle proof | Workflow steps 5 and 9 |
| [references/pitfalls-and-tests.md](references/pitfalls-and-tests.md) | Symptoms with causes and fixes, test recipes, every checker rule, `rn-skills.config.json` keys | A check fails; writing tests |
| `templates/src/config/app-variant.ts` | The canonical resolver: `resolveAppVariant`, `parseAppVariant`, `variantIdentity`, `variantEnv` (synced from the library; do not edit here) | Workflow step 2 |
| `templates/src/config/app-variant.test.ts` | Its tests, including a fast-check property (synced; do not edit here) | With the resolver |
| `templates/src/config/current-app-variant.ts` | `currentAppVariant()`: the one runtime reader of the variant | Workflow step 2 |
| `templates/src/config/current-app-variant.test.ts` | Its tests | With the reader |
| `templates/src/env.d.ts` | The typed `EXPO_PUBLIC_*` declarations both TypeScript programs read (synced from the library; do not edit here) | Workflow step 2; each new public variable |
| `templates/app.config.ts` | The app config: identity, icons (`iconsFor`, `STORE_IOS_ICON`), scheme and dev-client scheme per variant; comments show where merged values go | Workflow step 2 |
| `templates/metro.config.js` | Metro config with `cacheVersion` keyed on the public values | Workflow step 2 |
| `templates/scripts/metro/public-env-cache-version.ts` | The tested cache-key helper `metro.config.js` requires | Workflow step 2 |
| `templates/scripts/metro/compose-metro-config.ts` | The Metro slot: `readMetroKits` (the optional `scripts/metro/metro-kits.ts`) and `composeMetroConfig` (Expo's default or one kit's base, then each kit's transform); `metro.config.js` requires it | Workflow step 2; a kit that needs Metro |
| `templates/scripts/metro/compose-metro-config.test.ts` | Its tests: no kits, one base and transforms in order, two bases refused, the kits file | With the helper |
| `templates/scripts/metro/public-env-cache-version.test.ts` | Its tests | With the helper |
| `templates/eas.json` | The three build profiles wired to their variant and EAS environment, merged into the app's eas.json (`eas-build-and-submit` creates the file) | Workflow step 2 |
| `templates/dot-env.example` | `.env.example`: every variable by name, no values | Workflow step 2 |
| [examples/environment.ts](examples/environment.ts) | A typed public-environment reader for the Tasks app (shape only) | Workflow step 3 |
| [examples/variant-proof.md](examples/variant-proof.md) | The back-to-back export proof with real output | Workflow step 9 |
| `scripts/make-variant-icons.mjs` | Writes badged development and preview icons from `assets/icons/production/` (checks the sources are square PNGs, and that the two themed icons differ in alpha) | Workflow step 2 |
| `scripts/check-variants.mjs` | Evaluates the config per variant and per broken environment (committed env files loaded): resolve, plugins installed, ids, names, icon files and icons, schemes, eas.json profiles (shared bases too), secrets in the config; loads metro.config.js to prove the cache key | Workflow step 8 |
| `scripts/check-env.mjs` | Scans app code (`src/`, a root `app/`, the entry and everything they import) and files: public-env secrets (LLM and paid API keys included, the app config too), the one reader (server code exempt), the variant reader, inclusion decided at the `require()`, server imports, secrets and variants in eas.json and env files, `.gitignore` | Workflow step 8 |
| `scripts/check-variant-bundle.mjs` | Checks an exported bundle for sentinels and secrets against its variant (secrets even before a test-only gate exists) | Workflow step 9 |
| `scripts/lib/env-scan.mjs` | Shared helpers: secret names and values, dotenv, `.gitignore` matching, eas.json profiles | Only when changing a checker |
| `scripts/lib/source-scan.mjs` | Dependency-free source lexer (synced from the library; do not edit here) | Only when changing a checker |
| `scripts/check-lib.mjs` | Shared checker library (synced from the library; do not edit here) | Never by hand |
| `scripts/selftest.mjs` | Runs every checker against the fixtures | After changing a checker or a template |
| `tests/bases/` | The base app and the planted decoys the fixtures assemble from | When adding a fixture |
| `tests/fixtures/` | Overlay cases per script: good, bad cases per rule, pass and error cases | When adding a rule |
| `assets/shared.json` | The shared files this skill syncs in | When adding a shared file |
| `assets/template-placeholders.json` | Values for the `__UPPER_SNAKE__` placeholders in template checks | When a template gains a placeholder |

## Related skills

- `expo-prebuild-and-config-plugins` - app.config.ts as the single source, config plugins, never editing native folders.
- `eas-build-and-submit` - the rest of eas.json: credentials, versions, submit profiles; Expo's official `eas-app-stores` skill is the EAS API reference.
- `debug-menu-and-test-hooks` - the test-only gate file, the debug registry and the sentinel this skill's bundle check reads.
- `icons-app-icon-and-splash` - may redraw the badged development and preview icons at `assets/icons/<variant>/` (icon.png 1024 x 1024, android-foreground/background/monochrome.png).
- `privacy-and-store-declarations` - the SDK inventory, store privacy answers and repository-wide secret audit.
- `networking-and-server-state` - the `ApiClient` that calls the app's API routes; Expo's official `eas-hosting` skill is the API-route and EAS Hosting reference.
- `crash-reporting-and-observability` - Sentry release names per variant and the upload token as an EAS secret.
- `ota-updates` - channels per variant and `eas update --environment`.
- `ci-pipeline-and-gates` - CI secrets and the variables every CI job exports.
- `ios-simulator-build`, `android-emulator-build` - building and proving each variant on a named device.

Files in this skill

  • SKILL.md24.9 KB
  • assets/shared.json1 KB
  • assets/template-placeholders.json123 B
  • examples/environment.ts2.1 KB
  • examples/variant-proof.md10.5 KB
  • references/environment-variables.md14.8 KB
  • references/pitfalls-and-tests.md22.2 KB
  • references/secrets.md14.2 KB
  • references/stripping-test-code.md14.5 KB
  • references/variant-matrix.md18.1 KB
  • scripts/check-env.mjs35.5 KB
  • scripts/check-variant-bundle.mjs11.3 KB
  • scripts/check-variants.mjs50.1 KB
  • scripts/lib/env-scan.mjs22.6 KB
  • scripts/lib/source-scan.mjs35.1 KB
  • scripts/make-variant-icons.mjs10.1 KB
  • scripts/selftest.mjs1 KB
  • templates/app.config.ts5.1 KB
  • templates/dot-env.example963 B

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.