# io.github.gbatistuta0/appfactory (pypi · appfactory)

Lets your coding agent ship a SwiftUI iOS subscription app from idea to TestFlight.

- Trust score: 75/100 (medium)
- Registry status: active
- Liveness: live
- Owner verified: no
- Last scored: 2026-10-04

## Components

- pypi · `appfactory`: 75/100 (this document), [markdown](https://verifymcp.io/servers/gbatistuta0-appfactory/appfactory.md), [page](https://verifymcp.io/servers/gbatistuta0-appfactory/appfactory)

## Channel facts

- Registry: `pypi`
- Package: `appfactory`
- Version: `0.1.5`
- Transport: `stdio`

## Trust breakdown

How this component scores in each security and reliability category. Every signal is checked automatically from public evidence about the published package, including repeated runs of it in an isolated sandbox, and we only credit what we can confirm. Scores are 0–100 per category. Scoring method: https://verifymcp.io/docs/scoring (what has changed: https://verifymcp.io/docs/scoring/changelog)

Scored 2026-10-04.

- **Supply Chain Security**: 100/100
  - No malware found by supply-chain analysis.
  - No known CVEs affecting this package version or its production dependencies.
  - Runs hatchling.build at install time, a recognised build step with no custom scripting around it.
  - 1 of 22 dependencies flagged as unhealthy.
- **Provenance & Transparency**: 100/100
  - Source repository is publicly reachable at the declared URL.
  - Cryptographically verified build provenance (signed, bound to gbatistuta0/appfactory).
  - Clear OSI-approved license (MIT).
  - Actively maintained (last published 4 days ago).
  - Publishes a security disclosure policy (SECURITY.md).
- **Schema Quality & AI Usability**: 48/100
  - 100% of prompts and resources have a non-trivial description (not blank, and not just the item's name).
  - Instruction-quality not yet verified: the AI judgement didn't run.
  - Context-footprint check failed: tool/resource definitions use about 19176 tokens (~152/item across 126 items; 125 tools + 1 resources), over budget; trim descriptions and params.
  - Usage-examples check failed: none of the tools include examples.
- **Stability & Change Management**: 0/100
  - Stability not yet verified: not enough scan history yet (needs a 30-day window).
- **Tool Coverage**: 100/100
  - 100% of tools have a non-trivial description (not blank, and not just the tool's name).
  - 100% of tool parameters carry a description.
  - Structured output schemas are declared (100% of tools); any adoption earns full credit.
- **Tool Safety**: 75/100
  - No prompt-injection markers were found in the server instructions, tool names or descriptions we captured.
  - All 4 tool(s) whose name or description implies an irreversible operation declare an MCP destructiveHint annotation.
  - Manipulation not yet verified: only 2 of 127 captured unit(s) of tool text has been judged so far, so we will not certify text no model has read as clean.
- **Capabilities**: 100/100
  - Implements a current MCP spec version (2026-07-28).

**Unverified: 1 category.** A category scored 0 because we could not verify it: a data source with nothing on this package, evidence we could not reach, or a check we could not run. We only credit what we can confirm.

## Install

### How do I install the io.github.gbatistuta0/appfactory MCP server?

io.github.gbatistuta0/appfactory runs locally as a PyPI package, launched with uvx appfactory. Ready-made configuration for Claude, Cursor, VS Code, Codex and 5 more is on this page, copied from each client's own documentation.

### Claude

```bash
claude mcp add gbatistuta0-appfactory -- uvx appfactory
```

### Cursor

```json
{
  "mcpServers": {
    "gbatistuta0-appfactory": {
      "command": "uvx",
      "args": [
        "appfactory"
      ]
    }
  }
}
```

### VS Code

```json
{
  "servers": {
    "gbatistuta0-appfactory": {
      "command": "uvx",
      "args": [
        "appfactory"
      ]
    }
  }
}
```

### Codex

```bash
codex mcp add gbatistuta0-appfactory -- uvx appfactory
```

### opencode

```json
{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "gbatistuta0-appfactory": {
      "type": "local",
      "command": [
        "uvx",
        "appfactory"
      ],
      "enabled": true
    }
  }
}
```

### OpenClaw

```bash
openclaw mcp add gbatistuta0-appfactory --command uvx --arg appfactory
```

### Hermes

```yaml
mcp_servers:
  gbatistuta0-appfactory:
    command: "uvx"
    args: ["appfactory"]
```

### Netclaw

```json
{
  "McpServers": {
    "gbatistuta0-appfactory": {
      "Transport": "stdio",
      "Command": "uvx",
      "Arguments": [
        "appfactory"
      ]
    }
  }
}
```

### Vellum

```bash
assistant mcp add gbatistuta0-appfactory -t stdio -c uvx -a appfactory
```

### Other

```json
{
  "mcpServers": {
    "gbatistuta0-appfactory": {
      "command": "uvx",
      "args": [
        "appfactory"
      ]
    }
  }
}
```

## Changelog

Every change recorded for this component, newest first. Days that predate change tracking, or that we cannot explain, say so: "we were watching and nothing happened" and "we were not watching" are different claims.

### 2026-09-30 (score 75, +15)

- [security improvement] Malware scan: unverified → pass

### 2026-09-29 (score 60)

First indexed and scored.

## MCP tools (125)

### `setup_status` (~83 tokens)

Setup Status

Report the AppFactory setup state per service: enabled, keys set or missing (never values), missing tools
with install commands, approvals mode.

Use when: first call of every session, and after any setup change. Works with no config file.
Returns: {ok, services: {name: {enabled, keys, missing}}, approvals, next: [steps]}.

### `setup_services` (~122 tokens)

Setup Services

Turn optional services on or off (research is always on).

Use when: the user has said which services they want; ask them first. Not for: entering credentials (use
setup_credentials).
Returns: the setup_status result after the change.

Input parameters:

- `disable`: Service ids to turn off (same ids as enable).
- `enable`: Service ids to turn on, e.g. ['apple', 'supabase', 'revenuecat', 'github', 'firebase', 'xcode', 'maestro', 'design', 'ai', 'lottie'].

### `setup_set` (~112 tokens)

Setup Set

Set ONE non-secret config key such as asc_key_id, team_id or support_email; an empty value clears it.

Use when: changing a single key. Secrets are refused; use setup_credentials so they never pass through the
chat. For several fields at once use config_set.
Returns: {ok, key, error?}.

Input parameters:

- `key` (string, required): Non-secret config key, e.g. 'asc_key_id', 'team_id', 'support_email'.
- `value` (string, required): Value to store.

### `setup_approvals` (~86 tokens)

Setup Approvals

Set human approvals for live writes to 'required' (recommended).

Use when: re-enabling approvals. 'off' is refused here; only the human can switch it off on the
setup_credentials page.
Returns: {ok, approvals}.

Input parameters:

- `mode` (string, required): Approvals mode; only 'required' is accepted here ('off' must be set by the human).

### `setup_credentials` (~100 tokens)

Setup Credentials

Open a local browser page (127.0.0.1, random port, one-time token) where the USER types credentials for the
given services.

Use when: setup_status shows missing keys. Secrets never reach the agent; return immediately, tell the user
to fill the form and Save, then call setup_status.
Returns: {ok, url, services}.

Input parameters:

- `services`: Service ids to collect credentials for; omit for every enabled service.

### `config_doctor` (~97 tokens)

Config Doctor

Report which config keys in ~/.appfactory/config.toml are present or missing (secrets masked).

Use when: diagnosing ASC/Finance key paths, session state or CLI availability. Not for: overall setup
guidance (use setup_status) or toolchain checks (use env_doctor).
Returns: {ok, config_path, present, missing, locales, asc_p8_resolved, session, asc_cli, maestro, notes}.

### `config_set` (~299 tokens)

Config Set

Write several NON-secret settings (ASC key id/issuer/path, team id, copyright, support email, ...) to
\~/.appfactory/config.toml.

Use when: setting multiple ASC/identity fields together; fields left empty are unchanged. Not for: secrets
(use setup_credentials) or one key (use setup_set).
Returns: the config_doctor result after the update, or {ok: false, error} if a secret key was passed.

Input parameters:

- `apple_id`: Apple ID email of the developer account (used by fastlane produce only).
- `asc_finance_key_filepath`: Path to the Finance-role .p8 key file.
- `asc_finance_key_id`: Key id of a Finance-role App Store Connect API key (for sales reports).
- `asc_issuer_id`: App Store Connect API issuer id (UUID).
- `asc_key_filepath`: Path to the App Store Connect .p8 private key file.
- `asc_key_id`: App Store Connect API key id (10 characters).
- `asc_vendor_number`: App Store Connect vendor number (sales reports).
- `copyright`: Copyright line for the store listing, e.g. '2026 Example Ltd'; falls back to the config 'copyright' key.
- `legal_controller`: Legal entity name shown as data controller in the privacy policy.
- `support_email`: Public support email address.
- `team_id`: Apple Developer Team id (10 characters).

### `asc_token_check` (~88 tokens)

ASC Token Check

Verify App Store Connect authentication through the asc CLI with one live read-only call.

Use when: before any asc_* tool, to confirm key id, issuer and .p8 work. Not for: listing apps (use
asc_list_apps).
Returns: {ok, asc: {path, version}, auth_source, key_id, p8, keychain_profiles, note, error?}.

### `asc_list_apps` (~69 tokens)

ASC List Apps

List the apps in App Store Connect (live, read-only).

Use when: you need app ids or want to see what exists. To find one app by bundle id use asc_get_app.
Returns: {ok, count, apps: [{id, bundleId, name, sku}]}.

### `asc_get_app` (~94 tokens)

ASC Get App

Find one App Store Connect app by bundle id (live, read-only).

Use when: you need the numeric app id for a known bundle id. Not for: listing all apps (use asc_list_apps).
Returns: {ok, data: {data: [app resources]}} from the ASC API.

Input parameters:

- `bundle_id` (string, required): App bundle identifier in reverse-DNS form, e.g. com.example.app.

### `asc_create_bundle_id` (~252 tokens)

ASC Create Bundle ID

Register a bundle id in App Store Connect and check or apply its App ID capabilities (live write, needs
human approval).

Use when: first ASC step for a new app; capabilities (IN_APP_PURCHASE, Sign in with Apple, HEALTHKIT if
enabled) must exist before signing. Not for: creating the app record (use asc_create_app).
Returns: {bundle_id: <ASC result>, capabilities: <store_setup report>} or an approval_required refusal.

Input parameters:

- `app_dir`: Absolute path to the app project directory (contains app.spec.json), e.g. /Users/me/Apps/MyApp.
- `apply_capabilities` (boolean): If true, also enable the spec's App ID capabilities (needs app_dir); false only checks them.
- `approval_id`: Approval id from a previous approval_required refusal; omit on the first call. The human approves out-of-band with `appfactory approve <id>`, then call again with the same arguments and this id.
- `identifier` (string, required): Bundle identifier to register, e.g. com.example.app.
- `name` (string, required): Human-readable name of the bundle id in the developer portal, e.g. 'My App'.

### `asc_create_app` (~235 tokens)

ASC Create App

Create an app shell in App Store Connect via fastlane produce (live write, needs human approval).

Use when: after aso_check_name confirms a free name; pass candidate_names and the first available is used.
Not for: bundle ids (asc_create_bundle_id) or metadata (deliver_metadata).
Returns: {ok, app_id, name, ...} or an approval_required refusal.

Input parameters:

- `app_name`: Single app name; prefer candidate_names. Converted to a one-element candidate list.
- `approval_id`: Approval id from a previous approval_required refusal; omit on the first call. The human approves out-of-band with `appfactory approve <id>`, then call again with the same arguments and this id.
- `bundle_id` (string, required): App bundle identifier in reverse-DNS form, e.g. com.example.app.
- `candidate_names`: Ordered candidate App Store names; the first one not already taken is used.
- `primary_language` (string): Primary App Store language, e.g. 'en-US'.
- `sku`: Unique SKU string for the app record; omit to derive from the bundle id.

### `asc_create_subscription_group` (~183 tokens)

ASC Create Subscription Group

Create a subscription group for an app (live write, needs human approval).

Use when: hand-building the subscription tree. Prefer store_setup, which does groups, products, prices and
offers idempotently from app.spec.json.
Returns: {ok, data: {data: {id, ...}}} or an approval_required refusal.

Input parameters:

- `app_id` (string, required): App Store Connect app id (numeric Apple id, e.g. '1234567890'); for aso_competitor_iap the iTunes trackId.
- `approval_id`: Approval id from a previous approval_required refusal; omit on the first call. The human approves out-of-band with `appfactory approve <id>`, then call again with the same arguments and this id.
- `reference_name` (string, required): Internal reference name of the subscription group (not shown to users).

### `asc_finalize_subscription` (~271 tokens)

ASC Finalize Subscription

Finalize one subscription in order: localization, availability (all but CHN), price, intro offer (live
write, needs human approval).

Use when: fixing a single product by hand. Prefer store_setup, which does every product idempotently. Pass
the spec product's intro (e.g. {"type": "free", "duration": "P3D"}); offer products get no intro.
Returns: {ok, steps...} per step, or an approval_required refusal.

Input parameters:

- `approval_id`: Approval id from a previous approval_required refusal; omit on the first call. The human approves out-of-band with `appfactory approve <id>`, then call again with the same arguments and this id.
- `description` (string, required): Subscription description shown to users (en-US).
- `intro`: Intro offer from the spec product, e.g. {"type": "free", "duration": "P3D"}; omit for offer products (no intro).
- `name` (string, required): Subscription display name shown to users (en-US).
- `period`: Ignored; kept for backward compatibility.
- `sub_id` (string, required): App Store Connect subscription id.
- `usd_price` (string, required): USD base price as a decimal string, e.g. '49.99'.

### `asc_finalize_submission_requirements` (~354 tokens)

ASC Finalize Submission Requirements

Fill the submit-blocking app-level fields in one call: content rights, copyright, age rating (4+), free
price, App Review contact and notes (live write, needs human approval).

Use when: once per app before asc_submit_for_review. App Privacy is not in Apple's API and must be set in
the ASC web UI. Missing copyright falls back to config; missing contact_email to support_email.
Returns: {ok, steps...} or an approval_required refusal.

Input parameters:

- `ai_services`: Short description of the AI services the app uses, inserted into the review notes.
- `app_name`: App name used to fill the generic App Review notes template.
- `approval_id`: Approval id from a previous approval_required refusal; omit on the first call. The human approves out-of-band with `appfactory approve <id>`, then call again with the same arguments and this id.
- `bundle_id` (string, required): App bundle identifier in reverse-DNS form, e.g. com.example.app.
- `contact_email`: App Review contact email; defaults to the config support_email.
- `contact_first` (string): App Review contact first name.
- `contact_last` (string): App Review contact last name.
- `contact_phone` (string): App Review contact phone in international format, e.g. '+14155550100'.
- `copyright`: Copyright line for the store listing, e.g. '2026 Example Ltd'; falls back to the config 'copyright' key.
- `free` (boolean): If true (default) the app price is set to Free.
- `review_notes`: Custom App Review notes; omit to fill the generic 7-item test-flow template.

### `asc_append_subscription_disclosure` (~236 tokens)

ASC Append Subscription Disclosure

Append the subscription disclosure, Terms/EULA and Privacy links to the app description in every language
(Apple 3.1.2; live write, needs human approval).

Use when: preparing a subscription app for review. Idempotent (skips when the marker is present) and
truncated to 4000 characters. Not for: uploading other metadata (use deliver_metadata).
Returns: {ok, per-locale results} or an approval_required refusal.

Input parameters:

- `approval_id`: Approval id from a previous approval_required refusal; omit on the first call. The human approves out-of-band with `appfactory approve <id>`, then call again with the same arguments and this id.
- `bundle_id` (string, required): App bundle identifier in reverse-DNS form, e.g. com.example.app.
- `disclosure_by_locale`: Optional {locale: disclosure text} overriding the default subscription disclosure per language.
- `privacy_url` (string, required): Public privacy policy URL, e.g. https://example.com/privacy.
- `terms_url`: Terms of Use/EULA URL; omit to use Apple's standard EULA.

### `asc_ensure_subscription_prices` (~202 tokens)

ASC Ensure Subscription Prices

Set a price on every subscription of the app that has none, clearing MISSING_METADATA (live write, needs
human approval).

Use when: subscriptions are stuck in MISSING_METADATA for price. Idempotent: priced subscriptions are
skipped.
Returns: {ok, priced: [...], skipped: [...]} or an approval_required refusal.

Input parameters:

- `approval_id`: Approval id from a previous approval_required refusal; omit on the first call. The human approves out-of-band with `appfactory approve <id>`, then call again with the same arguments and this id.
- `bundle_id` (string, required): App bundle identifier in reverse-DNS form, e.g. com.example.app.
- `default_usd`: USD price string applied to subscriptions not listed in usd_price_by_product.
- `usd_price_by_product`: Map of subscription productId to USD price string, e.g. {'com.example.app.yearly': '49.99'}.

### `asc_add_subscription_group_localization` (~161 tokens)

ASC Add Subscription Group Localization

Add one display-name localization to a subscription group (required to clear MISSING_METADATA; live write,
needs human approval).

Use when: a single locale is needed. For many languages use asc_localize_group.
Returns: {ok, data} or an approval_required refusal.

Input parameters:

- `approval_id`: Approval id from a previous approval_required refusal; omit on the first call. The human approves out-of-band with `appfactory approve <id>`, then call again with the same arguments and this id.
- `group_id` (string, required): App Store Connect subscription group id.
- `locale` (string): Locale code, e.g. 'en-US', 'de-DE'.
- `name` (string, required): Subscription group display name shown to users.

### `asc_localize_subscription` (~156 tokens)

ASC Localize Subscription

Localize a subscription's name and description in many languages at once (live write, needs human approval).

Use when: filling per-locale product copy. Languages the IAP API does not support are skipped.
Returns: {ok, done: [locales], skipped: [locales]} or an approval_required refusal.

Input parameters:

- `approval_id`: Approval id from a previous approval_required refusal; omit on the first call. The human approves out-of-band with `appfactory approve <id>`, then call again with the same arguments and this id.
- `items` (object, required): Map {locale: {name, description}} of subscription display copy per locale.
- `sub_id` (string, required): App Store Connect subscription id.

### `asc_localize_group` (~148 tokens)

ASC Localize Group

Localize a subscription group's display name in many languages at once (live write, needs human approval).

Use when: several locales are needed; for one locale use asc_add_subscription_group_localization.
Returns: {ok, done: [locales], skipped: [locales]} or an approval_required refusal.

Input parameters:

- `approval_id`: Approval id from a previous approval_required refusal; omit on the first call. The human approves out-of-band with `appfactory approve <id>`, then call again with the same arguments and this id.
- `group_id` (string, required): App Store Connect subscription group id.
- `name_by_locale` (object, required): Map {locale: group display name}.

### `asc_create_subscription` (~212 tokens)

ASC Create Subscription

Create a subscription product inside a subscription group (live write, needs human approval).

Use when: hand-building a single product. Prefer store_setup for the whole spec.
Returns: {ok, data: {data: {id, ...}}} or an approval_required refusal.

Input parameters:

- `approval_id`: Approval id from a previous approval_required refusal; omit on the first call. The human approves out-of-band with `appfactory approve <id>`, then call again with the same arguments and this id.
- `family_shareable` (boolean): Whether the subscription can be shared with family members; default false.
- `group_id` (string, required): App Store Connect subscription group id.
- `name` (string, required): Internal reference name of the subscription product.
- `period` (string): Subscription period enum: ONE_WEEK, ONE_MONTH, TWO_MONTHS, THREE_MONTHS, SIX_MONTHS or ONE_YEAR.
- `product_id` (string, required): Product identifier of the subscription, e.g. com.example.app.yearly.

### `asc_submit_for_review` (~184 tokens)

ASC Submit For Review

Submit the app to App Store review (live write, ALWAYS needs out-of-band human approval, even with approvals
off).

Use when: everything else is ready and the human has approved. Without a valid approval_id nothing is
submitted. Not for: uploading a TestFlight build (use testflight_ship).
Returns: {ok, review_submission_id, detail} or {ok: false, blockers, next}, or an approval_required refusal.

Input parameters:

- `app_id` (string, required): App Store Connect app id (numeric Apple id, e.g. '1234567890'); for aso_competitor_iap the iTunes trackId.
- `approval_id`: Approval id from a previous approval_required refusal; omit on the first call. The human approves out-of-band with `appfactory approve <id>`, then call again with the same arguments and this id.

### `env_doctor` (~89 tokens)

Environment Doctor

Check the local toolchain: xcode, swift, node, ruby, git, uv, asc, fastlane, maestro, Java 17+ and maestro-
live.

Use when: something fails to run locally. Not for: credentials/config (use config_doctor or setup_status).
Returns: {ok, available: {tool: bool}, versions: {tool: str}, notes?}.

### `build_xcode_version` (~52 tokens)

Build Xcode Version

Return the installed Xcode version.

Use when: a quick check that xcodebuild works before build_* tools.
Returns: {ok, exit_code, stdout, stderr} (stdout holds the version lines).

### `build_list_simulators` (~64 tokens)

Build List Simulators

List the available iOS simulators, iPhones first.

Use when: you need a udid for build_boot_sim, build_screenshot or screenshot_capture.
Returns: {ok, count, simulators: [{name, udid, state, runtime}]}.

### `build_boot_sim` (~93 tokens)

Build Boot Simulator

Boot an iOS simulator and open Simulator.app.

Use when: before capturing screenshots or running maestro_test. Idempotent if already booted.
Returns: {ok, exit_code, stdout, stderr}, or {ok, note: 'already booted'}.

Input parameters:

- `udid` (string, required): Simulator UDID from build_list_simulators, e.g. 'A1B2C3D4-...'.

### `build_screenshot` (~101 tokens)

Build Screenshot

Capture a PNG screenshot of the booted simulator to out_path.

Use when: an ad-hoc check. For localized store screenshots use screenshot_capture / screenshot_build_all.
Returns: {ok, exit_code, stdout, stderr, path}.

Input parameters:

- `out_path` (string, required): Output file path (PNG).
- `udid` (string, required): Simulator UDID from build_list_simulators, e.g. 'A1B2C3D4-...'.

### `build_for_sim` (~134 tokens)

Build For Simulator

Build the Xcode project for the simulator to verify it compiles.

Use when: verifying code changes. Not for: signed/release builds (use build_archive or testflight_ship).
Returns: {ok, exit_code, stdout, stderr} from xcodebuild.

Input parameters:

- `device_name` (string): Simulator device name, e.g. 'iPhone 16'.
- `project` (string, required): Path to the .xcodeproj or .xcworkspace to build, e.g. /path/App/App.xcodeproj.
- `scheme` (string, required): Xcode scheme name to build, e.g. 'MyApp'.

### `build_test` (~126 tokens)

Build Test

Run xcodebuild test for the scheme on a simulator.

Use when: running unit/UI tests. For end-to-end Maestro flows use maestro_test.
Returns: {ok, exit_code, stdout, stderr} from xcodebuild.

Input parameters:

- `device_name` (string): Simulator device name, e.g. 'iPhone 16'.
- `project` (string, required): Path to the .xcodeproj or .xcworkspace to build, e.g. /path/App/App.xcodeproj.
- `scheme` (string, required): Xcode scheme name to build, e.g. 'MyApp'.

### `maestro_test` (~186 tokens)

Maestro Test

Run the app's Maestro flows (.maestro/) on the booted simulator with the live Viewer, and write
.appfactory/verify/maestro.json.

Use when: verifying the app end to end; testflight_ship and the features gate require every smoke flow
green. The app must already be installed (build_for_sim + simctl install). Opens the Viewer at
http://localhost:7777; there is no headless mode.
Returns: {ok, flows: [{name, passed}], report path}.

Input parameters:

- `app_dir` (string, required): Absolute path to the app project directory (contains app.spec.json), e.g. /Users/me/Apps/MyApp.
- `device`: Simulator UDID to run on; omit for the booted simulator.
- `tags`: Comma-separated Maestro tag filter, e.g. 'smoke'; omit to run every flow.

### `build_archive` (~152 tokens)

Build Archive

Run xcodebuild archive (generic iOS device) to produce a .xcarchive.

Use when: manual signing pipeline steps. For the whole signed TestFlight flow use testflight_ship.
Returns: {ok, exit_code, stdout, stderr} from xcodebuild.

Input parameters:

- `archive_path` (string, required): Path to the .xcarchive, e.g. build/App.xcarchive.
- `configuration` (string): Xcode build configuration, default 'Release'.
- `project` (string, required): Path to the .xcodeproj or .xcworkspace to build, e.g. /path/App/App.xcodeproj.
- `scheme` (string, required): Xcode scheme name to build, e.g. 'MyApp'.

### `build_export_ipa` (~130 tokens)

Build Export IPA

Run xcodebuild -exportArchive to turn a .xcarchive into an .ipa.

Use when: after build_archive with an ExportOptions.plist. For the whole flow use testflight_ship.
Returns: {ok, exit_code, stdout, stderr} from xcodebuild.

Input parameters:

- `archive_path` (string, required): Path to the .xcarchive, e.g. build/App.xcarchive.
- `export_dir` (string, required): Directory to write the exported .ipa into.
- `export_options_plist` (string, required): Path to the ExportOptions.plist for xcodebuild -exportArchive.

### `supabase_list_projects` (~57 tokens)

Supabase List Projects

List Supabase projects (live, read-only).

Use when: you need a project ref. For org ids use supabase_list_orgs.
Returns: {ok, projects: [{name, ref, region, status}]}.

### `supabase_list_orgs` (~49 tokens)

Supabase List Orgs

List Supabase organizations (live, read-only).

Use when: you need the org_id for supabase_create_project.
Returns: {ok, data: [{id, name}]}.

### `supabase_get_keys` (~105 tokens)

Supabase Get Keys

Get a Supabase project's URL and anon key; the service_role key is saved to
\~/.appfactory/supabase/<ref>.json (0600) and never returned.

Use when: wiring the app (app_inject_config) or backend.
Returns: {ok, project_url, anon_key, service_role_key_file}.

Input parameters:

- `ref` (string, required): Supabase project ref (20-char id from supabase_list_projects, e.g. 'abcdefghijklmnopqrst').

### `supabase_run_sql` (~198 tokens)

Supabase Run SQL

Run SQL on a Supabase project (live write, needs human approval).

Use when: applying schema or migrations by hand; backend_deploy applies the spec's migrations for you.
Destructive statements (DROP, TRUNCATE, ALTER ... DROP, GRANT/REVOKE on auth, DELETE/UPDATE without WHERE)
always need approval.
Returns: {ok, data} rows/result, or an approval_required refusal.

Input parameters:

- `approval_id`: Approval id from a previous approval_required refusal; omit on the first call. The human approves out-of-band with `appfactory approve <id>`, then call again with the same arguments and this id.
- `ref` (string, required): Supabase project ref (20-char id from supabase_list_projects, e.g. 'abcdefghijklmnopqrst').
- `sql` (string, required): SQL to execute. Destructive statements (DROP, TRUNCATE, DELETE/UPDATE without WHERE) always need approval.

### `supabase_set_secret` (~182 tokens)

Supabase Set Secret

Write one edge-function secret on a Supabase project, server-side only (live write, needs human approval).

Use when: adding a single secret such as FAL_KEY. backend_deploy sets the standard set for you.
Returns: {ok, name} (value never echoed), or an approval_required refusal.

Input parameters:

- `approval_id`: Approval id from a previous approval_required refusal; omit on the first call. The human approves out-of-band with `appfactory approve <id>`, then call again with the same arguments and this id.
- `name` (string, required): Edge function secret name, e.g. 'FAL_KEY'.
- `ref` (string, required): Supabase project ref (20-char id from supabase_list_projects, e.g. 'abcdefghijklmnopqrst').
- `value` (string, required): Secret value; written server-side and never echoed back.

### `supabase_create_project` (~191 tokens)

Supabase Create Project

Create a new Supabase project (live write that provisions billable resources, needs human approval).

Use when: the app has no backend project yet. Check supabase_list_projects first to avoid duplicates.
Returns: {ok, data: {id/ref, name, region, ...}} or an approval_required refusal.

Input parameters:

- `approval_id`: Approval id from a previous approval_required refusal; omit on the first call. The human approves out-of-band with `appfactory approve <id>`, then call again with the same arguments and this id.
- `db_pass` (string, required): Database password for the new project.
- `name` (string, required): Name of the new Supabase project.
- `org_id` (string, required): Supabase organization id from supabase_list_orgs.
- `region` (string, required): Supabase region code, e.g. 'us-east-1', 'eu-central-1'.

### `app_scaffold` (~207 tokens)

App Scaffold

Scaffold a new SwiftUI app from app.spec.json (xcodegen), init the pipeline manifest and a local git repo.

Use when: starting a new app after idea validation. The spec drives products, StoreKit file, locales,
onboarding length, monetization mode and backend mode. Not for: editing an existing app (use app_sync_spec
after changing the spec).
Returns: {ok, dir, name, bundle_id, manifest}.

Input parameters:

- `bundle_id`: App bundle identifier in reverse-DNS form, e.g. com.example.app.
- `dest_dir`: Directory to create the app folder in; omit for the default apps directory.
- `display_name`: Name shown under the app icon; defaults to name.
- `name`: App name, e.g. 'PetPortrait'; used for the folder and target.
- `spec`: Full app.spec.json as a dict, a dict of overrides on the defaults, or a path to an app.spec.json file.

### `github_create_repo` (~211 tokens)

GitHub Create Repo

Create a PRIVATE GitHub repo under the configured account and push the app (needs human approval unless
dry_run).

Use when: the app should be tracked on GitHub. dry_run=true (default) only returns the command. For later
commits use github_push.
Returns: {ok, command/url, dry_run} or an approval_required refusal.

Input parameters:

- `app_dir` (string, required): Absolute path to the app project directory (contains app.spec.json), e.g. /Users/me/Apps/MyApp.
- `approval_id`: Approval id from a previous approval_required refusal; omit on the first call. The human approves out-of-band with `appfactory approve <id>`, then call again with the same arguments and this id.
- `dry_run` (boolean): If true (default) nothing is changed or sent and the exact plan/command is returned; false performs the live action.
- `repo_name` (string, required): Name of the new private repository, e.g. 'my-app'.

### `github_push` (~148 tokens)

GitHub Push

Commit all changes in the app repo and push (live write, needs human approval).

Use when: regular tracking after milestones. For the first push of a new repo use github_create_repo.
Returns: {ok, commit, push} or an approval_required refusal.

Input parameters:

- `app_dir` (string, required): Absolute path to the app project directory (contains app.spec.json), e.g. /Users/me/Apps/MyApp.
- `approval_id`: Approval id from a previous approval_required refusal; omit on the first call. The human approves out-of-band with `appfactory approve <id>`, then call again with the same arguments and this id.
- `message` (string, required): Git commit message.

### `firebase_setup` (~208 tokens)

Firebase Setup

Set up Firebase Analytics: GCP project, iOS app and GoogleService-Info.plist into Resources/ (live write,
needs human approval).

Use when: once per app (mandatory for every app). Requires gcloud auth and firebase-tools.
Returns: {ok, project_id, app_id, google_analytics, console, plist} or an approval_required refusal.

Input parameters:

- `app_dir` (string, required): Absolute path to the app project directory (contains app.spec.json), e.g. /Users/me/Apps/MyApp.
- `app_name` (string, required): App name as shown on the App Store (globally unique, max 30 characters).
- `approval_id`: Approval id from a previous approval_required refusal; omit on the first call. The human approves out-of-band with `appfactory approve <id>`, then call again with the same arguments and this id.
- `bundle_id` (string, required): App bundle identifier in reverse-DNS form, e.g. com.example.app.

### `app_inject_config` (~377 tokens)

App Inject Config

Fill in the scaffolded app's AppConfig and StoreKit tokens (Supabase, proxy, PostHog, product ids, credits,
paywall strategy).

Use when: after backend and RevenueCat values exist. Credit fields default to 15/10/10/30/60 and pack
amounts must match the server-side PACK_MAP. Not for: spec-driven changes (edit app.spec.json, then
app_sync_spec).
Returns: {ok, changed: [files]}.

Input parameters:

- `ai_proxy_url`: URL of the deployed AI proxy/edge function.
- `credit_pack_large`: Credits in the large top-up pack (default 60).
- `credit_pack_medium`: Credits in the medium top-up pack (default 30).
- `credit_pack_small`: Credits in the small top-up pack (default 10).
- `paywall_strategy`: 'hard_only' (hard paywall only) or 'hard_and_offer' (default: plus a discounted offer paywall on dismiss).
- `posthog_key`: PostHog project API key.
- `privacy_url`: Public privacy policy URL.
- `product_weekly`: Weekly subscription product id.
- `product_yearly`: Yearly subscription product id.
- `project_dir` (string, required): Path to the scaffolded app project directory.
- `revenuecat_key`: RevenueCat public SDK key (appl_...).
- `supabase_anon_key`: Supabase anon (public) key.
- `supabase_url`: Supabase project URL, e.g. https://abcd.supabase.co.
- `support_email`: Public support email address.
- `weekly_credits`: Credits granted per week on the weekly plan (default 10).
- `yearly_credits`: Credits granted per month on the yearly plan (default 15).

### `onboarding_plan` (~101 tokens)

Onboarding Plan

Return guidance for choosing the onboarding step count; does not change any file.

Use when: deciding onboarding length with the user (quiz-style flow with about 5 personalization questions
is the standard).
Returns: {ok, step_count, ranges: {"8-10"|"11-13"|"14-15": description}, warning?}.

Input parameters:

- `step_count` (integer): Desired number of onboarding steps; recommended range 8-15 (default 12).

### `animation_fetch_recolor` (~223 tokens)

Animation Fetch Recolor

Fetch a cute Lottie animation from the free LottieFiles library and recolor it to the app palette into
Resources/Animations/<slot>.json.

Use when: once per slot (loading, success, empty, onboarding_hero) for every app; rendered by lottie-spm
with .named(slot). Colors are mapped at build time (dominant to primary, others to accent).
Returns: {ok, path, source, colors}.

Input parameters:

- `accent_hex` (string, required): Accent palette color as hex, '#' optional, e.g. 'FFB300'.
- `app_dir` (string, required): Absolute path to the app project directory (contains app.spec.json), e.g. /Users/me/Apps/MyApp.
- `keyword`: App-themed search keyword override, e.g. 'happy dog celebration'.
- `primary_hex` (string, required): Primary palette color as hex, '#' optional, e.g. '006A63'.
- `slot` (string, required): Animation slot: 'loading', 'success', 'empty' or 'onboarding_hero'.

### `metadata_check` (~81 tokens)

Metadata Check

Validate an apple-metadata.md file: required fields and character limits (no writes).

Use when: checking a single metadata file. For the store listing.json use metadata_listing_check; for the
ASO outputs folder use aso_validate_metadata.
Returns: {ok, problems: [...], locales}.

Input parameters:

- `source_md` (string, required): Path to apple-metadata.md.

### `metadata_export` (~209 tokens)

Metadata Export

Export apple-metadata.md to fastlane/metadata/<locale>/*.txt after validation.

Use when: preparing metadata for deliver_metadata. With app_dir set, refuses until the ASO stage is
complete. Not for: uploading to App Store Connect (use deliver_metadata).
Returns: {ok, written: [files], locales} or a gate refusal.

Input parameters:

- `app_dir`: App directory; when given, export is refused until the ASO stage is complete.
- `dest_dir` (string, required): Output directory for the per-locale .txt files, e.g. <app>/fastlane/metadata.
- `primary_category`: Primary App Store category id, e.g. 'PHOTO_AND_VIDEO'.
- `privacy_url`: Public privacy policy URL written to the metadata files.
- `secondary_category`: Secondary App Store category id, e.g. 'PRODUCTIVITY'.
- `source_md` (string, required): Path to apple-metadata.md to export.
- `support_url`: Public support page URL.

### `aso_check_name` (~103 tokens)

ASO Check Name

Check whether an App Store app name is free, by exact-name collision in iTunes Search.

Use when: testing one name before asc_create_app. For several names use aso_find_available_name.
Returns: {ok, name, available, conflicts: [...]}.

Input parameters:

- `country` (string): Two-letter App Store storefront code, lowercase, e.g. 'us', 'gb', 'de'.
- `name` (string, required): Candidate App Store app name to check.

### `aso_find_available_name` (~108 tokens)

ASO Find Available Name

Pick the first available App Store name from a candidate list.

Use when: you have ordered name options; a taken name gets a submission rejected. For one name use
aso_check_name.
Returns: {ok, name, checked: [...]} or {ok: false} if none is free.

Input parameters:

- `candidates` (array, required): Ordered candidate app names.
- `country` (string): Two-letter App Store storefront code, lowercase, e.g. 'us', 'gb', 'de'.

### `aso_fetch_competitors` (~150 tokens)

ASO Fetch Competitors

Fetch competitor apps for a search term through iTunes Search (live, read-only).

Use when: sizing up a niche or getting trackIds for aso_competitor_iap. For a scored go/no-go use
aso_niche_score.
Returns: {ok, count, apps: [{trackId, name, ratings, price, ...}]} (untrusted_content).

Input parameters:

- `country` (string): Two-letter App Store storefront code, lowercase, e.g. 'us', 'gb', 'de'.
- `limit` (integer): Number of competitor apps to return (default 10).
- `term` (string, required): Search term or seed keyword, e.g. 'habit tracker'.

### `aso_top_grossing` (~157 tokens)

ASO Top Grossing

List the top-grossing apps of a storefront and optional category as a revenue proxy.

Use when: hunting proven ideas in one category. To sweep all categories and storefronts use idea_harvest. An
unknown genre returns ok:false, never the overall chart.
Returns: {ok, apps: [...]} (untrusted_content).

Input parameters:

- `country` (string): Two-letter App Store storefront code, lowercase, e.g. 'us', 'gb', 'de'.
- `genre`: App Store category name (e.g. 'photo_video', 'productivity', 'health') or numeric genre id; omit for all/inferred.
- `limit` (integer): Number of chart entries to return (default 25).

### `aso_search_hints` (~157 tokens)

ASO Search Hints

Query App Store autocomplete for real search demand; the idea-stage hard gate (0 suggestions for a real term
\= no demand, reject).

Use when: validating an idea or expanding keywords (expand=true appends a-z). An endpoint error returns
ok:false, never an empty list.
Returns: {ok, term, suggestions: [...], count} (untrusted_content).

Input parameters:

- `country` (string): Two-letter App Store storefront code, lowercase, e.g. 'us', 'gb', 'de'.
- `expand` (boolean): If true, append a-z to the seed to collect the full keyword universe; default false.
- `term` (string, required): Search term or seed keyword, e.g. 'habit tracker'.

### `aso_niche_score` (~183 tokens)

ASO Niche Score

Score an idea 0-100 from demand, competitor weakness, saturation and monetization, with a go/no-go verdict.

Use when: ranking one idea quickly. For the full evidence bundle use idea_evaluate. verdict: GO (>=60),
MAYBE (>=40), WEAK, REJECT (median competitor >50k ratings or no demand).
Returns: {ok, score, verdict, demand, competitors, ...} (untrusted_content).

Input parameters:

- `country` (string): Two-letter App Store storefront code, lowercase, e.g. 'us', 'gb', 'de'.
- `genre`: App Store category name (e.g. 'photo_video', 'productivity', 'health') or numeric genre id; omit for all/inferred.
- `term` (string, required): Search term or seed keyword, e.g. 'habit tracker'.

### `idea_harvest` (~284 tokens)

Idea Harvest

Sweep top-grossing and top-free charts across every App Store category and storefront to find proven and
rising ideas.

Use when: Phase 0 idea generation (about 1.5 minutes for the default sweep). Not for: evaluating one idea
(use idea_evaluate) or a single chart (use aso_top_grossing). Failed feeds are listed, never read as no
apps.
Returns: {ok, chart_proven, rising_newcomers, clusters: [{genre, open_niche}], failed_feeds}
(untrusted_content).

Input parameters:

- `countries`: List of two-letter storefront codes, e.g. ['us', 'gb', 'de']; omit for the default set.
- `exclude_terms`: Name/seller substrings to drop from results (e.g. your own apps).
- `feeds`: Chart feeds to use, e.g. ['topgrossing', 'topfree']; omit for both.
- `genres`: Category names to sweep, e.g. ['health', 'productivity']; omit for all 24 charted categories.
- `limit` (integer): Chart entries fetched per genre and storefront (default 100).
- `max_results` (integer): Maximum entries per result list (default 50).
- `newcomer_months` (integer): Apps released within this many months count as rising newcomers (default 12).

### `idea_evaluate` (~210 tokens)

Idea Evaluate

Build the evidence bundle to rank one idea in any category.

Use when: comparing shortlisted ideas after idea_harvest. Not for: a quick score only (use aso_niche_score).
Includes ai_needed and build_complexity heuristics for Claude to judge.
Returns: {ok, autocomplete, niche, newcomers, leaders, ai_needed, build_complexity, review_risk,
available_name} (untrusted_content).

Input parameters:

- `country` (string): Two-letter App Store storefront code, lowercase, e.g. 'us', 'gb', 'de'.
- `genre`: App Store category name (e.g. 'photo_video', 'productivity', 'health') or numeric genre id; omit for all/inferred.
- `leaders` (integer): Number of category leaders to profile with price ladders (default 3).
- `name_candidates`: Candidate app names; the first available one is reported.
- `term` (string, required): Search term or seed keyword, e.g. 'habit tracker'.

### `aso_competitor_iap` (~156 tokens)

ASO Competitor IAP

Fetch a competitor's subscription/IAP price ladder from its App Store product page.

Use when: setting our pricing. app_id is the iTunes trackId from aso_fetch_competitors. Fragile,
undocumented source: on failure ok:false and prices null mean unknown, not free.
Returns: {ok, prices: [{name, price}] | null} (untrusted_content).

Input parameters:

- `app_id` (string, required): App Store Connect app id (numeric Apple id, e.g. '1234567890'); for aso_competitor_iap the iTunes trackId.
- `country` (string): Two-letter App Store storefront code, lowercase, e.g. 'us', 'gb', 'de'.

### `aso_unit_economics` (~228 tokens)

ASO Unit Economics

Compute margin, break-even and warnings from prices, credits and AI cost (pure calculation, no network).

Use when: idea/scaffold stage pricing decisions. Not for: measured-cost economics for a built app (use
pricing_unit_economics). apple_cut is 0.15 for Small Business, else 0.30.
Returns: {ok, weekly, yearly, margins, break_even, warnings}.

Input parameters:

- `ai_cost_per_credit` (number): AI provider cost per credit in USD (default 0.003).
- `apple_cut` (number): Apple's commission as a fraction: 0.15 (Small Business Program, default) or 0.30.
- `weekly_credits` (integer): Credits per week on the weekly plan (default 10).
- `weekly_price` (number): Weekly subscription price in USD (default 7.99).
- `yearly_monthly_credits` (integer): Credits per month on the yearly plan (default 15).
- `yearly_price` (number): Yearly subscription price in USD (default 49.99).

### `aso_scaffold_outputs` (~118 tokens)

ASO Scaffold Outputs

Create the outputs/<App>/ ASO skeleton including apple-metadata.md in 32 languages.

Use when: standalone ASO work. Inside the pipeline use aso_run, which also returns the skill instructions.
Returns: {ok, dir, files}.

Input parameters:

- `app_name` (string, required): App name; creates outputs/<app_name>/.
- `base_dir` (string, required): Directory that contains (or will contain) the outputs/<App>/ folder.
- `locales`: Locale codes for the metadata skeleton; omit for the 32 default languages.

### `aso_validate_metadata` (~116 tokens)

ASO Validate Metadata

Validate outputs/<App>/02-metadata/apple-metadata.md (fields and character limits).

Use when: checking ASO output by app name and base dir. For an arbitrary file use metadata_check; to mark
the stage done use aso_complete.
Returns: {ok, problems: [...]}.

Input parameters:

- `app_name` (string, required): App name whose outputs/<app_name>/02-metadata/apple-metadata.md is validated.
- `base_dir` (string, required): Directory that contains (or will contain) the outputs/<App>/ folder.

### `aso_run` (~80 tokens)

ASO Run

Start the mandatory ASO stage: create the outputs skeleton and return the skill instructions.

Use when: the pipeline reaches ASO. Finish with aso_complete.
Returns: {ok, outputs_dir, instructions}.

Input parameters:

- `app_dir` (string, required): Absolute path to the app project directory (contains app.spec.json), e.g. /Users/me/Apps/MyApp.

### `aso_complete` (~96 tokens)

ASO Complete

Validate the ASO output and mark the 'aso' stage done, opening the metadata/deliver gate.

Use when: ASO files are written. Not for: other stages (use pipeline_mark).
Returns: {ok, problems?} or a refusal listing what is missing.

Input parameters:

- `app_dir` (string, required): Absolute path to the app project directory (contains app.spec.json), e.g. /Users/me/Apps/MyApp.

### `icon_install` (~148 tokens)

Icon Install

Install the Claude Design app icon (1024x1024 master) into AppIcon.appiconset and write
.appfactory/verify/icon.json.

Use when: after design_export_png produced design/icon.png and design_record_upload recorded the upload; the
icon gate requires it. Alpha is flattened.
Returns: {ok, icon, marker} or an error naming the missing/invalid master.

Input parameters:

- `app_dir` (string, required): Absolute path to the app project directory (contains app.spec.json), e.g. /Users/me/Apps/MyApp.
- `source` (string): Path to the 1024x1024 icon master, relative to the app dir (default 'design/icon.png').

### `icon_generate` (~184 tokens)

Icon Generate

Generate a fal.ai raster icon DRAFT into design/icon_drafts/ as reference for the Claude Design icon board
(paid, opt-in only).

Use when: the user explicitly allows an external generator. Never installs an icon; the shipped icon comes
from Claude Design (icon_install).
Returns: {ok, draft, prompt, next} or a refusal unless allow_external_generator is true.

Input parameters:

- `allow_external_generator` (boolean): Must be true to allow the paid fal.ai image generator; default false refuses.
- `app_dir` (string, required): Absolute path to the app project directory (contains app.spec.json), e.g. /Users/me/Apps/MyApp.
- `concept` (string, required): Short concept description of the app used to write the image prompt, e.g. 'AI pet portrait maker'.
- `extra` (string): Extra prompt text appended to the concept.

### `localize_apply` (~121 tokens)

Localize Apply

Merge translations into the in-app String Catalog for the spec's app locales.

Use when: after translating UI strings; locales not in spec locales.app are ignored. Not for: store listing
copy (use metadata_render_listing / deliver_metadata).
Returns: {ok, locales, keys, missing}.

Input parameters:

- `app_dir` (string, required): Absolute path to the app project directory (contains app.spec.json), e.g. /Users/me/Apps/MyApp.
- `translations` (object, required): Map {english_key: {locale: translated value}} for the String Catalog.

### `pipeline_status` (~95 tokens)

Pipeline Status

Show which pipeline stages are done or pending and what comes next.

Use when: checking progress of one app. To get the next instructions use pipeline_next; for the autonomous
loop use orchestrator_next_action.
Returns: {ok, stages: {stage: status}, next}.

Input parameters:

- `app_dir` (string, required): Absolute path to the app project directory (contains app.spec.json), e.g. /Users/me/Apps/MyApp.

### `pipeline_next` (~140 tokens)

Pipeline Next

Return the next mandatory pipeline step with its instructions.

Use when: driving the pipeline manually. Refuses until run options are confirmed (run_options,
run_options_save) unless skip_options_check. Not for: the autonomous loop with retry budgets (use
orchestrator_next_action).
Returns: {ok, stage, instructions} or setup_required / options-not-confirmed errors.

Input parameters:

- `app_dir` (string, required): Absolute path to the app project directory (contains app.spec.json), e.g. /Users/me/Apps/MyApp.
- `skip_options_check` (boolean): If true, do not require run_options_save to have been called first. Default false.

### `pipeline_mark` (~150 tokens)

Pipeline Mark

Set a pipeline stage's status (done, in_progress, pending) in the app manifest.

Use when: recording progress. Marking done runs the stage's enforced gate. To only test the gate use
pipeline_validate.
Returns: {ok, stage, status} or a gate refusal.

Input parameters:

- `app_dir` (string, required): Absolute path to the app project directory (contains app.spec.json), e.g. /Users/me/Apps/MyApp.
- `stage` (string, required): Pipeline stage id, e.g. 'aso', 'screenshots', 'features' (see pipeline_status for the list).
- `status` (string): New stage status: 'done', 'in_progress' or 'pending'.

### `pipeline_validate` (~107 tokens)

Pipeline Validate

Run a stage's enforced gate without modifying the manifest.

Use when: self-checking before pipeline_mark(done). Read-only.
Returns: {ok, stage, problems: [...]}.

Input parameters:

- `app_dir` (string, required): Absolute path to the app project directory (contains app.spec.json), e.g. /Users/me/Apps/MyApp.
- `stage` (string, required): Pipeline stage id, e.g. 'aso', 'screenshots', 'features' (see pipeline_status for the list).

### `orchestrator_preflight` (~122 tokens)

Orchestrator Preflight

Pre-flight for an autonomous run: run options confirmed, config keys present, fastlane session fresh,
caffeinate command.

Use when: before starting the orchestrator_next_action loop. Ready when blockers is empty. Returns
setup_required first if AppFactory is not set up.
Returns: {ok, blockers: [...], caffeinate, ...}.

Input parameters:

- `app_dir`: App directory; omit before the app is scaffolded.
- `skip_options_check` (boolean): If true, do not require run_options_save to have been called first. Default false.

### `orchestrator_next_action` (~132 tokens)

Orchestrator Next Action

Return the next action for the autonomous driver loop: stage, subagent role, retry budget and instructions.

Use when: running unattended. done=true means the pipeline is finished (submit still needs human approval).
For a manual step use pipeline_next.
Returns: {ok, done, stage, role, attempts_left, instructions}.

Input parameters:

- `app_dir` (string, required): Absolute path to the app project directory (contains app.spec.json), e.g. /Users/me/Apps/MyApp.
- `skip_options_check` (boolean): If true, do not require run_options_save to have been called first. Default false.

### `run_options` (~107 tokens)

Run Options

List every optional part of a run with its question, kind, choices, default and current value.

Use when: BEFORE any other work when the user says run. Ask the user each question one at a time (or as a
checklist), then call run_options_save.
Returns: {ok, options: [{id, question, kind, choices, default, value}], confirmed}.

Input parameters:

- `app_dir`: App directory to read current values from; omit before the app is scaffolded.

### `run_options_save` (~126 tokens)

Run Options Save

Save the user's answers to the run options and mark options confirmed.

Use when: after asking every question from run_options. Validates types, choices and dependencies; services
go to config.toml, the rest to app.spec.json (or a pending file that app_scaffold applies).
Returns: {ok, saved, errors?}.

Input parameters:

- `answers` (object, required): Map {option id: value} covering every option id returned by run_options.
- `app_dir`: App directory whose app.spec.json receives the answers; omit to save to the pending file used by app_scaffold.

### `orchestrator_record_attempt` (~123 tokens)

Orchestrator Record Attempt

Count one failed attempt of a stage after a gate failure.

Use when: a stage gate failed in the autonomous loop; escalate with orchestrator_needs_human when
should_retry is false.
Returns: {ok, stage, attempts, should_retry}.

Input parameters:

- `app_dir` (string, required): Absolute path to the app project directory (contains app.spec.json), e.g. /Users/me/Apps/MyApp.
- `stage` (string, required): Pipeline stage id, e.g. 'aso', 'screenshots', 'features' (see pipeline_status for the list).

### `orchestrator_needs_human` (~155 tokens)

Orchestrator Needs Human

Write NEEDS_HUMAN.md when self-correction is exhausted and return its path.

Use when: a stage keeps failing and only the human can unblock it (stop the loop afterwards).
Returns: {ok, path}.

Input parameters:

- `app_dir` (string, required): Absolute path to the app project directory (contains app.spec.json), e.g. /Users/me/Apps/MyApp.
- `how_to_resolve` (string, required): What the human needs to do to unblock the stage.
- `reason` (string, required): Why self-correction is exhausted (what failed).
- `stage` (string, required): Pipeline stage id, e.g. 'aso', 'screenshots', 'features' (see pipeline_status for the list).

### `screenshot_capture` (~148 tokens)

Screenshot Capture

Capture a raw screenshot from the booted simulator into marketing/raw/<locale>/<name>.png.

Use when: capturing a single screen manually. For the full localized set use screenshot_build_all.
Returns: {ok, path}.

Input parameters:

- `locale` (string, required): App locale of the capture, e.g. 'en-US'; used as the output subfolder.
- `marketing_dir` (string, required): Path to the app's marketing/ directory.
- `name` (string, required): Screen name, used as the file name, e.g. 'paywall'.
- `udid` (string, required): Simulator UDID from build_list_simulators, e.g. 'A1B2C3D4-...'.

### `screenshot_brand` (~71 tokens)

Screenshot Brand

Render branded App Store screenshots with the node compositor using the Claude Design store layout.

Use when: raw captures exist; run screenshot_apply_layout first (screenshot_build_all does both).
Returns: {ok, rendered: count, dir}.

Input parameters:

- `marketing_dir` (string, required): Path to the app's marketing/ directory.

### `screenshot_apply_layout` (~85 tokens)

Screenshot Apply Layout

Merge the Claude Design store layout (design/project/store_layout.json) into
marketing/screenshots/config.json.

Use when: before screenshot_brand so every locale renders the approved layout.
Returns: {ok, config path, boards}.

Input parameters:

- `app_dir` (string, required): Absolute path to the app project directory (contains app.spec.json), e.g. /Users/me/Apps/MyApp.

### `screenshot_sync` (~89 tokens)

Screenshot Sync

Copy branded screenshots into fastlane/screenshots/<locale>/.

Use when: after screenshot_brand, before deliver_screenshots. Local only; nothing is uploaded.
Returns: {ok, synced: count}.

Input parameters:

- `fastlane_screenshots_dir` (string, required): Path to fastlane/screenshots/ to sync branded images into.
- `marketing_dir` (string, required): Path to the app's marketing/ directory.

### `screenshot_build_all` (~275 tokens)

Screenshot Build All

Run the whole turnkey screenshot step: sample image, build and install, captions, capture 32 languages x 6
screens, brand, sync to fastlane/screenshots.

Use when: the mandatory screenshots stage. Not for: single captures (use screenshot_capture) or uploading
(use deliver_screenshots). Long-running.
Returns: {ok, locales, screens, dir} or the first failing step.

Input parameters:

- `app_dir` (string, required): Absolute path to the app project directory (contains app.spec.json), e.g. /Users/me/Apps/MyApp.
- `bundle_id` (string, required): App bundle identifier in reverse-DNS form, e.g. com.example.app.
- `locales`: List of locale codes, e.g. ['en-US', 'de-DE']; omit for the default set.
- `project` (string, required): Path to the .xcodeproj or .xcworkspace to build, e.g. /path/App/App.xcodeproj.
- `sample_prompt` (string, required): Prompt describing a sample output of the app (fills Result/Gallery screens).
- `scheme` (string, required): Xcode scheme name to build, e.g. 'MyApp'.
- `udid` (string, required): Simulator UDID from build_list_simulators, e.g. 'A1B2C3D4-...'.

### `screenshot_generate_sample` (~103 tokens)

Screenshot Generate Sample

Generate a sample image with fal.ai into Resources/sample_headshot.jpg to fill the Result/Gallery screens
(paid).

Use when: screenshots need real-looking app output; screenshot_build_all calls it for you.
Returns: {ok, path}.

Input parameters:

- `app_dir` (string, required): Absolute path to the app project directory (contains app.spec.json), e.g. /Users/me/Apps/MyApp.
- `prompt` (string, required): Image generation prompt for the sample output.

### `screenshot_onboarding_heroes` (~175 tokens)

Screenshot Onboarding Heroes

LEGACY, opt-in: generate fal hero images per onboarding step (onb_step0..N).

Use when: only for a legacy hero-image onboarding. Onboarding visuals normally come from Claude Design
boards. Requires allow_external_generator=true.
Returns: {ok, images: [paths]} or a refusal.

Input parameters:

- `allow_external_generator` (boolean): Must be true to allow the paid fal.ai image generator; default false refuses.
- `app_dir` (string, required): Absolute path to the app project directory (contains app.spec.json), e.g. /Users/me/Apps/MyApp.
- `concept` (string, required): Short concept description of the app used to write the image prompt, e.g. 'AI pet portrait maker'.
- `count` (integer): Number of onboarding hero images to generate (default 11).

### `growth_build_slideshows` (~162 tokens)

Growth Build Slideshows

Render viral TikTok/Reels slideshows (1080x1920 PNGs) to marketing/growth/<name>/NN.png.

Use when: post-launch content only, after the app is submitted. You write the hooks and slide text; images
come from the app's AI (fal). No scheduling.
Returns: {ok, sets: [{name, slides, ok, dir}]}.

Input parameters:

- `app_dir` (string, required): Absolute path to the app project directory (contains app.spec.json), e.g. /Users/me/Apps/MyApp.
- `slideshows` (array, required): List of {name, slides: [{text, image_prompt or image, cta}]} to render at 1080x1920.

### `preview_brief` (~148 tokens)

Preview Brief

Write marketing/preview/BRIEF.md (the HyperFrames App Preview brief) from app.spec.json and create the
recordings folders.

Use when: starting the App Preview video. Then record the real app and author the video with the hyperframes
skill (real footage only). Check with preview_check.
Returns: {ok, brief path, recordings dirs}.

Input parameters:

- `app_dir` (string, required): Absolute path to the app project directory (contains app.spec.json), e.g. /Users/me/Apps/MyApp.
- `message` (string): Optional extra direction appended to the brief.
- `overwrite` (boolean): If true, replace files that already exist; default false refuses to overwrite.

### `preview_check` (~116 tokens)

Preview Check

Check offline readiness of the App Preview set: brief, recordings, one preview per locale, ffprobe specs,
self-review status.

Use when: before preview_upload. Files must be 886x1920, <=30 fps, H.264, 15-30 s, <=500 MB, stereo AAC or
silent.
Returns: {ok, problems: [...], plan}.

Input parameters:

- `app_dir` (string, required): Absolute path to the app project directory (contains app.spec.json), e.g. /Users/me/Apps/MyApp.

### `preview_review_sheets` (~101 tokens)

Preview Review Sheets

Generate self-review material (contact sheet, phone-size sheet, transition strip) for every final preview
with ffmpeg.

Use when: reviewing a rendered preview; open the sheets, score them, then call preview_review_log.
Returns: {ok, sheets: {locale: [paths]}}.

Input parameters:

- `app_dir` (string, required): Absolute path to the app project directory (contains app.spec.json), e.g. /Users/me/Apps/MyApp.

### `preview_review_log` (~194 tokens)

Preview Review Log

Log one self-review round (scores 1-10 and worst problems) for a final preview file.

Use when: after preview_review_sheets. Every score must reach 8+ on the exact final file (MD5-matched) or
preview_upload and the gate refuse.
Returns: {ok, passed, scores, problems}.

Input parameters:

- `app_dir` (string, required): Absolute path to the app project directory (contains app.spec.json), e.g. /Users/me/Apps/MyApp.
- `file` (string, required): Preview file path relative to the app dir, e.g. 'fastlane/app_previews/en-US/preview.mp4'.
- `problems`: Up to 3 worst problems as [{t: seconds, issue: text}]; required while any score is under 8.
- `scores` (object, required): Scores 1-10 for keys hook, readability, motion, variety, brand, music.

### `preview_upload` (~242 tokens)

Preview Upload

Upload fastlane/app_previews/<locale>/ to the editable version's iPhone preview sets via the asc CLI (needs
human approval unless dry_run).

Use when: preview_check is clean and self-review passed. dry_run=true (default) sends nothing.
MD5-idempotent; refuses while preview_check has problems.
Returns: {ok, uploaded/planned: [...], dry_run} or an approval_required refusal.

Input parameters:

- `app_dir` (string, required): Absolute path to the app project directory (contains app.spec.json), e.g. /Users/me/Apps/MyApp.
- `approval_id`: Approval id from a previous approval_required refusal; omit on the first call. The human approves out-of-band with `appfactory approve <id>`, then call again with the same arguments and this id.
- `dry_run` (boolean): If true (default) nothing is changed or sent and the exact plan/command is returned; false performs the live action.
- `locales`: Locale codes to upload; omit for every locale in fastlane/app_previews/.
- `replace` (boolean): If true, replace preview sets that already exist on App Store Connect.

### `cpp_build_all` (~177 tokens)

CPP Build All

Create one Custom Product Page per Search Ads theme (page, version, localization) and return the deep-link
URLs (live write, needs human approval).

Use when: after ASO produced theme clusters; the URLs feed Ad Ops.
Returns: {ok, pages: [{theme, ok, id, version_id, localization_id, url}]} or an approval_required refusal.

Input parameters:

- `approval_id`: Approval id from a previous approval_required refusal; omit on the first call. The human approves out-of-band with `appfactory approve <id>`, then call again with the same arguments and this id.
- `bundle_id` (string, required): App bundle identifier in reverse-DNS form, e.g. com.example.app.
- `themes` (array, required): List of {name, locale} Search Ads themes, one Custom Product Page each.

### `deliver_metadata` (~182 tokens)

Deliver Metadata

Upload metadata to the App Store Connect draft version through the API (live write, needs human approval,
ASO-gated).

Use when: metadata files are exported (metadata_export). Not for: screenshots (deliver_screenshots) or the
submit step (asc_submit_for_review).
Returns: {ok, uploaded: [...]} or a gate / approval_required refusal.

Input parameters:

- `app_dir` (string, required): Absolute path to the app project directory (contains app.spec.json), e.g. /Users/me/Apps/MyApp.
- `approval_id`: Approval id from a previous approval_required refusal; omit on the first call. The human approves out-of-band with `appfactory approve <id>`, then call again with the same arguments and this id.
- `bundle_id` (string, required): App bundle identifier in reverse-DNS form, e.g. com.example.app.

### `deliver_screenshots` (~180 tokens)

Deliver Screenshots

Upload screenshots to the App Store Connect draft with checksum-based incremental sync (live write, needs
human approval).

Use when: screenshot_build_all is done; unchanged files (MD5 match) are skipped. To verify afterwards use
deliver_screenshots_audit.
Returns: {ok, uploaded, skipped} or a gate / approval_required refusal.

Input parameters:

- `app_dir` (string, required): Absolute path to the app project directory (contains app.spec.json), e.g. /Users/me/Apps/MyApp.
- `approval_id`: Approval id from a previous approval_required refusal; omit on the first call. The human approves out-of-band with `appfactory approve <id>`, then call again with the same arguments and this id.
- `bundle_id` (string, required): App bundle identifier in reverse-DNS form, e.g. com.example.app.

### `deliver_screenshots_audit` (~128 tokens)

Deliver Screenshots Audit

Read-only audit that App Store Connect screenshot sets exactly match the local fastlane/screenshots files
(order, COMPLETE state, MD5).

Use when: after deliver_screenshots and before submission. No writes.
Returns: {ok, locales: {locale: {display_type: status}}, previews, problems}.

Input parameters:

- `app_dir` (string, required): Absolute path to the app project directory (contains app.spec.json), e.g. /Users/me/Apps/MyApp.
- `bundle_id` (string, required): App bundle identifier in reverse-DNS form, e.g. com.example.app.

### `deliver_subscription_review_screenshots` (~177 tokens)

Deliver Subscription Review Screenshots

Upload the App Review screenshot of every subscription from store/review-screenshots/<product key>.png (live
write, needs human approval).

Use when: subscriptions sit in MISSING_METADATA for the review screenshot. Idempotent by MD5.
Returns: {ok, uploaded, skipped} or an approval_required refusal.

Input parameters:

- `app_dir` (string, required): Absolute path to the app project directory (contains app.spec.json), e.g. /Users/me/Apps/MyApp.
- `approval_id`: Approval id from a previous approval_required refusal; omit on the first call. The human approves out-of-band with `appfactory approve <id>`, then call again with the same arguments and this id.
- `bundle_id` (string, required): App bundle identifier in reverse-DNS form, e.g. com.example.app.

### `testflight_ship` (~275 tokens)

TestFlight Ship

Build, sign and upload a TestFlight build end to end: distribution signing, archive, App Store profiles,
manual-signing export, asc builds upload (needs human approval).

Use when: shipping a build to TestFlight. Refuses unless the Maestro smoke flows passed (maestro_test) and
App ID capabilities exist. Never submits for App Store review (use asc_submit_for_review for that). Not for:
single build steps (build_archive, build_export_ipa, signing_*).
Returns: {ok, build, ...} or an error / approval_required refusal.

Input parameters:

- `app_dir` (string, required): Absolute path to the app project directory (contains app.spec.json), e.g. /Users/me/Apps/MyApp.
- `approval_id`: Approval id from a previous approval_required refusal; omit on the first call. The human approves out-of-band with `appfactory approve <id>`, then call again with the same arguments and this id.
- `bundle_id` (string, required): App bundle identifier in reverse-DNS form, e.g. com.example.app.
- `project` (string, required): Path to the .xcodeproj or .xcworkspace to build, e.g. /path/App/App.xcodeproj.
- `scheme` (string, required): Xcode scheme name to build, e.g. 'MyApp'.

### `signing_setup_distribution` (~109 tokens)

Signing Setup Distribution

Create a distribution certificate and install it into a temporary keychain (WWDR included; live write, needs
human approval).

Use when: hand-running signing steps; testflight_ship does this for you.
Returns: {ok, cert_id, identity, keychain}.

Input parameters:

- `approval_id`: Approval id from a previous approval_required refusal; omit on the first call. The human approves out-of-band with `appfactory approve <id>`, then call again with the same arguments and this id.

### `signing_create_profile` (~164 tokens)

Signing Create Profile

Create an IOS_APP_STORE provisioning profile and write it to the standard locations (live write, needs human
approval).

Use when: after signing_setup_distribution; testflight_ship does this for you.
Returns: {ok, profile_id, name, path}.

Input parameters:

- `approval_id`: Approval id from a previous approval_required refusal; omit on the first call. The human approves out-of-band with `appfactory approve <id>`, then call again with the same arguments and this id.
- `bundle_id` (string, required): App bundle identifier in reverse-DNS form, e.g. com.example.app.
- `cert_id` (string, required): App Store Connect certificate id from signing_setup_distribution.
- `name` (string): Provisioning profile name (default 'AppFactory AppStore').

### `ai_configure` (~78 tokens)

AI Configure

Report the AI provider/model selection (currently fal.ai Flux schnell); does not change anything.

Use when: checking which image model the backend will use. Keys are set with ai_deploy_proxy or
backend_deploy.
Returns: {ok, providers, models, note}.

Input parameters:

- `providers`: AI provider ids; default ['fal.ai'].

### `ai_deploy_proxy` (~201 tokens)

AI Deploy Proxy

Deploy the AI backend: the full spec-driven backend in subscription mode, or the legacy ai-proxy in credits
mode (live, needs human approval).

Use when: the app has an AI feature. AI keys go only to server-side secrets. For explicit dry-run planning
use backend_deploy.
Returns: {ok, deployed: [...]} or an approval_required refusal.

Input parameters:

- `app_dir` (string, required): Absolute path to the app project directory (contains app.spec.json), e.g. /Users/me/Apps/MyApp.
- `approval_id`: Approval id from a previous approval_required refusal; omit on the first call. The human approves out-of-band with `appfactory approve <id>`, then call again with the same arguments and this id.
- `daily_limit` (integer): Per-user daily request cap for the legacy ai-proxy (default 20).
- `project_ref` (string, required): Supabase project ref to deploy to.

### `revenuecat_setup` (~269 tokens)

RevenueCat Setup

Idempotently set up the RevenueCat v2 project: premium entitlement, subscription attachments, SDK key and
Supabase secret (live write, needs human approval).

Use when: after a human created the RC project, linked ASC and supplied the v2 key. Returns NEEDS_HUMAN if
no project exists. For ASC plus RC in one pass use store_setup.
Returns: {ok, entitlement, sdk_key_set, secrets} or an approval_required refusal.

Input parameters:

- `approval_id`: Approval id from a previous approval_required refusal; omit on the first call. The human approves out-of-band with `appfactory approve <id>`, then call again with the same arguments and this id.
- `bundle_id` (string, required): App bundle identifier in reverse-DNS form, e.g. com.example.app.
- `env_suffix` (string): Suffix for secret names when several apps share one Supabase project, e.g. '_MYAPP'.
- `set_secrets` (boolean): If true (default) also write the RevenueCat secrets to Supabase.
- `supabase_ref` (string, required): Supabase project ref that receives the RevenueCat secrets.
- `v2_key` (string, required): RevenueCat secret API v2 key (sk_...); used for this call only.

### `backend_render` (~107 tokens)

Backend Render

Render the scaffolded backend from app.spec.json (offline, idempotent): prune supabase/ to the monetization
mode, generate shared config, legal sources and listing locales.

Use when: after editing the spec, before backend_deploy. Nothing is deployed.
Returns: {ok, mode, render, legal, listing?}.

Input parameters:

- `app_dir` (string, required): Absolute path to the app project directory (contains app.spec.json), e.g. /Users/me/Apps/MyApp.

### `backend_deploy` (~222 tokens)

Backend Deploy

Deploy the spec's Supabase backend: migrations, secrets, auth, legal build and edge functions (live unless
dry_run; needs human approval).

Use when: after backend_render. dry_run=true (default) returns the plan without any call. Not for: single
SQL (supabase_run_sql) or one secret (supabase_set_secret).
Returns: {ok, plan/steps, dry_run} or an approval_required refusal.

Input parameters:

- `app_dir` (string, required): Absolute path to the app project directory (contains app.spec.json), e.g. /Users/me/Apps/MyApp.
- `approval_id`: Approval id from a previous approval_required refusal; omit on the first call. The human approves out-of-band with `appfactory approve <id>`, then call again with the same arguments and this id.
- `dry_run` (boolean): If true (default) nothing is changed or sent and the exact plan/command is returned; false performs the live action.
- `project_ref` (string, required): Supabase project ref to deploy to.

### `legal_render` (~109 tokens)

Legal Render

Fill the legal sources (store/privacy/*.md, backend/PRIVACY.md) from the spec and config, keeping or
dropping the HealthKit block.

Use when: before legal_check. Local writes only.
Returns: {ok, files, placeholders_left}.

Input parameters:

- `app_dir` (string, required): Absolute path to the app project directory (contains app.spec.json), e.g. /Users/me/Apps/MyApp.
- `values`: Map of placeholder name to text for product-specific legal placeholders.

### `legal_check` (~86 tokens)

Legal Check

List what still blocks publishing the legal pages: placeholders, unrendered blocks, missing languages.

Use when: after legal_render, offline. For checking the live pages use legal_verify.
Returns: {ok, problems: [...]}.

Input parameters:

- `app_dir` (string, required): Absolute path to the app project directory (contains app.spec.json), e.g. /Users/me/Apps/MyApp.

### `legal_verify` (~109 tokens)

Legal Verify

Check that every deployed privacy/terms page answers 200 in each app language with the support email and no
placeholder (live, read-only).

Use when: after backend_deploy. Needs supabase_url in app outputs. For offline checks use legal_check.
Returns: {ok, pages: [{locale, url, status, problems}]}.

Input parameters:

- `app_dir` (string, required): Absolute path to the app project directory (contains app.spec.json), e.g. /Users/me/Apps/MyApp.

### `store_setup` (~309 tokens)

Store Setup

Idempotent App Store Connect and RevenueCat setup from app.spec.json and listing.json (capabilities,
subscriptions, prices, offers, age rating, legal URLs, RC entitlement/offerings).

Use when: the standard way to set up the store side. mode: plan (offline), check (live reads, simulated
writes, reports CONFLICT), apply (live writes, needs human approval). Prefer this over the individual asc_*
write tools; asc_create_app is still separate.
Returns: {ok, mode, steps: [...], conflicts} or an approval_required refusal.

Input parameters:

- `app_dir` (string, required): Absolute path to the app project directory (contains app.spec.json), e.g. /Users/me/Apps/MyApp.
- `approval_id`: Approval id from a previous approval_required refusal; omit on the first call. The human approves out-of-band with `appfactory approve <id>`, then call again with the same arguments and this id.
- `mode` (string): 'plan' (offline), 'check' (live reads, simulated writes, reports CONFLICT) or 'apply' (live writes, needs approval).
- `rc_apple_notification_url`: RevenueCat's Apple Server-to-Server notification URL, stored in app outputs.
- `rc_project_id`: RevenueCat project id (proj...) if it already exists.
- `target` (string): What to set up: 'capabilities', 'asc', 'rc' or 'all' (default).

### `metadata_listing_check` (~93 tokens)

Metadata Listing Check

Validate store/metadata/listing.json: length limits, keyword rules, no word overlap, subscription disclosure
and legal links.

Use when: before deliver_metadata. For a bare apple-metadata.md use metadata_check.
Returns: {ok, problems: [...]}.

Input parameters:

- `app_dir` (string, required): Absolute path to the app project directory (contains app.spec.json), e.g. /Users/me/Apps/MyApp.

### `metadata_render_listing` (~94 tokens)

Metadata Render Listing

Sync listing.json to the spec (store locales, IAP copy slots) and fill the legal URLs.

Use when: after changing the spec's store locales or products. Local writes only; verify with
metadata_listing_check.
Returns: {ok, locales, changed}.

Input parameters:

- `app_dir` (string, required): Absolute path to the app project directory (contains app.spec.json), e.g. /Users/me/Apps/MyApp.

### `pricing_unit_economics` (~188 tokens)

Pricing Unit Economics

Compute unit economics from the measured AI cost per call and usage profiles, and write the generated blocks
of store/pricing.md.

Use when: pricing a built app with real eval data. For quick what-if numbers at idea stage use
aso_unit_economics.
Returns: {ok, products: [{margin, ...}], path}.

Input parameters:

- `app_dir` (string, required): Absolute path to the app project directory (contains app.spec.json), e.g. /Users/me/Apps/MyApp.
- `apple_cut` (number): Apple's commission as a fraction: 0.15 (Small Business Program, default) or 0.30.
- `cost_photo`: Measured AI cost in USD per photo call; omit to read from backend/eval/results.
- `cost_text`: Measured AI cost in USD per text call; omit to read from backend/eval/results.

### `asc_sbp_check` (~141 tokens)

ASC SBP Check

Prove Small Business Program status from the latest subscription sales report (US proceeds/price ratio,
about 0.85 SBP vs 0.70 standard; live, read-only).

Use when: confirming the 15% commission. Needs a Finance-role key (asc_finance_key_id,
asc_finance_key_filepath, asc_vendor_number); a 403 gives a clear error.
Returns: {ok, ratio, program, report_date}.

Input parameters:

- `days_back` (integer): How many days back to search for the latest report (default 7).
- `report_date`: Sales report date YYYY-MM-DD; omit to use the latest available.

### `storekit_generate` (~100 tokens)

StoreKit Generate

Write Resources/Configuration.storekit from app.spec.json (group, levels, free-trial intros, trial-less
offer product).

Use when: after any product change. Deterministic. app_sync_spec also regenerates it together with Swift
sources.
Returns: {ok, path, products}.

Input parameters:

- `app_dir` (string, required): Absolute path to the app project directory (contains app.spec.json), e.g. /Users/me/Apps/MyApp.

### `storekit_parity` (~108 tokens)

StoreKit Parity

Compare a Configuration.storekit with app.spec.json and list every mismatch (read-only).

Use when: verifying products match before store_setup or release. ok=true means in parity.
Returns: {ok, mismatches: [...]}.

Input parameters:

- `app_dir` (string, required): Absolute path to the app project directory (contains app.spec.json), e.g. /Users/me/Apps/MyApp.
- `storekit_path`: Path to a Configuration.storekit; omit for Resources/Configuration.storekit.

### `app_sync_spec` (~85 tokens)

App Sync Spec

Regenerate AppSpec.swift, PaywallSource.swift and Configuration.storekit and prune String Catalogs to
locales.app.

Use when: after editing app.spec.json. Local writes only.
Returns: {ok, files}.

Input parameters:

- `app_dir` (string, required): Absolute path to the app project directory (contains app.spec.json), e.g. /Users/me/Apps/MyApp.

### `design_screens_skeleton` (~158 tokens)

Design Screens Skeleton

Produce the structural skeleton for design/screens.json, following the design brief's onboarding flow when
one exists.

Use when: starting design. Adapt every screen to the app; the look is authored in Claude Design. write=true
saves it (refuses to replace an existing file unless overwrite).
Returns: {ok, screens|path, onboarding, main}.

Input parameters:

- `app_dir` (string, required): Absolute path to the app project directory (contains app.spec.json), e.g. /Users/me/Apps/MyApp.
- `overwrite` (boolean): If true, replace files that already exist; default false refuses to overwrite.
- `write` (boolean): If true, save the skeleton to design/screens.json; default false only returns it.

### `design_research_collect` (~204 tokens)

Design Research Collect

Download category-leader App Store screenshots and icons into design/research/apps/ with references.json
(study material only).

Use when: first design step. Then write the brief (design_research_brief_template) and run
design_research_check.
Returns: {ok, apps: [{id, name, files}], references} (untrusted_content).

Input parameters:

- `app_dir` (string, required): Absolute path to the app project directory (contains app.spec.json), e.g. /Users/me/Apps/MyApp.
- `countries`: List of two-letter storefront codes, e.g. ['us', 'gb', 'de']; omit for the default set.
- `max_apps` (integer): Maximum reference apps to download (default 16).
- `screenshots_per_app` (integer): Screenshots to download per reference app (default 6).
- `terms` (array, required): Search terms describing the app category, e.g. ['meditation', 'sleep sounds'].

### `design_research_brief_template` (~90 tokens)

Design Research Brief Template

Return the design/research/brief.json shape pre-filled with the reference ids.

Use when: authoring the design brief after design_research_collect. Read-only.
Returns: {ok, template, write_to: [paths]}.

Input parameters:

- `app_dir` (string, required): Absolute path to the app project directory (contains app.spec.json), e.g. /Users/me/Apps/MyApp.

### `design_research_check` (~83 tokens)

Design Research Check

Run the design_research gate: at least 6 reference apps on disk and a valid, cited brief.

Use when: before design_generate. Read-only.
Returns: {ok, problems: [...]}.

Input parameters:

- `app_dir` (string, required): Absolute path to the app project directory (contains app.spec.json), e.g. /Users/me/Apps/MyApp.

### `design_generate` (~111 tokens)

Design Generate

Prepare the app's Claude Design project from the brief, spec and screens.json: scaffolds, mascot boards,
canvas, store layout and upload plan.

Use when: after design_research_check passes. Never uploads; it returns the claude-design MCP calls to make.
Then record with design_record_upload.
Returns: {ok, plan, mcp_calls}.

Input parameters:

- `app_dir` (string, required): Absolute path to the app project directory (contains app.spec.json), e.g. /Users/me/Apps/MyApp.

### `design_record_upload` (~142 tokens)

Design Record Upload

Record a finished Claude Design upload: the SHA-256 of every file in upload_plan.json into
design/project/claude_design.json.

Use when: after uploading through the claude-design MCP; the design, icon and screenshot gates fail until
this receipt matches.
Returns: {ok, receipt path, files}.

Input parameters:

- `app_dir` (string, required): Absolute path to the app project directory (contains app.spec.json), e.g. /Users/me/Apps/MyApp.
- `open_url` (string, required): The claude.ai/design link returned by render_preview (not the serve_url).
- `project_id` (string, required): Claude Design project id from create_project.

### `design_upload_status` (~75 tokens)

Design Upload Status

Check whether the local Claude Design project is uploaded and unchanged since (read-only).

Use when: diagnosing a design gate failure.
Returns: {ok, uploaded, changed_files}.

Input parameters:

- `app_dir` (string, required): Absolute path to the app project directory (contains app.spec.json), e.g. /Users/me/Apps/MyApp.

### `design_export_png` (~200 tokens)

Design Export PNG

Rasterize a Claude Design board to PNG with local headless Chrome.

Use when: icon (B01-AppIcon 1024x1024 to design/icon.png) or store layout boards (440x956, scale 3).
serve_url comes from claude-design render_preview and is used once.
Returns: {ok, path, width, height}.

Input parameters:

- `height` (integer, required): Viewport height in CSS pixels, e.g. 1024 for the icon board or 956 for a store board.
- `out_path` (string, required): Output file path (PNG).
- `scale` (integer): Device scale factor (default 1; use 3 for store boards).
- `serve_url` (string, required): Temporary serve_url from claude-design render_preview; used once, never stored.
- `width` (integer, required): Viewport width in CSS pixels, e.g. 1024 for the icon board or 440 for a store board.

### `mascot_blink` (~136 tokens)

Mascot Blink

Create closed-eye blink copies (<state>-blink.png) of the approved mascot pose PNGs.

Use when: before mascot_assets. Needs the optional extra `uv sync --extra mascot`.
Returns: {ok, written: [paths]}.

Input parameters:

- `app_dir` (string, required): Absolute path to the app project directory (contains app.spec.json), e.g. /Users/me/Apps/MyApp.
- `paths`: Explicit pose PNG paths; overrides the default <app>/design/mascot/<state>.png.
- `states`: Mascot pose/state names, e.g. ['idle', 'happy']; omit for all states.

### `mascot_assets` (~131 tokens)

Mascot Assets

Import approved mascot poses and blink variants into Resources/Assets.xcassets/Mascot/.

Use when: after mascot_blink; states without a pose borrow a fallback.
Returns: {ok, imagesets: [names]}.

Input parameters:

- `app_dir` (string, required): Absolute path to the app project directory (contains app.spec.json), e.g. /Users/me/Apps/MyApp.
- `source_dir`: Directory with approved pose PNGs; omit for <app>/design/mascot.
- `states`: Mascot pose/state names, e.g. ['idle', 'happy']; omit for all states.

### `team_brief` (~150 tokens)

Team Brief

Render the team docs (TEAM.md, per-role briefs, onboarding plan, ASO research, CHECKLIST.md) from the spec.

Use when: setting up multi-session work. Existing files are kept unless overwrite=true.
Returns: {ok, files}.

Input parameters:

- `app_dir` (string, required): Absolute path to the app project directory (contains app.spec.json), e.g. /Users/me/Apps/MyApp.
- `overwrite` (boolean): If true, replace files that already exist; default false refuses to overwrite.
- `repo`: GitHub repo 'owner/name' to reference in the briefs; optional.
- `sessions`: Names of the team sessions as {lead, ios, backend, store}.

### `github_issues_bootstrap` (~226 tokens)

GitHub Issues Bootstrap

Create or refresh the GitHub label set (ios, backend, store, lead, founder, next, later, other-project) on a
repo (needs human approval unless dry_run).

Use when: before github_issue_create. dry_run=true (default) returns the commands. No-op (n/a) when the
github_issues run option is off.
Returns: {ok, commands/created, dry_run} or an approval_required refusal.

Input parameters:

- `app_dir`: App directory; when given, the github_issues run option is read from its spec.
- `approval_id`: Approval id from a previous approval_required refusal; omit on the first call. The human approves out-of-band with `appfactory approve <id>`, then call again with the same arguments and this id.
- `dry_run` (boolean): If true (default) nothing is changed or sent and the exact plan/command is returned; false performs the live action.
- `repo` (string, required): GitHub repository as 'owner/name', e.g. 'octocat/my-app'.

### `github_issue_create` (~277 tokens)

GitHub Issue Create

Open a GitHub issue for postponed work (needs human approval unless dry_run).

Use when: parking work with a role label plus next or later. dry_run=true (default) returns the exact
command. No-op (n/a) when the github_issues run option is off.
Returns: {ok, url/command, dry_run} or an approval_required refusal.

Input parameters:

- `app_dir`: App directory; when given, the github_issues run option is read from its spec.
- `approval_id`: Approval id from a previous approval_required refusal; omit on the first call. The human approves out-of-band with `appfactory approve <id>`, then call again with the same arguments and this id.
- `body` (string, required): Issue body with what / why / done-when sections.
- `dry_run` (boolean): If true (default) nothing is changed or sent and the exact plan/command is returned; false performs the live action.
- `labels` (array, required): Issue labels: one role label (ios, backend, store, lead, founder) plus next or later.
- `repo` (string, required): GitHub repository as 'owner/name', e.g. 'octocat/my-app'.
- `title` (string, required): Issue title in imperative form, e.g. 'Add offer paywall analytics'.

### `playbook` (~50 tokens)

Playbook

Return the AppFactory run playbook as markdown.

Use when: before driving the pipeline; the same text is the `run` prompt and the appfactory://playbook
resource.
Returns: markdown string.

Output parameters:

- `result` (string)

## Diagnostics

Captured diagnostic sections: Provenance, Install scripts, Dependencies. The full working is on the page: https://verifymcp.io/servers/gbatistuta0-appfactory/appfactory#diagnostics

## Score history

- 2026-10-04: 75
- 2026-10-03: 75
- 2026-10-02: 75
- 2026-10-01: 75
- 2026-09-30: 75
- 2026-09-29: 60

## Common questions

### What is the io.github.gbatistuta0/appfactory MCP server?

io.github.gbatistuta0/appfactory is an MCP server listed in the public MCP registry as io.github.gbatistuta0/appfactory. Lets your coding agent ship a SwiftUI iOS subscription app from idea to TestFlight. This page covers its PyPI package (appfactory).

### Is the io.github.gbatistuta0/appfactory MCP server safe to use?

io.github.gbatistuta0/appfactory scores 75 out of 100 on VerifyMCP. We found no known CVEs affecting it as of 4 October 2026. Its build provenance is signed and verified. That is a record of what we were able to check automatically, not an endorsement. The category breakdown on this page shows every signal behind the number, including the ones we could not confirm.

### What tools does the io.github.gbatistuta0/appfactory MCP server expose?

io.github.gbatistuta0/appfactory exposes 125 tools: setup_status, setup_services, setup_set, setup_approvals, setup_credentials, and 120 more. Their descriptions and schemas cost roughly 18,907 tokens of context every time the server is loaded.

### Is the io.github.gbatistuta0/appfactory MCP server still maintained?

io.github.gbatistuta0/appfactory is still listed as active in the MCP registry. We last reached this channel on 4 October 2026. Those dates come from our own scans of the registry and the channel itself, not from anything the publisher announced.

### What licence is the io.github.gbatistuta0/appfactory MCP server under?

io.github.gbatistuta0/appfactory declares the MIT licence, which is OSI-approved. That covers the source only, and says nothing about the cost of any service it calls.

## Links

- PyPI project: https://pypi.org/project/appfactory/
- Socket report: https://socket.dev/pypi/package/appfactory
- Repository: https://github.com/gbatistuta0/appfactory
- Changelog RSS feed: https://verifymcp.io/servers/gbatistuta0-appfactory/appfactory.xml
- Changelog JSON feed: https://verifymcp.io/servers/gbatistuta0-appfactory/appfactory.json
- HTML version of this page: https://verifymcp.io/servers/gbatistuta0-appfactory/appfactory
