copilot/create-zip-file
Port existing app
Goal
Move the existing Expo app from .migration-backup/ into the new workspace. It must look and work exactly like before.
This app is the user's real product. They built it, their users depend on it, and they trust you not to break it. Every lost feature, broken style, or missing interaction is a regression they'll notice. Treat this like porting someone's live production app — because it is.
In scope
- Porting the project — copying files, fixing imports, connecting to the API
- Making it look and function identically to the original
- Adapting to the restructured workspace — the scaffold has already added
artifacts/mockup-sandbox/,lib/*packages, and scaffold-requiredapp.jsonadditions (expo-router plugin, expo-font plugin, typedRoutes, Replit origin). Your job is to fit the existing app into this new layout, not relitigate it.
Out of scope
- Building the new app — that's the next task
- New features, refactoring, or code quality improvements
- Fixing bugs that existed before migration
- Strict typecheck compliance — use
// @ts-ignoreor type assertions to keep moving. Functionality and visual parity matter more than zero TS errors
Pre-existing scaffold (do NOT remove or relitigate)
The pnpm_workspace scaffold is already applied before this task begins. The following packages existed before any agent work — they are not your additions:
artifacts/mockup-sandbox/— design/mockup artifact (pre-existing).lib/*— shared packages bundled with the scaffold.
Scaffold-required additions to app.json (e.g. expo-router plugin, expo-font plugin, typedRoutes: true, Replit origin) are part of the new scaffold and must remain — do not treat them as drift from the original.
Note to the code reviewer
When reviewing the diff for this task:
- HONOR the
Out of scopesection above. Do NOT reject for items the plan explicitly marks out of scope (e.g.anytypes, typecheck errors — those are explicitly permitted). - Do NOT reject for pre-existing issues. Type errors,
anytypes, and legacy patterns in code copied from.migration-backup/are not regressions. The goal of a port is behavior parity, not refactor. - Do NOT flag the pre-existing scaffold packages above as agent additions, and do NOT reject scaffold-required
app.jsonadditions as drift. - Do NOT reject for
pnpm dev/pnpm run devfailing at workspace root. Replit apps run via workflows (restart_workflow), not root-levelpnpm dev. If the agent verified via workflow and it works, that is correct. - Do NOT flag unused scaffold files (e.g., default
components/ui/*,assets/files that came with the Expo scaffold). Focus on files the agent actually wrote or changed. - Apply only the criteria from this task's
## Goal+## In scope. Reject only for real regressions (broken runtime, missing files, lost features).
User communication
Tell the user what's happening at each major phase. Keep it short and non-technical:
- Before starting: "Looking at your project to understand how it's set up..."
- Before backend work: "Setting up the database and API..."
- Before frontend work: "Moving your app files over to the new structure..."
- Before sanity check: "Running a quick check to make sure everything is in the right place..."
- Before build: "Building everything to make sure it works..."
- Before
mark_task_complete: "Running some checks to make sure your app is safe and sound after the migration. This may take a minute." - If validation fails: "Found a small issue — fixing it now..."
Steps
- Read the pnpm_workspace skill first
Before doing anything else, read .local/skills/pnpm-workspace/SKILL.md. It contains the canonical patterns for this workspace (TypeScript project references, artifact routing, package management, logging, common pitfalls). You will need it for every later step.
- Detect old project structure and batch-read (ONE parallel call)
Include these in your single parallel batch of reads:
ls .migration-backup/(top-level structure)ls .migration-backup/client/ 2>/dev/null(Expo v3 code prefix — exists if non-routing code lives underclient/)ls .migration-backup/server/ 2>/dev/null(determines single-file vs directory routes layout; absent for frontend-only projects)cat .migration-backup/app/_layout.tsx 2>/dev/null || echo "NO_EXPO_ROUTER"— determines routing paradigmcat .migration-backup/package.jsonandcat .migration-backup/app.json.migration-backup/server/routes.tsor files in.migration-backup/server/routes/— use thelsresult above to pick the right one.migration-backup/shared/schema.tsor equivalentcat .migration-backup/<prefix>/constants/colors.ts 2>/dev/null || cat .migration-backup/constants/colors.ts 2>/dev/null— design token colorscat .migration-backup/<prefix>/constants/typography.ts 2>/dev/null || cat .migration-backup/constants/typography.ts 2>/dev/null— font families, sizes, weights (may not exist)cat .migration-backup/<prefix>/constants/theme.ts 2>/dev/null || cat .migration-backup/constants/theme.ts 2>/dev/null— unified theme file if it exists
After reading, determine two things:
a. Routing paradigm:
- expo-router: app/_layout.tsx exists → screens are files under app/, routing is filesystem-based
- react-navigation: no app/_layout.tsx → a Navigator tree wires up screens explicitly
b. Code prefix (where non-routing code lives — components, constants, hooks, assets):
- If ls .migration-backup/client/ returned a listing → prefix is client/ (Expo v3 template)
- Otherwise → prefix is . (root-level, older projects)
Both determinations are required. Do not proceed to step 2 without knowing both.
Run
pnpm install— before any other pnpm commands. If it fails, read the error, fix the issue (e.g. remove conflicting packages), and retry.Port backend (can run in parallel with step 4; skip entirely if no
server/orshared/directory)
Always port ALL server files, even if they contain only stubs or appear unused. Copy first, fix imports after.
a. DB schema — Always copy the schema even if the app appears to use only local storage. Copy then immediately fix imports:
```sh
cp .migration-backup/shared/schema.ts lib/db/src/schema/
sed -i 's/from "zod"/from "zod\/v4"/g' lib/db/src/schema/schema.ts
sed -i 's/from '\''zod'\''/from '\''zod\/v4'\''/g' lib/db/src/schema/schema.ts
```
b. OpenAPI spec — Write lib/api-spec/openapi.yaml based on the routes you already read.
c. Run codegen + DB push (parallel):
- pnpm --filter @workspace/api-spec run codegen
- pnpm --filter @workspace/db run push
d. Read generated hooks — After codegen completes, read lib/api-client-react/src/generated/api.ts to get exact hook names and signatures before fixing frontend imports.
e. API routes — Copy then fix imports. Stock Expo backends ship either a single server/routes.ts or a server/routes/ directory — check which exists:
```sh
# Single-file template (most common):
cp .migration-backup/server/routes.ts artifacts/api-server/src/routes/routes.ts
# Directory template:
cp -r .migration-backup/server/routes/* artifacts/api-server/src/routes/
# Copy storage layer — always, even if it looks like a stub:
cp .migration-backup/server/storage.ts artifacts/api-server/src/storage.ts 2>/dev/null; true
# Copy any other server files (middleware, utils, etc.):
cp .migration-backup/server/*.ts artifacts/api-server/src/ 2>/dev/null; true
```
**Signature change required:** the legacy export is `registerRoutes(httpServer, app)`. Convert it to an Express router and mount it from `src/routes/index.ts`. Targeted edits: `@shared/` or `../../shared/` → `@workspace/db`, add `@workspace/api-zod` for validation.
- Port frontend
Always start by creating the artifact — the scaffold must exist before you copy into it:
await createArtifact({ artifactType: "expo", slug: "<app-name>", previewPath: "/", title: "<Title>" })
Then copy based on the routing paradigm detected in step 1.
If expo-router (old project has app/_layout.tsx):
Copy the directory structure directly — it maps to the new artifact. First, list what's actually in the prefix so you copy everything:
ls .migration-backup/<prefix>/
Then copy every directory that exists (skip node_modules, app/ — that's routing, handled separately):
cp .migration-backup/app.json artifacts/<app-name>/app.json
cp -r .migration-backup/app/* artifacts/<app-name>/app/
# <prefix> is "client" or "." — determined in step 1. Do not guess.
# Copy directory CONTENTS, not the directory itself — trailing /* prevents nesting:
cp -r .migration-backup/<prefix>/components/* artifacts/<app-name>/components/
cp -r .migration-backup/<prefix>/constants/* artifacts/<app-name>/constants/
cp -r .migration-backup/<prefix>/hooks/* artifacts/<app-name>/hooks/
cp -r .migration-backup/<prefix>/lib/* artifacts/<app-name>/lib/
cp -r .migration-backup/<prefix>/assets/* artifacts/<app-name>/assets/
cp -r .migration-backup/<prefix>/navigation/* artifacts/<app-name>/navigation/
# Also copy any other directories shown by ls above (e.g. services/, utils/, store/)
# If a target dir doesn't exist yet, create it first: mkdir -p artifacts/<app-name>/<dir>
Do not add 2>/dev/null — if a cp fails, you need to see it. After copying, verify:
bash .local/secondary_skills/migrate-to-multi-artifact/references/expo_verify_copy.sh <app-name> <prefix>
Fix any missing directories before proceeding.
Always copy app.json to preserve name, slug, bundleIdentifier, icons, plugins, and schemes. The @/ alias in the artifact resolves to artifacts/<app-name>/. Since you preserved the same directory structure, all existing @/components/..., @/constants/..., and @/assets/... paths continue to work without changes.
If react-navigation (no app/_layout.tsx):
The old project has a Navigator tree instead of file-based routing. You cannot copy the routing structure — you must translate it:
a. Copy non-routing code in bulk. First, list what's in the prefix:
```sh
ls .migration-backup/<prefix>/
```
Then copy every directory that exists (skip `node_modules`). Do NOT use `2>/dev/null`:
```sh
# <prefix> is "client" or "." — determined in step 1. Do not guess.
cp -r .migration-backup/<prefix>/components/* artifacts/<app-name>/components/
cp -r .migration-backup/<prefix>/constants/* artifacts/<app-name>/constants/
cp -r .migration-backup/<prefix>/hooks/* artifacts/<app-name>/hooks/
cp -r .migration-backup/<prefix>/lib/* artifacts/<app-name>/lib/
cp -r .migration-backup/<prefix>/assets/* artifacts/<app-name>/assets/
cp -r .migration-backup/<prefix>/navigation/* artifacts/<app-name>/navigation/
cp -r .migration-backup/<prefix>/screens/* artifacts/<app-name>/screens/
# Also copy any other directories shown by ls above
```
After copying, verify:
```sh
bash .local/secondary_skills/migrate-to-multi-artifact/references/expo_verify_copy.sh <app-name> <prefix>
```
b. For each screen in the old Navigator tree, create the corresponding expo-router file:
- Root screen → app/index.tsx
- Tab screen "Home" → app/(tabs)/index.tsx
- Tab screen "Profile" → app/(tabs)/profile.tsx
- Modal screen "Detail" → app/detail.tsx
Copy the screen component's body verbatim. Replace `navigation.navigate('X')` with `router.push('/x')` from `expo-router`.
c. Verify @/ alias paths after copying:
```sh
bash .local/secondary_skills/migrate-to-multi-artifact/references/expo_verify_imports.sh <app-name>
```
Fix every broken import before proceeding.
Copy app.json from the old project to artifacts/<app-name>/app.json. Verify name, slug, and bundleIdentifier are preserved.
Fix frontend imports (both paradigms — wait for codegen in step 3d to complete first):
- Replace old fetch hooks with generated hooks from @workspace/api-client-react
- Add setBaseUrl in app/_layout.tsx at module level (before the component):
```typescript
import { setBaseUrl } from "@workspace/api-client-react";
const domain = process.env.EXPO_PUBLIC_DOMAIN;
if (domain) setBaseUrl(`https://${domain}`);
```
- Remove
@shared/or../../shared/imports — types now come from generated hooks - Preserve the original
queryClientconfiguration — do not create a new one - Do not import from
@workspace/dbor server code
- Run sanity check — do NOT move to step 6 until the output prints ALL CHECKS PASSED:
bash .local/secondary_skills/migrate-to-multi-artifact/references/expo_sanity_check.sh <app-name> <prefix>
If any FAIL line appears, fix and re-run until ALL CHECKS PASSED.
Verify the app works — restart the workflow and check that Metro bundles without errors. Do NOT run
pnpm run buildmanually — the workflow handles env vars and build automatically. If there are visual differences, verify thatconstants/colors.tsandapp.jsonwere copied correctly.Pre-completion checklist — verify before marking complete:
- ALL backend files ported (routes, storage, schema — even stubs)?
-
zod→zod/v4imports fixed in schema files? -
@shared/→@workspace/dbimports fixed in all server files? -
app.jsoncopied with correct name, slug, bundleIdentifier? - App renders correctly after workflow restart?
- Sanity check prints ALL CHECKS PASSED?
Use the
mark_task_completetool (a tool call, not a code execution callback) — the user will verify visual parity before the next task starts.
Expo-specific pitfalls
- Do not change
app.jsontoapp.config.ts: The project must use staticapp.json. Dynamic config files break the Expo Launch build process. - Do not modify StyleSheet styles: Copy component files verbatim. Styling uses
StyleSheet.create()andconstants/colors.ts, not CSS/Tailwind. - Preserve the font-loading pattern: The scaffold uses
useFonts+SplashScreengating in_layout.tsx. Copy the old layout's font loading exactly. expo-routerfile-based routing: Theapp/directory structure IS the routing. Preserve the exact directory hierarchy when copying (expo-router projects only).- Do not port infrastructure: Do not copy DB connection setup (e.g., Neon serverless). The workspace has its own in
lib/db. - Native package versions: Expo has strict version requirements for native modules (e.g.
react-native-keyboard-controller1.21.4 is incompatible with Expo SDK expecting 1.18.5). The sanity check compares versions between the original and the artifact. Always preserve the original project's native package versions — do not upgrade them.