The Migration Pack
Everything your coding agent needs to move an app to native.
Agents and converters can write the screens. The Pack covers what they miss, as checked steps:
- Users stay signed in
- Push notifications keep working
- Analytics events match
- Visual parity is measured
- The cutover is planned
Agent Skills, two reviewer agents, scripts and a playbook. Your agent migrates a React Native or Expo app to Swift and Kotlin one checkpoint at a time, keeping the app's existing design unless you choose platform conventions. It installs as a Claude Code plugin, and an install script adds it to Cursor, Codex and other Agent Skills readers.
All 16 items ship today. New ones reach every licence as free updates when they work end to end.
Not sure the Pack fits your app?
Talk to the founder. We will say whether your app is worth porting, and whether the Pack is the right way to do it. The call is free, and it ends with an answer either way.
.claude-plugin/
skills/
assess-rn-app/
plan-checkpoints/
spec-screen/
port-screen-swiftui/
port-screen-compose/
check-parity/
replace-expo-module/
plan-cutover/
agents/
parity-reviewer.md
architecture-reviewer.md
scripts/
install.sh
playbook/3 scripts ship inside the skills they belong to.
Skills
Each skill runs one stage of the migration, in this order.
1assess-rn-app
Before anything else, on the whole repository.
A static analysis and a Readiness Report: every native dependency mapped to iOS and Android, custom native code, OTA, the hard parts, and a heuristic effort band.
2plan-checkpoints
After the assessment is signed off.
An ordered checkpoint plan. A human accepts it against a SHA-256 hash, so no checkpoint starts on a plan that changed.
3spec-screen
At the start of every checkpoint.
A written spec of one screen from its React Native source: inputs, states, data, interactions, navigation, test IDs and strings.
4port-screen-swiftui
Implementing a spec on iOS.
A SwiftUI screen, view model and tests. Keeps the app's existing design unless you choose platform conventions.
5port-screen-compose
Implementing a spec on Android.
A Jetpack Compose screen, ViewModel and tests. Keeps the app's existing design unless you choose platform conventions.
6check-parity
Before a checkpoint can close.
One Maestro flow drives the React Native and native builds. Screenshots and events are diffed into a pass, drift or fail report.
7replace-expo-module
A screen depends on an Expo or community native module.
A Swift and Kotlin service behind an interface, with permissions, stored data and behaviour differences carried over.
8plan-cutover
Planning the release that retires the React Native build.
OTA replacement, bundle ID continuity, session and data migration, phased rollout and a rollback plan.
Reviewer agents
The agent that wrote a screen will approve it. These two are separate, read-only, and briefed to find problems before a human approves.
parity-reviewer
Checks the native code against the running React Native app and the spec: behaviour, visuals, events, accessibility, edge cases.
architecture-reviewer
Checks the Swift and Kotlin code for duplication, layering, state handling, performance, test quality and platform API misuse.
Scripts
The parts that must be exact are code, not prompts. Each script ships with its skill.
Static analyzer
skills/assess-rn-app/scripts/analyze.mjs
Reads an RN or Expo repository without running its code. Cites a file for every finding. Markdown and JSON output.
Checkpoint plan
skills/plan-checkpoints/scripts/plan.mjs
Drafts the plan, records acceptance against a hash of its contents, and tracks checkpoint status.
Parity pipeline
skills/check-parity/scripts/
Maestro-driven screenshots of both builds, a screenshot comparison that reports structure, position and content, and an event diff.
Install
Needs Node.js 20 or newer. Porting screens and checking parity also need Xcode with an iOS simulator, the Android SDK with an emulator, and Maestro. The screenshot comparator is part of the Pack and needs nothing else installed; ImageMagick is optional and only the older pixel-diff script uses it.
Claude Code
/plugin marketplace add also accepts the Git URL of your licensed copy.
/plugin marketplace add /path/to/migration-pack
/plugin install nativestarter-migration-pack@nativestarterCursor, Codex and others
Copies or symlinks the skills into your project's agent folders.
scripts/install.sh /path/to/your-app # copies skills into .agents/skills and .claude/skills
scripts/install.sh /path/to/your-app --symlink # symlinks instead, so Pack updates applyFirst run
Ask your agent to assess the app for migration, or run the analyzer yourself.
node skills/assess-rn-app/scripts/analyze.mjs /path/to/your-app --md readiness-draft.md --json readiness.jsonA spec you can argue with
spec-screen reads the React Native source and writes this before any native code exists. It is checked against the running app, so misreadings get caught while they are cheap.
Abridged from the Bluesky port. The agent wrote Purpose, States and Open questions; spec-screen generated the rest.
# Screen spec: ThreadPreferencesScreen
Screen file src/screens/Settings/ThreadPreferences.tsx
Generated 2026-10-22 by spec-screen 1.0.0
## Purpose
The user opens this screen from Settings > Content & Media > Thread
preferences and chooses how replies under a post are ordered and whether
they are drawn as a tree. Both values are server preferences. The screen
saves through a two-second debounce and flushes the pending save when it
loses focus, so the last change is never lost.
## States
| State | Condition | What is visible | Test IDs |
|---------|-----------|------------------------------------------|-------------------------|
| content | always | "Sort replies" radio group, three rows; | threadPreferencesScreen |
| | | "Tree view" group with one switch | |
A save failure changes nothing on screen: the hook logs and returns.
## Interactions
| Element | Handler | Action | Line |
|-----------------|----------|--------------------------------------------------|------|
| <Toggle.Group> | onChange | setSort(normalizeSort(values[0])) | L55 |
| <Toggle.Item> | onChange | setView(normalizeView({treeViewEnabled: value})) | L94 |
## Test IDs
Keep every ID identical on both platforms: testID = SwiftUI
accessibilityIdentifier = Compose Modifier.testTag.
## Strings
Thread Preferences · Sort replies · Top replies first · Oldest replies
first · Newest replies first · Tree view · Show post replies in a
threaded tree view
## Open questions
- A failed save is silent. RN logs it and shows no toast or inline error,
so the switch moves while the server is unchanged. Ported as observed.
- Any save normalises the sort to top, oldest or newest. An account on the
server default, hotness, is rewritten to top by the first change. That
is RN's behaviour, not a port decision.The playbook
Six chapters on the decisions around the code.
1Should you migrate?
The questions that decide it, and the honest cases for staying on React Native.
2Brownfield or rewrite
Hosting remaining React Native screens inside a native shell, or rebuilding from the running app.
3Running checkpoints
Sizing a checkpoint, what gates it, and who signs it off.
4Parity without a simulator farm
Driving both apps to the same state and capturing it cheaply enough to do every time.
5Life after OTA
What you lose without over-the-air updates, what replaces it, and how release cadence changes.
6Cutover and store submission
Phased rollout, keeping the bundle ID, and not losing reviews or subscriptions.
- Individual1 developer
- $149
- TeamUp to 10 developers
- $499
- CompanyUnlimited developers
- $1,499