diff --git a/apps/expo/I18N_SUMMARY.md b/apps/expo/I18N_SUMMARY.md
deleted file mode 100644
index 3b56eaeb38..0000000000
--- a/apps/expo/I18N_SUMMARY.md
+++ /dev/null
@@ -1,182 +0,0 @@
-# i18n Implementation Summary
-
-## Overview
-
-The PackRat Expo app has been set up with internationalization (i18n) support following [Expo's best practices](https://docs.expo.dev/versions/latest/sdk/localization/).
-
-## What Was Implemented
-
-### 1. Core Infrastructure
-
-- **expo-localization** (v~16.1.6): Detects device locale
-- **i18next** (v^25.8.18): Core i18n library
-- **react-i18next** (v^16.5.6): React bindings (provides `useTranslation` hook)
-- **Configuration**: `apps/expo/lib/i18n/index.ts`
-- **TypeScript Types**: `apps/expo/lib/i18n/i18next.d.ts` (module augmentation)
-- **Translation Hook**: `apps/expo/lib/hooks/useTranslation.ts`
-
-### 2. English Translations
-
-Created comprehensive English translation file with 150+ keys organized into 15 categories:
-
-- `common` - UI elements (buttons, labels)
-- `errors` - Error messages
-- `auth` - Authentication
-- `profile` - User profile
-- `navigation` - Navigation labels
-- `packs` - Pack management
-- `items` - Item management
-- `trips` - Trip planning
-- `catalog` - Item catalog
-- `welcome` - Onboarding
-- `ai` - AI features
-- `weather` - Weather info
-- `location` - Location services
-- `shopping` - Shopping lists
-- `admin` - Admin panel
-- `seasons` - Season suggestions
-- `experience` - Experience levels
-
-### 3. Example Implementations
-
-Updated 7 files to demonstrate i18n usage:
-
-1. `components/ErrorState.tsx` - Error component
-2. `screens/ConsentWelcomeScreen.tsx` - Welcome/onboarding
-3. `screens/ProfileScreen.tsx` - Profile screen
-4. `app/(app)/(tabs)/_layout.tsx` - Tab navigation
-5. `app/(app)/(tabs)/profile/index.tsx` - Profile page
-6. `features/ai/components/ErrorState.tsx` - AI error state
-
-### 4. Documentation & Tools
-
-Created comprehensive developer resources:
-
-- **README.md** - Complete i18n documentation
-- **MIGRATION.md** - Step-by-step migration guide
-- **EXAMPLES.tsx** - Code examples (7 patterns)
-- **types.ts** - TypeScript type definitions
-- **extract-strings.js** - Helper script to find hardcoded strings
-
-## File Structure
-
-```
-apps/expo/
-├── lib/
-│ ├── i18n/
-│ │ ├── index.ts # i18next configuration + resources
-│ │ ├── i18next.d.ts # TypeScript module augmentation
-│ │ ├── types.ts # TranslationKeys convenience type
-│ │ ├── locales/
-│ │ │ └── en.json # English translations
-│ │ ├── README.md # Documentation
-│ │ ├── MIGRATION.md # Migration guide
-│ │ ├── EXAMPLES.tsx # Code examples
-│ │ └── extract-strings.js # Helper script
-│ └── hooks/
-│ └── useTranslation.ts # Re-exports useTranslation from react-i18next
-└── package.json # Updated with dependencies
-```
-
-## Usage
-
-### In Components
-
-```tsx
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
-
-function MyComponent() {
- const { t } = useTranslation();
-
- return (
-
- {t('common.welcome')}
-
-
- );
-}
-```
-
-### With Variables
-
-```tsx
-// In en.json: "memberSince": "Member since {{date}}"
-const { t } = useTranslation();
-{t('profile.memberSince', { date: '2024' })}
-```
-
-### Outside Components
-
-```tsx
-import { t } from 'expo-app/lib/i18n';
-const message = t('errors.somethingWentWrong');
-```
-
-## Next Steps
-
-### For Developers
-
-1. **Find strings to migrate:**
- ```bash
- node apps/expo/lib/i18n/extract-strings.js
- ```
-
-2. **Follow migration guide:**
- - Read `apps/expo/lib/i18n/MIGRATION.md`
- - Add translation keys to `en.json`
- - Replace hardcoded strings with `t()` calls
-
-3. **Add new translations:**
- - Add keys to appropriate section in `en.json`
- - Use in components with `t('section.key')`
-
-### For Multi-Language Support
-
-When ready to add other languages:
-
-1. Create new locale file (e.g., `es.json`, `fr.json`)
-2. Copy structure from `en.json`
-3. Translate all values
-4. Import in `lib/i18n/index.ts`:
- ```typescript
- import es from './locales/es.json';
-
- export const resources = {
- en: { translation: en },
- es: { translation: es },
- } as const;
- ```
-
-The app automatically uses the device language if available, falling back to English.
-
-## Benefits
-
-✅ **Maintainability** - All text in one place
-✅ **Scalability** - Easy to add languages
-✅ **Type Safety** - TypeScript support for translation keys
-✅ **Standards** - Follows Expo best practices
-✅ **Developer Experience** - Hook-based API, comprehensive docs
-✅ **Future-Ready** - Infrastructure for multi-language support
-
-## Migration Status
-
-- **Total TypeScript files**: 375+
-- **Files updated**: 7 (examples)
-- **Remaining files**: 368+ (can be gradually migrated)
-- **Translation keys ready**: 150+
-
-The infrastructure is complete. Developers can now gradually migrate remaining components following the provided documentation and examples.
-
-## Resources
-
-- [Expo Localization Docs](https://docs.expo.dev/versions/latest/sdk/localization/)
-- [i18next Documentation](https://www.i18next.com/)
-- [react-i18next Documentation](https://react.i18next.com/)
-- [i18next TypeScript Guide](https://www.i18next.com/overview/typescript)
-- Local docs: `apps/expo/lib/i18n/README.md`
-- Migration guide: `apps/expo/lib/i18n/MIGRATION.md`
-- Code examples: `apps/expo/lib/i18n/EXAMPLES.tsx`
-
-## Questions?
-
-See the comprehensive documentation in `apps/expo/lib/i18n/` or check the example implementations in the updated components.
diff --git a/apps/expo/app/(app)/(tabs)/(home)/index.tsx b/apps/expo/app/(app)/(tabs)/(home)/index.tsx
index 95c487933c..0b8ad05a0f 100644
--- a/apps/expo/app/(app)/(tabs)/(home)/index.tsx
+++ b/apps/expo/app/(app)/(tabs)/(home)/index.tsx
@@ -1,5 +1,32 @@
'use client';
+import { AIChatTile } from '@packrat/app/ai/components/AIChatTile';
+import { ReportedContentTile } from '@packrat/app/ai/components/ReportedContentTile';
+import { AIPacksTile } from '@packrat/app/ai-packs/components/AIPacksTile';
+import { AndroidTabBarInsetFix } from '@packrat/app/components/AndroidTabBarInsetFix';
+import { Icon } from '@packrat/app/components/Icon';
+import { LargeTitleHeaderSearchContentContainer } from '@packrat/app/components/LargeTitleHeaderSearchContentContainer';
+import { appConfig, featureFlags } from '@packrat/app/config';
+import { FeedTile } from '@packrat/app/feed/components/FeedTile';
+import { GuidesTile } from '@packrat/app/guides/components/GuidesTile';
+import { cn } from '@packrat/app/lib/cn';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
+import { asNonNullableRef } from '@packrat/app/lib/utils/asNonNullableRef';
+import { PackTemplatesTile } from '@packrat/app/pack-templates/components/PackTemplatesTile';
+import { CurrentPackTile } from '@packrat/app/packs/components/CurrentPackTile';
+import { GearInventoryTile } from '@packrat/app/packs/components/GearInventoryTile';
+import { PackCategoriesTile } from '@packrat/app/packs/components/PackCategoriesTile';
+import { PackStatsTile } from '@packrat/app/packs/components/PackStatsTile';
+import { RecentPacksTile } from '@packrat/app/packs/components/RecentPacksTile';
+import { SeasonSuggestionsTile } from '@packrat/app/packs/components/SeasonSuggestionsTile';
+import { SharedPacksTile } from '@packrat/app/packs/components/SharedPacksTile';
+import { ShoppingListTile } from '@packrat/app/packs/components/ShoppingListTile';
+import { WeightAnalysisTile } from '@packrat/app/packs/components/WeightAnalysisTile';
+import { TrailConditionsTile } from '@packrat/app/trips/components/TrailConditionsTile';
+import { UpcomingTripsTile } from '@packrat/app/trips/components/UpcomingTripsTile';
+import { WeatherAlertsTile } from '@packrat/app/weather/components/WeatherAlertsTile';
+import { WeatherTile } from '@packrat/app/weather/components/WeatherTile';
+import { WildlifeTile } from '@packrat/app/wildlife/components/WildlifeTile';
import { arrayIncludes, assertIsString, objectKeys } from '@packrat/guards';
import type { LargeTitleSearchBarMethods, ListDataItem } from '@packrat/ui/nativewindui';
import {
@@ -8,33 +35,6 @@ import {
type ListRenderItemInfo,
ListSectionHeader,
} from '@packrat/ui/nativewindui';
-import { AndroidTabBarInsetFix } from 'expo-app/components/AndroidTabBarInsetFix';
-import { Icon } from 'expo-app/components/Icon';
-import { LargeTitleHeaderSearchContentContainer } from 'expo-app/components/LargeTitleHeaderSearchContentContainer';
-import { appConfig, featureFlags } from 'expo-app/config';
-import { AIChatTile } from 'expo-app/features/ai/components/AIChatTile';
-import { ReportedContentTile } from 'expo-app/features/ai/components/ReportedContentTile';
-import { AIPacksTile } from 'expo-app/features/ai-packs/components/AIPacksTile';
-import { FeedTile } from 'expo-app/features/feed/components/FeedTile';
-import { GuidesTile } from 'expo-app/features/guides/components/GuidesTile';
-import { PackTemplatesTile } from 'expo-app/features/pack-templates/components/PackTemplatesTile';
-import { CurrentPackTile } from 'expo-app/features/packs/components/CurrentPackTile';
-import { GearInventoryTile } from 'expo-app/features/packs/components/GearInventoryTile';
-import { PackCategoriesTile } from 'expo-app/features/packs/components/PackCategoriesTile';
-import { PackStatsTile } from 'expo-app/features/packs/components/PackStatsTile';
-import { RecentPacksTile } from 'expo-app/features/packs/components/RecentPacksTile';
-import { SeasonSuggestionsTile } from 'expo-app/features/packs/components/SeasonSuggestionsTile';
-import { SharedPacksTile } from 'expo-app/features/packs/components/SharedPacksTile';
-import { ShoppingListTile } from 'expo-app/features/packs/components/ShoppingListTile';
-import { WeightAnalysisTile } from 'expo-app/features/packs/components/WeightAnalysisTile';
-import { TrailConditionsTile } from 'expo-app/features/trips/components/TrailConditionsTile';
-import { UpcomingTripsTile } from 'expo-app/features/trips/components/UpcomingTripsTile';
-import { WeatherAlertsTile } from 'expo-app/features/weather/components/WeatherAlertsTile';
-import { WeatherTile } from 'expo-app/features/weather/components/WeatherTile';
-import { WildlifeTile } from 'expo-app/features/wildlife/components/WildlifeTile';
-import { cn } from 'expo-app/lib/cn';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
-import { asNonNullableRef } from 'expo-app/lib/utils/asNonNullableRef';
import { useMemo, useRef, useState } from 'react';
import { FlatList, Platform, Pressable, Text, View } from 'react-native';
diff --git a/apps/expo/app/(app)/(tabs)/_layout.tsx b/apps/expo/app/(app)/(tabs)/_layout.tsx
index c87a283b5e..44e506e4a7 100644
--- a/apps/expo/app/(app)/(tabs)/_layout.tsx
+++ b/apps/expo/app/(app)/(tabs)/_layout.tsx
@@ -1,5 +1,5 @@
-import { featureFlags } from 'expo-app/config';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
+import { featureFlags } from '@packrat/app/config';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
import { NativeTabs } from 'expo-router/unstable-native-tabs';
export default function TabLayout() {
diff --git a/apps/expo/app/(app)/(tabs)/catalog/index.tsx b/apps/expo/app/(app)/(tabs)/catalog/index.tsx
index deae5bbc28..57a563058f 100644
--- a/apps/expo/app/(app)/(tabs)/catalog/index.tsx
+++ b/apps/expo/app/(app)/(tabs)/catalog/index.tsx
@@ -1,4 +1,4 @@
-import CatalogItemsScreen from 'expo-app/features/catalog/screens/CatalogItemsScreen';
+import CatalogItemsScreen from '@packrat/app/catalog/screens/CatalogItemsScreen';
export default function CatalogItemsPage() {
return ;
diff --git a/apps/expo/app/(app)/(tabs)/feed/index.tsx b/apps/expo/app/(app)/(tabs)/feed/index.tsx
index b85975166d..8bfd772ba1 100644
--- a/apps/expo/app/(app)/(tabs)/feed/index.tsx
+++ b/apps/expo/app/(app)/(tabs)/feed/index.tsx
@@ -1,4 +1,4 @@
-import { FeedScreen } from 'expo-app/features/feed';
+import { FeedScreen } from '@packrat/app/feed/screens/FeedScreen';
export default function FeedRoute() {
return ;
diff --git a/apps/expo/app/(app)/(tabs)/packs/index.tsx b/apps/expo/app/(app)/(tabs)/packs/index.tsx
index 44b77b50d2..2c6c200909 100644
--- a/apps/expo/app/(app)/(tabs)/packs/index.tsx
+++ b/apps/expo/app/(app)/(tabs)/packs/index.tsx
@@ -1,5 +1,5 @@
-import { PackListScreen } from 'expo-app/features/packs/screens/PackListScreen';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { PackListScreen } from '@packrat/app/packs/screens/PackListScreen';
import { StatusBar } from 'expo-status-bar';
import { Platform } from 'react-native';
diff --git a/apps/expo/app/(app)/(tabs)/profile/index.tsx b/apps/expo/app/(app)/(tabs)/profile/index.tsx
index a38571090a..3ea5543400 100644
--- a/apps/expo/app/(app)/(tabs)/profile/index.tsx
+++ b/apps/expo/app/(app)/(tabs)/profile/index.tsx
@@ -1,3 +1,18 @@
+import { withAuthWall } from '@packrat/app/auth/hocs';
+import { useAuth } from '@packrat/app/auth/hooks/useAuth';
+import { useUser } from '@packrat/app/auth/hooks/useUser';
+import { AndroidTabBarInsetFix } from '@packrat/app/components/AndroidTabBarInsetFix';
+import { Icon } from '@packrat/app/components/Icon';
+import { cn } from '@packrat/app/lib/cn';
+import { hasUnsyncedChanges } from '@packrat/app/lib/hasUnsyncedChanges';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
+import { testIds } from '@packrat/app/lib/testIds';
+import { buildPackTemplateItemImageUrl } from '@packrat/app/lib/utils/buildPackTemplateItemImageUrl';
+import { useImagePicker } from '@packrat/app/packs/hooks/useImagePicker';
+import { uploadImage } from '@packrat/app/packs/utils/uploadImage';
+import { ProfileAuthWall } from '@packrat/app/profile/components';
+import { useUpdateProfile } from '@packrat/app/profile/hooks/useUpdateProfile';
import { clientEnvs } from '@packrat/env/expo-client';
import { isString } from '@packrat/guards';
import {
@@ -13,21 +28,6 @@ import {
ListSectionHeader,
Text,
} from '@packrat/ui/nativewindui';
-import { AndroidTabBarInsetFix } from 'expo-app/components/AndroidTabBarInsetFix';
-import { Icon } from 'expo-app/components/Icon';
-import { withAuthWall } from 'expo-app/features/auth/hocs';
-import { useAuth } from 'expo-app/features/auth/hooks/useAuth';
-import { useUser } from 'expo-app/features/auth/hooks/useUser';
-import { useImagePicker } from 'expo-app/features/packs/hooks/useImagePicker';
-import { uploadImage } from 'expo-app/features/packs/utils/uploadImage';
-import { ProfileAuthWall } from 'expo-app/features/profile/components';
-import { useUpdateProfile } from 'expo-app/features/profile/hooks/useUpdateProfile';
-import { cn } from 'expo-app/lib/cn';
-import { hasUnsyncedChanges } from 'expo-app/lib/hasUnsyncedChanges';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
-import { testIds } from 'expo-app/lib/testIds';
-import { buildPackTemplateItemImageUrl } from 'expo-app/lib/utils/buildPackTemplateItemImageUrl';
import * as FileSystem from 'expo-file-system/legacy';
import { Link, router, Stack } from 'expo-router';
import { useState } from 'react';
diff --git a/apps/expo/app/(app)/(tabs)/profile/name.tsx b/apps/expo/app/(app)/(tabs)/profile/name.tsx
index 083cf1495a..7636561883 100644
--- a/apps/expo/app/(app)/(tabs)/profile/name.tsx
+++ b/apps/expo/app/(app)/(tabs)/profile/name.tsx
@@ -1,9 +1,9 @@
+import { useUser } from '@packrat/app/auth/hooks/useUser';
+import { cn } from '@packrat/app/lib/cn';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
+import { testIds } from '@packrat/app/lib/testIds';
+import { useUpdateProfile } from '@packrat/app/profile/hooks/useUpdateProfile';
import { Button, Form, FormItem, FormSection, Text, TextField } from '@packrat/ui/nativewindui';
-import { useUser } from 'expo-app/features/auth/hooks/useUser';
-import { useUpdateProfile } from 'expo-app/features/profile/hooks/useUpdateProfile';
-import { cn } from 'expo-app/lib/cn';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
-import { testIds } from 'expo-app/lib/testIds';
import { router, Stack } from 'expo-router';
import * as React from 'react';
import { Alert, Platform, View } from 'react-native';
diff --git a/apps/expo/app/(app)/(tabs)/profile/notifications.tsx b/apps/expo/app/(app)/(tabs)/profile/notifications.tsx
index 6b2aad3e2c..09f9b0db0c 100644
--- a/apps/expo/app/(app)/(tabs)/profile/notifications.tsx
+++ b/apps/expo/app/(app)/(tabs)/profile/notifications.tsx
@@ -1,7 +1,7 @@
+import { Icon } from '@packrat/app/components/Icon';
+import { cn } from '@packrat/app/lib/cn';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
import { Button, Form, FormItem, FormSection, Text, Toggle } from '@packrat/ui/nativewindui';
-import { Icon } from 'expo-app/components/Icon';
-import { cn } from 'expo-app/lib/cn';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { router, Stack } from 'expo-router';
import * as React from 'react';
import { Platform, Pressable, ScrollView, View } from 'react-native';
diff --git a/apps/expo/app/(app)/(tabs)/profile/username.tsx b/apps/expo/app/(app)/(tabs)/profile/username.tsx
index ed6af20998..38cc0442f0 100644
--- a/apps/expo/app/(app)/(tabs)/profile/username.tsx
+++ b/apps/expo/app/(app)/(tabs)/profile/username.tsx
@@ -1,6 +1,6 @@
+import { cn } from '@packrat/app/lib/cn';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
import { Button, Form, FormItem, FormSection, Text, TextField } from '@packrat/ui/nativewindui';
-import { cn } from 'expo-app/lib/cn';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { router, Stack } from 'expo-router';
import * as React from 'react';
import { Platform, View } from 'react-native';
diff --git a/apps/expo/app/(app)/(tabs)/trips/index.tsx b/apps/expo/app/(app)/(tabs)/trips/index.tsx
index 2cf2fc0227..6ac7c5ccfe 100644
--- a/apps/expo/app/(app)/(tabs)/trips/index.tsx
+++ b/apps/expo/app/(app)/(tabs)/trips/index.tsx
@@ -1,5 +1,5 @@
-import { featureFlags } from 'expo-app/config';
-import { TripsListScreen } from 'expo-app/features/trips/screens/TripListScreen';
+import { featureFlags } from '@packrat/app/config';
+import { TripsListScreen } from '@packrat/app/trips/screens/TripListScreen';
import { Redirect } from 'expo-router';
import { StatusBar } from 'expo-status-bar';
diff --git a/apps/expo/app/(app)/_layout.tsx b/apps/expo/app/(app)/_layout.tsx
index 4213baab5f..5cb4dc9542 100644
--- a/apps/expo/app/(app)/_layout.tsx
+++ b/apps/expo/app/(app)/_layout.tsx
@@ -1,18 +1,18 @@
import { use$ } from '@legendapp/state/react';
+import { isLoadingAtom, needsReauthAtom } from '@packrat/app/auth/atoms/authAtoms';
+import { useAuthInit } from '@packrat/app/auth/hooks/useAuthInit';
+import { isAuthed } from '@packrat/app/auth/store';
+import { ThemeToggle } from '@packrat/app/components/ThemeToggle';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
+import type { TranslationFunction } from '@packrat/app/lib/i18n/types';
+import { testIds } from '@packrat/app/lib/testIds';
+import { getPackTemplateDetailOptions } from '@packrat/app/pack-templates/utils/getPackTemplateDetailOptions';
+import { getPackTemplateItemDetailOptions } from '@packrat/app/pack-templates/utils/getPackTemplateItemDetailOptions';
+import SyncBanner from '@packrat/app/packs/components/SyncBanner';
+import { getPackDetailOptions } from '@packrat/app/packs/utils/getPackDetailOptions';
+import { getPackItemDetailOptions } from '@packrat/app/packs/utils/getPackItemDetailOptions';
+import { getTripDetailOptions } from '@packrat/app/trips/utils/getTripDetailOptions';
import { ActivityIndicator } from '@packrat/ui/nativewindui';
-import { ThemeToggle } from 'expo-app/components/ThemeToggle';
-import { isLoadingAtom, needsReauthAtom } from 'expo-app/features/auth/atoms/authAtoms';
-import { useAuthInit } from 'expo-app/features/auth/hooks/useAuthInit';
-import { isAuthed } from 'expo-app/features/auth/store';
-import { getPackTemplateDetailOptions } from 'expo-app/features/pack-templates/utils/getPackTemplateDetailOptions';
-import { getPackTemplateItemDetailOptions } from 'expo-app/features/pack-templates/utils/getPackTemplateItemDetailOptions';
-import SyncBanner from 'expo-app/features/packs/components/SyncBanner';
-import { getPackDetailOptions } from 'expo-app/features/packs/utils/getPackDetailOptions';
-import { getPackItemDetailOptions } from 'expo-app/features/packs/utils/getPackItemDetailOptions';
-import { getTripDetailOptions } from 'expo-app/features/trips/utils/getTripDetailOptions';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
-import type { TranslationFunction } from 'expo-app/lib/i18n/types';
-import { testIds } from 'expo-app/lib/testIds';
import 'expo-dev-client';
import { type Href, router, Stack, useRouter } from 'expo-router';
import { useAtomValue } from 'jotai';
diff --git a/apps/expo/app/(app)/admin/ai-packs.tsx b/apps/expo/app/(app)/admin/ai-packs.tsx
index 95c501d116..baea24010a 100644
--- a/apps/expo/app/(app)/admin/ai-packs.tsx
+++ b/apps/expo/app/(app)/admin/ai-packs.tsx
@@ -1,4 +1,4 @@
-import { AIPacksScreen } from 'expo-app/features/ai-packs/screens/AIPacksScreen';
+import { AIPacksScreen } from '@packrat/app/ai-packs/screens/AIPacksScreen';
export default function AIPacks() {
return ;
diff --git a/apps/expo/app/(app)/ai-chat.tsx b/apps/expo/app/(app)/ai-chat.tsx
index 0088688673..3bfee7655d 100644
--- a/apps/expo/app/(app)/ai-chat.tsx
+++ b/apps/expo/app/(app)/ai-chat.tsx
@@ -1,31 +1,34 @@
import { type UIMessage, useChat } from '@ai-sdk/react';
+import { aiModeAtom, localModelStatusAtom } from '@packrat/app/ai/atoms/aiModeAtoms';
+import {
+ clearChatMessages,
+ loadChatMessages,
+ saveChatMessages,
+} from '@packrat/app/ai/atoms/chatStorageAtoms';
+import { ChatBubble } from '@packrat/app/ai/components/ChatBubble';
+import { ErrorState } from '@packrat/app/ai/components/ErrorState';
+import { LocationContext } from '@packrat/app/ai/components/LocationContext';
+import { CustomChatTransport } from '@packrat/app/ai/lib/CustomChatTransport';
+import { getLocalModel, initLocalModel } from '@packrat/app/ai/lib/localModelManager';
+import { createLocalTools } from '@packrat/app/ai/lib/tools';
+import { tokenAtom } from '@packrat/app/auth/atoms/authAtoms';
+import { AiChatHeader } from '@packrat/app/components/ai-chatHeader';
+import { Icon } from '@packrat/app/components/Icon';
+import { TextInput } from '@packrat/app/components/TextInput';
+import { featureFlags } from '@packrat/app/config';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
+import {
+ getContextualGreeting,
+ getContextualSuggestions,
+} from '@packrat/app/utils/chatContextHelpers';
+import { useActiveLocation } from '@packrat/app/weather/hooks';
+import type { WeatherLocation } from '@packrat/app/weather/types';
import { clientEnvs } from '@packrat/env/expo-client';
import { ActivityIndicator, Button, Text } from '@packrat/ui/nativewindui';
import { DefaultChatTransport, type TextUIPart } from 'ai';
import * as Burnt from 'burnt';
import { fetch as expoFetch } from 'expo/fetch';
-import { AiChatHeader } from 'expo-app/components/ai-chatHeader';
-import { Icon } from 'expo-app/components/Icon';
-import { TextInput } from 'expo-app/components/TextInput';
-import { featureFlags } from 'expo-app/config';
-import { aiModeAtom, localModelStatusAtom } from 'expo-app/features/ai/atoms/aiModeAtoms';
-import {
- clearChatMessages,
- loadChatMessages,
- saveChatMessages,
-} from 'expo-app/features/ai/atoms/chatStorageAtoms';
-import { ChatBubble } from 'expo-app/features/ai/components/ChatBubble';
-import { ErrorState } from 'expo-app/features/ai/components/ErrorState';
-import { LocationContext } from 'expo-app/features/ai/components/LocationContext';
-import { CustomChatTransport } from 'expo-app/features/ai/lib/CustomChatTransport';
-import { getLocalModel, initLocalModel } from 'expo-app/features/ai/lib/localModelManager';
-import { createLocalTools } from 'expo-app/features/ai/lib/tools';
-import { tokenAtom } from 'expo-app/features/auth/atoms/authAtoms';
-import { useActiveLocation } from 'expo-app/features/weather/hooks';
-import type { WeatherLocation } from 'expo-app/features/weather/types';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
-import { getContextualGreeting, getContextualSuggestions } from 'expo-app/utils/chatContextHelpers';
import { BlurView } from 'expo-blur';
import { Stack, useLocalSearchParams } from 'expo-router';
import { useAtomValue } from 'jotai';
diff --git a/apps/expo/app/(app)/catalog/[id].tsx b/apps/expo/app/(app)/catalog/[id].tsx
index a9b7e076eb..4a54619997 100644
--- a/apps/expo/app/(app)/catalog/[id].tsx
+++ b/apps/expo/app/(app)/catalog/[id].tsx
@@ -1,4 +1,4 @@
-import { CatalogItemDetailScreen } from 'expo-app/features/catalog/screens/CatalogItemDetailScreen';
+import { CatalogItemDetailScreen } from '@packrat/app/catalog/screens/CatalogItemDetailScreen';
export default function CatalogItemDetailPage() {
return ;
diff --git a/apps/expo/app/(app)/catalog/add-to-pack/details.tsx b/apps/expo/app/(app)/catalog/add-to-pack/details.tsx
index bb5ca85870..e9b1af809b 100644
--- a/apps/expo/app/(app)/catalog/add-to-pack/details.tsx
+++ b/apps/expo/app/(app)/catalog/add-to-pack/details.tsx
@@ -1,4 +1,4 @@
-import { AddCatalogItemDetailsScreen } from 'expo-app/features/catalog/screens/AddCatalogItemDetailsScreen';
+import { AddCatalogItemDetailsScreen } from '@packrat/app/catalog/screens/AddCatalogItemDetailsScreen';
export default function AddCatalogItemDetailsPage() {
return ;
diff --git a/apps/expo/app/(app)/catalog/add-to-pack/index.tsx b/apps/expo/app/(app)/catalog/add-to-pack/index.tsx
index 3328c1f4a0..476c5d367d 100644
--- a/apps/expo/app/(app)/catalog/add-to-pack/index.tsx
+++ b/apps/expo/app/(app)/catalog/add-to-pack/index.tsx
@@ -1,4 +1,4 @@
-import { PackSelectionScreen } from 'expo-app/features/catalog/screens/PackSelectionScreen';
+import { PackSelectionScreen } from '@packrat/app/catalog/screens/PackSelectionScreen';
export default function PackSelectionPage() {
return ;
diff --git a/apps/expo/app/(app)/consent-modal.tsx b/apps/expo/app/(app)/consent-modal.tsx
index d8658cf152..68c9eb5f61 100644
--- a/apps/expo/app/(app)/consent-modal.tsx
+++ b/apps/expo/app/(app)/consent-modal.tsx
@@ -1,5 +1,5 @@
-// import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { WelcomeConsentScreen } from 'expo-app/screens/ConsentWelcomeScreen';
+// import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { WelcomeConsentScreen } from '@packrat/app/screens/ConsentWelcomeScreen';
export default function ModalScreen() {
// const { colors, colorScheme } = useColorScheme();
diff --git a/apps/expo/app/(app)/current-pack/[id].tsx b/apps/expo/app/(app)/current-pack/[id].tsx
index 2a37709625..18e35e97e7 100644
--- a/apps/expo/app/(app)/current-pack/[id].tsx
+++ b/apps/expo/app/(app)/current-pack/[id].tsx
@@ -1,3 +1,11 @@
+import { userStore } from '@packrat/app/auth/store';
+import { cn } from '@packrat/app/lib/cn';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
+import { getRelativeTime } from '@packrat/app/lib/utils/getRelativeTime';
+import { usePackDetailsFromStore } from '@packrat/app/packs/hooks/usePackDetailsFromStore';
+import { type CategorySummary, computeCategorySummaries } from '@packrat/app/packs/utils';
+import type { PackItem } from '@packrat/app/types';
import {
Avatar,
AvatarFallback,
@@ -5,14 +13,6 @@ import {
LargeTitleHeader,
Text,
} from '@packrat/ui/nativewindui';
-import { userStore } from 'expo-app/features/auth/store';
-import { usePackDetailsFromStore } from 'expo-app/features/packs/hooks/usePackDetailsFromStore';
-import { type CategorySummary, computeCategorySummaries } from 'expo-app/features/packs/utils';
-import { cn } from 'expo-app/lib/cn';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
-import { getRelativeTime } from 'expo-app/lib/utils/getRelativeTime';
-import type { PackItem } from 'expo-app/types';
import { useLocalSearchParams } from 'expo-router';
import type React from 'react';
import { ScrollView, View } from 'react-native';
diff --git a/apps/expo/app/(app)/demo/index.tsx b/apps/expo/app/(app)/demo/index.tsx
index d2cd374c29..25c1c6c107 100644
--- a/apps/expo/app/(app)/demo/index.tsx
+++ b/apps/expo/app/(app)/demo/index.tsx
@@ -1,11 +1,11 @@
+import { Card } from '@packrat/app/components/Card';
+import { Icon } from '@packrat/app/components/Icon';
+import { ThemeToggle } from '@packrat/app/components/ThemeToggle';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { useHeaderSearchBar } from '@packrat/app/lib/hooks/useHeaderSearchBar';
import { Button, LargeTitleHeader, Text } from '@packrat/ui/nativewindui';
import { useHeaderHeight } from '@react-navigation/elements';
import { FlashList } from '@shopify/flash-list';
-import { Card } from 'expo-app/components/Card';
-import { Icon } from 'expo-app/components/Icon';
-import { ThemeToggle } from 'expo-app/components/ThemeToggle';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { useHeaderSearchBar } from 'expo-app/lib/hooks/useHeaderSearchBar';
import { useRouter } from 'expo-router';
import { cssInterop } from 'nativewind';
import type * as React from 'react';
diff --git a/apps/expo/app/(app)/feed/[id].tsx b/apps/expo/app/(app)/feed/[id].tsx
index c928127dc5..f636371d97 100644
--- a/apps/expo/app/(app)/feed/[id].tsx
+++ b/apps/expo/app/(app)/feed/[id].tsx
@@ -1,8 +1,8 @@
+import { userStore } from '@packrat/app/auth/store';
+import { PostDetailScreen } from '@packrat/app/feed/screens/PostDetailScreen';
+import { apiClient } from '@packrat/app/lib/api/packrat';
import { Text } from '@packrat/ui/nativewindui';
import { useQuery } from '@tanstack/react-query';
-import { userStore } from 'expo-app/features/auth/store';
-import { PostDetailScreen } from 'expo-app/features/feed';
-import { apiClient } from 'expo-app/lib/api/packrat';
import { useLocalSearchParams } from 'expo-router';
import { ActivityIndicator, View } from 'react-native';
diff --git a/apps/expo/app/(app)/feed/create.tsx b/apps/expo/app/(app)/feed/create.tsx
index 41603d7f8e..61c2b36b45 100644
--- a/apps/expo/app/(app)/feed/create.tsx
+++ b/apps/expo/app/(app)/feed/create.tsx
@@ -1,4 +1,4 @@
-import { CreatePostScreen } from 'expo-app/features/feed';
+import { CreatePostScreen } from '@packrat/app/feed/screens/CreatePostScreen';
import { useRouter } from 'expo-router';
export default function CreatePostRoute() {
diff --git a/apps/expo/app/(app)/gear-inventory.tsx b/apps/expo/app/(app)/gear-inventory.tsx
index f2fa21b2ed..01d78c673c 100644
--- a/apps/expo/app/(app)/gear-inventory.tsx
+++ b/apps/expo/app/(app)/gear-inventory.tsx
@@ -1,10 +1,10 @@
+import { cn } from '@packrat/app/lib/cn';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
+import type { PackItem } from '@packrat/app/packs';
+import { PackItemCard } from '@packrat/app/packs/components/PackItemCard';
+import { useUserPackItems } from '@packrat/app/packs/hooks/useUserPackItems';
import { assertDefined } from '@packrat/guards';
import { LargeTitleHeader, Text } from '@packrat/ui/nativewindui';
-import { PackItemCard } from 'expo-app/features/packs/components/PackItemCard';
-import { useUserPackItems } from 'expo-app/features/packs/hooks/useUserPackItems';
-import type { PackItem } from 'expo-app/features/packs/types';
-import { cn } from 'expo-app/lib/cn';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { useState } from 'react';
import { Pressable, ScrollView, View } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
diff --git a/apps/expo/app/(app)/guides/[id].tsx b/apps/expo/app/(app)/guides/[id].tsx
index 2e70061f1b..6ff79bb1ae 100644
--- a/apps/expo/app/(app)/guides/[id].tsx
+++ b/apps/expo/app/(app)/guides/[id].tsx
@@ -1,4 +1,4 @@
-import { GuideDetailScreen } from 'expo-app/features/guides/screens/GuideDetailScreen';
+import { GuideDetailScreen } from '@packrat/app/guides/screens/GuideDetailScreen';
export default function GuideDetailRoute() {
return ;
diff --git a/apps/expo/app/(app)/guides/index.tsx b/apps/expo/app/(app)/guides/index.tsx
index 389aab8e3c..eb4d9d4377 100644
--- a/apps/expo/app/(app)/guides/index.tsx
+++ b/apps/expo/app/(app)/guides/index.tsx
@@ -1,4 +1,4 @@
-import { GuidesListScreen } from 'expo-app/features/guides/screens/GuidesListScreen';
+import { GuidesListScreen } from '@packrat/app/guides/screens/GuidesListScreen';
export default function GuidesRoute() {
return ;
diff --git a/apps/expo/app/(app)/item/[id]/edit.tsx b/apps/expo/app/(app)/item/[id]/edit.tsx
index b5078c1e11..cb51f6d620 100644
--- a/apps/expo/app/(app)/item/[id]/edit.tsx
+++ b/apps/expo/app/(app)/item/[id]/edit.tsx
@@ -1,4 +1,4 @@
-import { EditPackItemScreen } from 'expo-app/features/packs/screens/EditPackItemScreen';
+import { EditPackItemScreen } from '@packrat/app/packs/screens/EditPackItemScreen';
export default function EditItemRoute() {
return ;
diff --git a/apps/expo/app/(app)/item/[id]/index.tsx b/apps/expo/app/(app)/item/[id]/index.tsx
index 01f795356e..e0a660f587 100644
--- a/apps/expo/app/(app)/item/[id]/index.tsx
+++ b/apps/expo/app/(app)/item/[id]/index.tsx
@@ -1,4 +1,4 @@
-import { ItemDetailScreen } from 'expo-app/features/packs/screens/PackItemDetailScreen';
+import { ItemDetailScreen } from '@packrat/app/packs/screens/PackItemDetailScreen';
export default function ItemDetailRoute() {
return ;
diff --git a/apps/expo/app/(app)/item/new.tsx b/apps/expo/app/(app)/item/new.tsx
index 4c22028ae2..6c97daf4e8 100644
--- a/apps/expo/app/(app)/item/new.tsx
+++ b/apps/expo/app/(app)/item/new.tsx
@@ -1,4 +1,4 @@
-import { CreatePackItemForm } from 'expo-app/features/packs/screens/CreatePackItemForm';
+import { CreatePackItemForm } from '@packrat/app/packs/screens/CreatePackItemForm';
import { useLocalSearchParams } from 'expo-router';
export default function NewItemScreen() {
diff --git a/apps/expo/app/(app)/messages/chat.android.tsx b/apps/expo/app/(app)/messages/chat.android.tsx
index 34ebcd27dc..49fea0e783 100644
--- a/apps/expo/app/(app)/messages/chat.android.tsx
+++ b/apps/expo/app/(app)/messages/chat.android.tsx
@@ -1,3 +1,7 @@
+import { Icon } from '@packrat/app/components/Icon';
+import { TextInput } from '@packrat/app/components/TextInput';
+import { cn } from '@packrat/app/lib/cn';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
import { isString } from '@packrat/guards';
import {
Avatar,
@@ -10,10 +14,6 @@ import {
} from '@packrat/ui/nativewindui';
import { Portal } from '@rn-primitives/portal';
import { FlashList } from '@shopify/flash-list';
-import { Icon } from 'expo-app/components/Icon';
-import { TextInput } from 'expo-app/components/TextInput';
-import { cn } from 'expo-app/lib/cn';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
import { router, Stack } from 'expo-router';
import * as React from 'react';
import {
diff --git a/apps/expo/app/(app)/messages/chat.tsx b/apps/expo/app/(app)/messages/chat.tsx
index f6c94459ad..e89b09235e 100644
--- a/apps/expo/app/(app)/messages/chat.tsx
+++ b/apps/expo/app/(app)/messages/chat.tsx
@@ -1,3 +1,7 @@
+import { Icon } from '@packrat/app/components/Icon';
+import { TextInput } from '@packrat/app/components/TextInput';
+import { cn } from '@packrat/app/lib/cn';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
import { assertDefined, isString } from '@packrat/guards';
import type { ContextMenuMethods } from '@packrat/ui/nativewindui';
import {
@@ -9,10 +13,6 @@ import {
Text,
} from '@packrat/ui/nativewindui';
import { FlashList } from '@shopify/flash-list';
-import { Icon } from 'expo-app/components/Icon';
-import { TextInput } from 'expo-app/components/TextInput';
-import { cn } from 'expo-app/lib/cn';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
import { BlurView } from 'expo-blur';
import { router, Stack } from 'expo-router';
import * as React from 'react';
diff --git a/apps/expo/app/(app)/messages/conversations.android.tsx b/apps/expo/app/(app)/messages/conversations.android.tsx
index 742d7bfa52..cba414b56b 100644
--- a/apps/expo/app/(app)/messages/conversations.android.tsx
+++ b/apps/expo/app/(app)/messages/conversations.android.tsx
@@ -1,3 +1,6 @@
+import { Icon } from '@packrat/app/components/Icon';
+import { cn } from '@packrat/app/lib/cn';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
import { assertDefined } from '@packrat/guards';
import {
AdaptiveSearchHeader,
@@ -16,9 +19,6 @@ import {
ToolbarCTA,
} from '@packrat/ui/nativewindui';
import { Portal } from '@rn-primitives/portal';
-import { Icon } from 'expo-app/components/Icon';
-import { cn } from 'expo-app/lib/cn';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
import * as Haptics from 'expo-haptics';
import { router } from 'expo-router';
import * as React from 'react';
diff --git a/apps/expo/app/(app)/messages/conversations.tsx b/apps/expo/app/(app)/messages/conversations.tsx
index 676d8e7c29..c6f477f6fb 100644
--- a/apps/expo/app/(app)/messages/conversations.tsx
+++ b/apps/expo/app/(app)/messages/conversations.tsx
@@ -1,3 +1,6 @@
+import { Icon } from '@packrat/app/components/Icon';
+import { cn } from '@packrat/app/lib/cn';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
import { assertDefined } from '@packrat/guards';
import {
Avatar,
@@ -15,9 +18,6 @@ import {
Text,
Toolbar,
} from '@packrat/ui/nativewindui';
-import { Icon } from 'expo-app/components/Icon';
-import { cn } from 'expo-app/lib/cn';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
import * as Haptics from 'expo-haptics';
import { router } from 'expo-router';
import * as React from 'react';
diff --git a/apps/expo/app/(app)/pack-categories/[id].tsx b/apps/expo/app/(app)/pack-categories/[id].tsx
index 4f8f97b771..8e4a1b42a6 100644
--- a/apps/expo/app/(app)/pack-categories/[id].tsx
+++ b/apps/expo/app/(app)/pack-categories/[id].tsx
@@ -1,10 +1,10 @@
+import { userStore } from '@packrat/app/auth/store';
+import { Icon, type MaterialIconName } from '@packrat/app/components/Icon';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
+import { usePackDetailsFromStore } from '@packrat/app/packs/hooks/usePackDetailsFromStore';
+import { computeCategorySummaries } from '@packrat/app/packs/utils';
import { LargeTitleHeader, Text } from '@packrat/ui/nativewindui';
-import { Icon, type MaterialIconName } from 'expo-app/components/Icon';
-import { userStore } from 'expo-app/features/auth/store';
-import { usePackDetailsFromStore } from 'expo-app/features/packs/hooks/usePackDetailsFromStore';
-import { computeCategorySummaries } from 'expo-app/features/packs/utils';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { useLocalSearchParams } from 'expo-router';
import { ScrollView, View } from 'react-native';
diff --git a/apps/expo/app/(app)/pack-stats/[id].tsx b/apps/expo/app/(app)/pack-stats/[id].tsx
index 3277f6d134..869d4bfdd7 100644
--- a/apps/expo/app/(app)/pack-stats/[id].tsx
+++ b/apps/expo/app/(app)/pack-stats/[id].tsx
@@ -1,10 +1,10 @@
+import { userStore } from '@packrat/app/auth/store';
+import { featureFlags } from '@packrat/app/config';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
+import { usePackDetailsFromStore } from '@packrat/app/packs/hooks/usePackDetailsFromStore';
+import { usePackWeightHistory } from '@packrat/app/packs/hooks/usePackWeightHistory';
+import { computeCategorySummaries } from '@packrat/app/packs/utils';
import { LargeTitleHeader, Text } from '@packrat/ui/nativewindui';
-import { featureFlags } from 'expo-app/config';
-import { userStore } from 'expo-app/features/auth/store';
-import { usePackDetailsFromStore } from 'expo-app/features/packs/hooks/usePackDetailsFromStore';
-import { usePackWeightHistory } from 'expo-app/features/packs/hooks/usePackWeightHistory';
-import { computeCategorySummaries } from 'expo-app/features/packs/utils';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { useLocalSearchParams } from 'expo-router';
import { ScrollView, View } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
diff --git a/apps/expo/app/(app)/pack-templates/[id]/edit.tsx b/apps/expo/app/(app)/pack-templates/[id]/edit.tsx
index ef63bd7d42..8b6b9b6c3c 100644
--- a/apps/expo/app/(app)/pack-templates/[id]/edit.tsx
+++ b/apps/expo/app/(app)/pack-templates/[id]/edit.tsx
@@ -1,4 +1,4 @@
-import { EditPackTemplateScreen } from 'expo-app/features/pack-templates/screens/EditPackTemplateScreen';
+import { EditPackTemplateScreen } from '@packrat/app/pack-templates/screens/EditPackTemplateScreen';
export default function EditPackTemplateScreenRoute() {
return ;
diff --git a/apps/expo/app/(app)/pack-templates/[id]/index.tsx b/apps/expo/app/(app)/pack-templates/[id]/index.tsx
index bd92b8fa3f..800e75c8d9 100644
--- a/apps/expo/app/(app)/pack-templates/[id]/index.tsx
+++ b/apps/expo/app/(app)/pack-templates/[id]/index.tsx
@@ -1,4 +1,4 @@
-import { PackTemplateDetailScreen } from 'expo-app/features/pack-templates/screens/PackTemplateDetailScreen';
+import { PackTemplateDetailScreen } from '@packrat/app/pack-templates/screens/PackTemplateDetailScreen';
export default function PackTemplateDetailScreenRoute() {
return ;
diff --git a/apps/expo/app/(app)/pack-templates/index.tsx b/apps/expo/app/(app)/pack-templates/index.tsx
index 002a312723..1a58946a2e 100644
--- a/apps/expo/app/(app)/pack-templates/index.tsx
+++ b/apps/expo/app/(app)/pack-templates/index.tsx
@@ -1,4 +1,4 @@
-import { PackTemplateListScreen } from 'expo-app/features/pack-templates/screens/PackTemplateListScreen';
+import { PackTemplateListScreen } from '@packrat/app/pack-templates/screens/PackTemplateListScreen';
export default function () {
return ;
diff --git a/apps/expo/app/(app)/pack-templates/items-scan.tsx b/apps/expo/app/(app)/pack-templates/items-scan.tsx
index fd170df957..cce5ee86a9 100644
--- a/apps/expo/app/(app)/pack-templates/items-scan.tsx
+++ b/apps/expo/app/(app)/pack-templates/items-scan.tsx
@@ -1,4 +1,4 @@
-import { ItemsScanScreen } from 'expo-app/features/pack-templates/screens/ItemsScanScreen';
+import { ItemsScanScreen } from '@packrat/app/pack-templates/screens/ItemsScanScreen';
export default function PackNewFromImageScreen() {
return ;
diff --git a/apps/expo/app/(app)/pack-templates/new.tsx b/apps/expo/app/(app)/pack-templates/new.tsx
index b913fdc6d3..9221ed0dca 100644
--- a/apps/expo/app/(app)/pack-templates/new.tsx
+++ b/apps/expo/app/(app)/pack-templates/new.tsx
@@ -1,4 +1,4 @@
-import { CreateTemplatePackScreen } from 'expo-app/features/pack-templates/screens/CreatePackTemplateScreen';
+import { CreateTemplatePackScreen } from '@packrat/app/pack-templates/screens/CreatePackTemplateScreen';
export default function PackNewScreen() {
return ;
diff --git a/apps/expo/app/(app)/pack/[id]/edit.tsx b/apps/expo/app/(app)/pack/[id]/edit.tsx
index d0bf6dd2b2..057024aac5 100644
--- a/apps/expo/app/(app)/pack/[id]/edit.tsx
+++ b/apps/expo/app/(app)/pack/[id]/edit.tsx
@@ -1,4 +1,4 @@
-import { EditPackScreen } from 'expo-app/features/packs/screens/EditPackScreen';
+import { EditPackScreen } from '@packrat/app/packs/screens/EditPackScreen';
export default function EditPackScreenRoute() {
return ;
diff --git a/apps/expo/app/(app)/pack/[id]/index.tsx b/apps/expo/app/(app)/pack/[id]/index.tsx
index 7bed2e5891..d7544a8086 100644
--- a/apps/expo/app/(app)/pack/[id]/index.tsx
+++ b/apps/expo/app/(app)/pack/[id]/index.tsx
@@ -1,4 +1,4 @@
-import { PackDetailScreen } from 'expo-app/features/packs/screens/PackDetailScreen';
+import { PackDetailScreen } from '@packrat/app/packs/screens/PackDetailScreen';
export default function PackDetailScreenRoute() {
return ;
diff --git a/apps/expo/app/(app)/pack/items-scan.tsx b/apps/expo/app/(app)/pack/items-scan.tsx
index e6ee9cf760..55e41f14bd 100644
--- a/apps/expo/app/(app)/pack/items-scan.tsx
+++ b/apps/expo/app/(app)/pack/items-scan.tsx
@@ -1,4 +1,4 @@
-import { ItemsScanScreen } from 'expo-app/features/packs/screens/ItemsScanScreen';
+import { ItemsScanScreen } from '@packrat/app/packs/screens/ItemsScanScreen';
export default function PackNewFromImageScreen() {
return ;
diff --git a/apps/expo/app/(app)/pack/new.tsx b/apps/expo/app/(app)/pack/new.tsx
index 2ecd9df5c7..1c2b746c95 100644
--- a/apps/expo/app/(app)/pack/new.tsx
+++ b/apps/expo/app/(app)/pack/new.tsx
@@ -1,4 +1,4 @@
-import { CreatePackScreen } from 'expo-app/features/packs/screens/CreatePackScreen';
+import { CreatePackScreen } from '@packrat/app/packs/screens/CreatePackScreen';
export default function PackNewScreen() {
return ;
diff --git a/apps/expo/app/(app)/recent-packs.tsx b/apps/expo/app/(app)/recent-packs.tsx
index 6ab73c53fb..d530df071c 100644
--- a/apps/expo/app/(app)/recent-packs.tsx
+++ b/apps/expo/app/(app)/recent-packs.tsx
@@ -1,10 +1,10 @@
+import { Icon } from '@packrat/app/components/Icon';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
+import { getRelativeTime } from '@packrat/app/lib/utils/getRelativeTime';
+import type { Pack } from '@packrat/app/packs';
+import { useRecentPacks } from '@packrat/app/packs/hooks/useRecentPacks';
import { LargeTitleHeader, Text } from '@packrat/ui/nativewindui';
-import { Icon } from 'expo-app/components/Icon';
-import type { Pack } from 'expo-app/features/packs';
-import { useRecentPacks } from 'expo-app/features/packs/hooks/useRecentPacks';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
-import { getRelativeTime } from 'expo-app/lib/utils/getRelativeTime';
import { Image, ScrollView, View } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
diff --git a/apps/expo/app/(app)/reported-ai-content.tsx b/apps/expo/app/(app)/reported-ai-content.tsx
index 4b8f114f59..b4f8f45802 100644
--- a/apps/expo/app/(app)/reported-ai-content.tsx
+++ b/apps/expo/app/(app)/reported-ai-content.tsx
@@ -1,3 +1,3 @@
-import ReportedContentScreen from 'expo-app/features/ai/screens/ReportedContentScreen';
+import ReportedContentScreen from '@packrat/app/ai/screens/ReportedContentScreen';
export default ReportedContentScreen;
diff --git a/apps/expo/app/(app)/season-suggestions.tsx b/apps/expo/app/(app)/season-suggestions.tsx
index fda46f799e..a74fed85ed 100644
--- a/apps/expo/app/(app)/season-suggestions.tsx
+++ b/apps/expo/app/(app)/season-suggestions.tsx
@@ -1,14 +1,14 @@
-import { assertDefined } from '@packrat/guards';
-import { Button, LargeTitleHeader, Text, useColorScheme } from '@packrat/ui/nativewindui';
-import { Icon } from 'expo-app/components/Icon';
-import { useCreatePackWithItems } from 'expo-app/features/packs/hooks/useCreatePackWithItems';
+import { Icon } from '@packrat/app/components/Icon';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
+import { useCreatePackWithItems } from '@packrat/app/packs/hooks/useCreatePackWithItems';
import {
type PackSuggestion,
useSeasonSuggestions,
-} from 'expo-app/features/packs/hooks/useSeasonSuggestions';
-import { LocationPicker } from 'expo-app/features/weather/components';
-import type { WeatherLocation } from 'expo-app/features/weather/types';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
+} from '@packrat/app/packs/hooks/useSeasonSuggestions';
+import { LocationPicker } from '@packrat/app/weather/components';
+import type { WeatherLocation } from '@packrat/app/weather/types';
+import { assertDefined } from '@packrat/guards';
+import { Button, LargeTitleHeader, Text, useColorScheme } from '@packrat/ui/nativewindui';
import { useRouter } from 'expo-router';
import { useState } from 'react';
import { ActivityIndicator, ScrollView, View } from 'react-native';
diff --git a/apps/expo/app/(app)/settings/index.tsx b/apps/expo/app/(app)/settings/index.tsx
index 0ec2f40c8f..9cabe83ec7 100644
--- a/apps/expo/app/(app)/settings/index.tsx
+++ b/apps/expo/app/(app)/settings/index.tsx
@@ -1,23 +1,23 @@
-import { ActivityIndicator, Text } from '@packrat/ui/nativewindui';
-import * as Burnt from 'burnt';
-import { appAlert } from 'expo-app/app/_layout';
-import { Icon, type MaterialIconName } from 'expo-app/components/Icon';
+import { LLAMA_MODEL_SIZE } from '@packrat/app/ai';
import {
localModelFileAvailableAtom,
localModelProgressAtom,
localModelStatusAtom,
-} from 'expo-app/features/ai/atoms/aiModeAtoms';
-import { LLAMA_MODEL_SIZE } from 'expo-app/features/ai/lib/constants';
+} from '@packrat/app/ai/atoms/aiModeAtoms';
import {
cancelLocalModelDownload,
deleteLocalModel,
downloadLocalModel,
isAppleIntelligenceAvailable,
-} from 'expo-app/features/ai/lib/localModelManager';
-import { DeleteAccountButton } from 'expo-app/features/auth/components/DeleteAccountButton';
-import { useAuth } from 'expo-app/features/auth/hooks/useAuth';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
+} from '@packrat/app/ai/lib/localModelManager';
+import { DeleteAccountButton } from '@packrat/app/auth/components/DeleteAccountButton';
+import { useAuth } from '@packrat/app/auth/hooks/useAuth';
+import { Icon, type MaterialIconName } from '@packrat/app/components/Icon';
+import { appAlert } from '@packrat/app/lib/appAlert';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
+import { ActivityIndicator, Text } from '@packrat/ui/nativewindui';
+import * as Burnt from 'burnt';
import Constants from 'expo-constants';
import { StatusBar } from 'expo-status-bar';
import { useAtomValue } from 'jotai';
diff --git a/apps/expo/app/(app)/shared-packs.tsx b/apps/expo/app/(app)/shared-packs.tsx
index 76ab6cf6ed..8ae1991254 100644
--- a/apps/expo/app/(app)/shared-packs.tsx
+++ b/apps/expo/app/(app)/shared-packs.tsx
@@ -1,3 +1,5 @@
+import { cn } from '@packrat/app/lib/cn';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
import {
Avatar,
AvatarFallback,
@@ -5,8 +7,6 @@ import {
LargeTitleHeader,
Text,
} from '@packrat/ui/nativewindui';
-import { cn } from 'expo-app/lib/cn';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { ScrollView, View } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
diff --git a/apps/expo/app/(app)/shopping-list.tsx b/apps/expo/app/(app)/shopping-list.tsx
index 931afdd7e4..d252689b43 100644
--- a/apps/expo/app/(app)/shopping-list.tsx
+++ b/apps/expo/app/(app)/shopping-list.tsx
@@ -1,11 +1,11 @@
'use client';
+import { Icon } from '@packrat/app/components/Icon';
+import { cn } from '@packrat/app/lib/cn';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
+import type { TranslationKeys } from '@packrat/app/lib/i18n/types';
import { LargeTitleHeader, Text } from '@packrat/ui/nativewindui';
-import { Icon } from 'expo-app/components/Icon';
-import { cn } from 'expo-app/lib/cn';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
-import type { TranslationKeys } from 'expo-app/lib/i18n/types';
import { useState } from 'react';
import { Pressable, ScrollView, View } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
diff --git a/apps/expo/app/(app)/templateItem/[id]/edit.tsx b/apps/expo/app/(app)/templateItem/[id]/edit.tsx
index 49c9565822..cfbe64c94a 100644
--- a/apps/expo/app/(app)/templateItem/[id]/edit.tsx
+++ b/apps/expo/app/(app)/templateItem/[id]/edit.tsx
@@ -1,4 +1,4 @@
-import { EditPackTemplateItemScreen } from 'expo-app/features/pack-templates/screens/EditPackTemplateItemScreen';
+import { EditPackTemplateItemScreen } from '@packrat/app/pack-templates/screens/EditPackTemplateItemScreen';
export default function EditTemplateItemRoute() {
return ;
diff --git a/apps/expo/app/(app)/templateItem/[id]/index.tsx b/apps/expo/app/(app)/templateItem/[id]/index.tsx
index 0c07ac815c..0aac8b9e5a 100644
--- a/apps/expo/app/(app)/templateItem/[id]/index.tsx
+++ b/apps/expo/app/(app)/templateItem/[id]/index.tsx
@@ -1,4 +1,4 @@
-import { PackTemplateItemDetailScreen } from 'expo-app/features/pack-templates/screens/PackTemplateItemDetailScreen';
+import { PackTemplateItemDetailScreen } from '@packrat/app/pack-templates/screens/PackTemplateItemDetailScreen';
export default function TemplateItemDetailRoute() {
return ;
diff --git a/apps/expo/app/(app)/templateItem/new.tsx b/apps/expo/app/(app)/templateItem/new.tsx
index b561f36cd6..cb64eb7bb0 100644
--- a/apps/expo/app/(app)/templateItem/new.tsx
+++ b/apps/expo/app/(app)/templateItem/new.tsx
@@ -1,4 +1,4 @@
-import { CreatePackTemplateItemForm } from 'expo-app/features/pack-templates/screens/CreatePackTemplateItemForm';
+import { CreatePackTemplateItemForm } from '@packrat/app/pack-templates/screens/CreatePackTemplateItemForm';
import { useLocalSearchParams } from 'expo-router';
export default function NewTemplateItemScreen() {
diff --git a/apps/expo/app/(app)/trail-conditions.tsx b/apps/expo/app/(app)/trail-conditions.tsx
index 984a7cf2f3..2961387a03 100644
--- a/apps/expo/app/(app)/trail-conditions.tsx
+++ b/apps/expo/app/(app)/trail-conditions.tsx
@@ -1,10 +1,10 @@
+import { featureFlags } from '@packrat/app/config';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
+import type { TrailConditionReport, TrailSurface } from '@packrat/app/trail-conditions';
+import { SubmitConditionReportForm } from '@packrat/app/trail-conditions/components/SubmitConditionReportForm';
+import { TrailConditionReportCard } from '@packrat/app/trail-conditions/components/TrailConditionReportCard';
+import { useTrailConditionReports } from '@packrat/app/trail-conditions/hooks/useTrailConditionReports';
import { ActivityIndicator, LargeTitleHeader, Text } from '@packrat/ui/nativewindui';
-import { featureFlags } from 'expo-app/config';
-import { SubmitConditionReportForm } from 'expo-app/features/trail-conditions/components/SubmitConditionReportForm';
-import { TrailConditionReportCard } from 'expo-app/features/trail-conditions/components/TrailConditionReportCard';
-import { useTrailConditionReports } from 'expo-app/features/trail-conditions/hooks/useTrailConditionReports';
-import type { TrailConditionReport, TrailSurface } from 'expo-app/features/trail-conditions/types';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { useMemo, useState } from 'react';
import { FlatList, Modal, Pressable, ScrollView, View } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
diff --git a/apps/expo/app/(app)/trip/[id]/edit.tsx b/apps/expo/app/(app)/trip/[id]/edit.tsx
index dba18c615a..1074ba730e 100644
--- a/apps/expo/app/(app)/trip/[id]/edit.tsx
+++ b/apps/expo/app/(app)/trip/[id]/edit.tsx
@@ -1,4 +1,4 @@
-import { EditTripScreen } from 'expo-app/features/trips/screens/EditTripScreen';
+import { EditTripScreen } from '@packrat/app/trips/screens/EditTripScreen';
export default function EditTripScreenRoute() {
return ;
diff --git a/apps/expo/app/(app)/trip/[id]/index.tsx b/apps/expo/app/(app)/trip/[id]/index.tsx
index a3b37861c7..917c9c2d2d 100644
--- a/apps/expo/app/(app)/trip/[id]/index.tsx
+++ b/apps/expo/app/(app)/trip/[id]/index.tsx
@@ -1,5 +1,5 @@
-import { featureFlags } from 'expo-app/config';
-import { TripDetailScreen } from 'expo-app/features/trips/screens/TripDetailScreen';
+import { featureFlags } from '@packrat/app/config';
+import { TripDetailScreen } from '@packrat/app/trips/screens/TripDetailScreen';
import { Redirect } from 'expo-router';
export default function TripDetailScreenRoute() {
diff --git a/apps/expo/app/(app)/trip/location-search.tsx b/apps/expo/app/(app)/trip/location-search.tsx
index 6926aab0cd..683abcf958 100644
--- a/apps/expo/app/(app)/trip/location-search.tsx
+++ b/apps/expo/app/(app)/trip/location-search.tsx
@@ -1,14 +1,14 @@
+import { SearchInput } from '@packrat/app/components/SearchInput';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
+import { useTripLocation } from '@packrat/app/trips/store/tripLocationStore';
import { clientEnvs } from '@packrat/env/expo-client';
import { ActivityIndicator, Button } from '@packrat/ui/nativewindui';
-import { SearchInput } from 'expo-app/components/SearchInput';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import Constants from 'expo-constants';
import { useRouter } from 'expo-router';
import { useRef, useState } from 'react';
import { Alert, Text, View } from 'react-native';
import MapView, { Marker } from 'react-native-maps';
import { SafeAreaView } from 'react-native-safe-area-context';
-import { useTripLocation } from '../../../features/trips/store/tripLocationStore';
const GOOGLE_MAPS_API_KEY =
Constants.expoConfig?.extra?.googleMapsApiKey || clientEnvs.EXPO_PUBLIC_GOOGLE_MAPS_API_KEY;
diff --git a/apps/expo/app/(app)/trip/new.tsx b/apps/expo/app/(app)/trip/new.tsx
index 3c917107d2..dbebacfd9e 100644
--- a/apps/expo/app/(app)/trip/new.tsx
+++ b/apps/expo/app/(app)/trip/new.tsx
@@ -1,5 +1,5 @@
-import { featureFlags } from 'expo-app/config';
-import { CreateTripScreen } from 'expo-app/features/trips/screens/CreateTripScreen';
+import { featureFlags } from '@packrat/app/config';
+import { CreateTripScreen } from '@packrat/app/trips/screens/CreateTripScreen';
import { Redirect } from 'expo-router';
export default function TripNewScreen() {
diff --git a/apps/expo/app/(app)/upcoming-trips.tsx b/apps/expo/app/(app)/upcoming-trips.tsx
index 158e218331..dae40a3de8 100644
--- a/apps/expo/app/(app)/upcoming-trips.tsx
+++ b/apps/expo/app/(app)/upcoming-trips.tsx
@@ -1,14 +1,14 @@
+import { featureFlags } from '@packrat/app/config';
+import { cn } from '@packrat/app/lib/cn';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
+import type { TranslationFunction } from '@packrat/app/lib/i18n/types';
+import { useDetailedPacks } from '@packrat/app/packs/hooks/useDetailedPacks';
+import { useTrips } from '@packrat/app/trips/hooks';
import { List, ListItem, Text } from '@packrat/ui/nativewindui';
import { format } from 'date-fns';
-import { featureFlags } from 'expo-app/config';
-import { useTrips } from 'expo-app/features/trips/hooks';
-import { cn } from 'expo-app/lib/cn';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
-import type { TranslationFunction } from 'expo-app/lib/i18n/types';
import { Redirect } from 'expo-router';
import { useEffect, useMemo, useState } from 'react';
import { ScrollView, View } from 'react-native';
-import { useDetailedPacks } from '../../features/packs/hooks/useDetailedPacks';
function formatDate(dateString?: string) {
if (!dateString) return '—';
diff --git a/apps/expo/app/(app)/weather-alert-preferences.tsx b/apps/expo/app/(app)/weather-alert-preferences.tsx
index 1f58119699..41b8eb285e 100644
--- a/apps/expo/app/(app)/weather-alert-preferences.tsx
+++ b/apps/expo/app/(app)/weather-alert-preferences.tsx
@@ -1,3 +1,6 @@
+import { Icon } from '@packrat/app/components/Icon';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
import {
Form,
FormItem,
@@ -6,9 +9,6 @@ import {
Text,
Toggle,
} from '@packrat/ui/nativewindui';
-import { Icon } from 'expo-app/components/Icon';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import * as React from 'react';
import { ScrollView, View } from 'react-native';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
diff --git a/apps/expo/app/(app)/weather-alerts.tsx b/apps/expo/app/(app)/weather-alerts.tsx
index c02838c9a4..91ccb65625 100644
--- a/apps/expo/app/(app)/weather-alerts.tsx
+++ b/apps/expo/app/(app)/weather-alerts.tsx
@@ -1,9 +1,9 @@
import { MaterialCommunityIcons } from '@expo/vector-icons';
+import { cn } from '@packrat/app/lib/cn';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
+import { useWeatherAlerts } from '@packrat/app/weather/hooks/useWeatherAlert';
import { LargeTitleHeader, Text } from '@packrat/ui/nativewindui';
-import { useWeatherAlerts } from 'expo-app/features/weather/hooks/useWeatherAlert';
-import { cn } from 'expo-app/lib/cn';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { useRouter } from 'expo-router';
import { Pressable, ScrollView, View } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
diff --git a/apps/expo/app/(app)/weather/[id].tsx b/apps/expo/app/(app)/weather/[id].tsx
index 20eb723864..6d6105dfb5 100644
--- a/apps/expo/app/(app)/weather/[id].tsx
+++ b/apps/expo/app/(app)/weather/[id].tsx
@@ -1,4 +1,4 @@
-import { LocationDetailScreen } from 'expo-app/features/weather/screens';
+import { LocationDetailScreen } from '@packrat/app/weather/screens';
export default function LocationDetailIndexScreen() {
return ;
diff --git a/apps/expo/app/(app)/weather/geo.tsx b/apps/expo/app/(app)/weather/geo.tsx
index 5c1ade3e60..2b6cad153f 100644
--- a/apps/expo/app/(app)/weather/geo.tsx
+++ b/apps/expo/app/(app)/weather/geo.tsx
@@ -1,4 +1,4 @@
-import TripWeatherDetailsScreen from 'expo-app/features/trips/screens/TripWeatherDetailsScreen';
+import TripWeatherDetailsScreen from '@packrat/app/trips/screens/TripWeatherDetailsScreen';
export default function GeoWeatherDetailsScreen() {
return ;
diff --git a/apps/expo/app/(app)/weather/index.tsx b/apps/expo/app/(app)/weather/index.tsx
index ee8c67f846..78ecac111b 100644
--- a/apps/expo/app/(app)/weather/index.tsx
+++ b/apps/expo/app/(app)/weather/index.tsx
@@ -1,4 +1,4 @@
-import { LocationsScreen } from 'expo-app/features/weather/screens';
+import { LocationsScreen } from '@packrat/app/weather/screens';
export default function LocationsIndexScreen() {
return ;
diff --git a/apps/expo/app/(app)/weather/preview.tsx b/apps/expo/app/(app)/weather/preview.tsx
index 2db5fdfb4a..94e4c8fbf6 100644
--- a/apps/expo/app/(app)/weather/preview.tsx
+++ b/apps/expo/app/(app)/weather/preview.tsx
@@ -1,4 +1,4 @@
-import { LocationPreviewScreen } from 'expo-app/features/weather/screens';
+import { LocationPreviewScreen } from '@packrat/app/weather/screens';
export default function LocationPreviewIndexScreen() {
return ;
diff --git a/apps/expo/app/(app)/weather/search.tsx b/apps/expo/app/(app)/weather/search.tsx
index be625321fc..7b3e3b23d4 100644
--- a/apps/expo/app/(app)/weather/search.tsx
+++ b/apps/expo/app/(app)/weather/search.tsx
@@ -1,4 +1,4 @@
-import { LocationSearchScreen } from 'expo-app/features/weather/screens';
+import { LocationSearchScreen } from '@packrat/app/weather/screens';
export default function LocationSearchIndexScreen() {
return ;
diff --git a/apps/expo/app/(app)/weight-analysis/[id].tsx b/apps/expo/app/(app)/weight-analysis/[id].tsx
index 01978d9fd1..17dff2064e 100644
--- a/apps/expo/app/(app)/weight-analysis/[id].tsx
+++ b/apps/expo/app/(app)/weight-analysis/[id].tsx
@@ -1,10 +1,10 @@
'use client';
+import { userStore } from '@packrat/app/auth/store';
+import { cn } from '@packrat/app/lib/cn';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
+import { usePackWeightAnalysis } from '@packrat/app/packs/hooks/usePackWeightAnalysis';
import { LargeTitleHeader, Text } from '@packrat/ui/nativewindui';
-import { userStore } from 'expo-app/features/auth/store';
-import { usePackWeightAnalysis } from 'expo-app/features/packs/hooks/usePackWeightAnalysis';
-import { cn } from 'expo-app/lib/cn';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { useLocalSearchParams } from 'expo-router';
import { ScrollView, View } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
diff --git a/apps/expo/app/(app)/wildlife/[id].tsx b/apps/expo/app/(app)/wildlife/[id].tsx
index 2c90425f68..49fca8c50c 100644
--- a/apps/expo/app/(app)/wildlife/[id].tsx
+++ b/apps/expo/app/(app)/wildlife/[id].tsx
@@ -1,5 +1,5 @@
-import { featureFlags } from 'expo-app/config';
-import { SpeciesDetailScreen } from 'expo-app/features/wildlife/screens/SpeciesDetailScreen';
+import { featureFlags } from '@packrat/app/config';
+import { SpeciesDetailScreen } from '@packrat/app/wildlife/screens/SpeciesDetailScreen';
import { Redirect } from 'expo-router';
export default function SpeciesDetailRoute() {
diff --git a/apps/expo/app/(app)/wildlife/_layout.tsx b/apps/expo/app/(app)/wildlife/_layout.tsx
index dae2b4a976..348e9851e0 100644
--- a/apps/expo/app/(app)/wildlife/_layout.tsx
+++ b/apps/expo/app/(app)/wildlife/_layout.tsx
@@ -1,4 +1,4 @@
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
import { Stack } from 'expo-router';
export default function WildlifeLayout() {
diff --git a/apps/expo/app/(app)/wildlife/identify.tsx b/apps/expo/app/(app)/wildlife/identify.tsx
index 9de85cbf13..a2ac238aec 100644
--- a/apps/expo/app/(app)/wildlife/identify.tsx
+++ b/apps/expo/app/(app)/wildlife/identify.tsx
@@ -1,5 +1,5 @@
-import { featureFlags } from 'expo-app/config';
-import { IdentificationScreen } from 'expo-app/features/wildlife/screens/IdentificationScreen';
+import { featureFlags } from '@packrat/app/config';
+import { IdentificationScreen } from '@packrat/app/wildlife/screens/IdentificationScreen';
import { Redirect } from 'expo-router';
export default function IdentifyRoute() {
diff --git a/apps/expo/app/(app)/wildlife/index.tsx b/apps/expo/app/(app)/wildlife/index.tsx
index 47b5afb522..645b3fbba7 100644
--- a/apps/expo/app/(app)/wildlife/index.tsx
+++ b/apps/expo/app/(app)/wildlife/index.tsx
@@ -1,5 +1,5 @@
-import { featureFlags } from 'expo-app/config';
-import { WildlifeScreen } from 'expo-app/features/wildlife/screens/WildlifeScreen';
+import { featureFlags } from '@packrat/app/config';
+import { WildlifeScreen } from '@packrat/app/wildlife/screens/WildlifeScreen';
import { Redirect } from 'expo-router';
export default function WildlifeRoute() {
diff --git a/apps/expo/app/_layout.tsx b/apps/expo/app/_layout.tsx
index 81dd6426d5..a475c07a0a 100644
--- a/apps/expo/app/_layout.tsx
+++ b/apps/expo/app/_layout.tsx
@@ -6,13 +6,14 @@ import { Stack } from 'expo-router';
import { StatusBar } from 'expo-status-bar';
import '../global.css';
+import { userStore } from '@packrat/app/auth/store';
+import { registerAppAlert } from '@packrat/app/lib/appAlert';
+import { useColorScheme, useInitialAndroidBarSync } from '@packrat/app/lib/hooks/useColorScheme';
+import { Providers } from '@packrat/app/providers';
+import { NAV_THEME } from '@packrat/app/theme';
import { clientEnvs } from '@packrat/env/expo-client';
import { Alert, type AlertMethods } from '@packrat/ui/nativewindui';
import * as Sentry from '@sentry/react-native';
-import { userStore } from 'expo-app/features/auth/store';
-import { useColorScheme, useInitialAndroidBarSync } from 'expo-app/lib/hooks/useColorScheme';
-import { Providers } from 'expo-app/providers';
-import { NAV_THEME } from 'expo-app/theme';
import { useEffect, useRef } from 'react';
Sentry.init({
@@ -32,12 +33,11 @@ export {
ErrorBoundary,
} from 'expo-router';
-export let appAlert: React.RefObject;
-
function RootLayout() {
useInitialAndroidBarSync();
- appAlert = useRef(null);
+ const alertRef = useRef(null);
+ registerAppAlert(alertRef);
const { colorScheme, isDarkColorScheme } = useColorScheme();
@@ -59,7 +59,7 @@ function RootLayout() {
-
+
);
diff --git a/apps/expo/app/auth/(create-account)/_layout.tsx b/apps/expo/app/auth/(create-account)/_layout.tsx
index 30ec86f709..555b029cc6 100644
--- a/apps/expo/app/auth/(create-account)/_layout.tsx
+++ b/apps/expo/app/auth/(create-account)/_layout.tsx
@@ -1,4 +1,4 @@
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
import { Stack } from 'expo-router';
export default function CreateAccountLayout() {
diff --git a/apps/expo/app/auth/(create-account)/credentials.tsx b/apps/expo/app/auth/(create-account)/credentials.tsx
index 0c8ca8ebef..a1e569422a 100644
--- a/apps/expo/app/auth/(create-account)/credentials.tsx
+++ b/apps/expo/app/auth/(create-account)/credentials.tsx
@@ -1,3 +1,7 @@
+import { useAuthActions } from '@packrat/app/auth/hooks/useAuthActions';
+import { Icon } from '@packrat/app/components/Icon';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
+import type { TranslationKeys } from '@packrat/app/lib/i18n/types';
import type { AlertMethods } from '@packrat/ui/nativewindui';
import {
AlertAnchor,
@@ -10,10 +14,6 @@ import {
TextField,
} from '@packrat/ui/nativewindui';
import { useForm } from '@tanstack/react-form';
-import { Icon } from 'expo-app/components/Icon';
-import { useAuthActions } from 'expo-app/features/auth/hooks/useAuthActions';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
-import type { TranslationKeys } from 'expo-app/lib/i18n/types';
import { router, useLocalSearchParams } from 'expo-router';
import * as React from 'react';
import { Alert, Image, Platform, View } from 'react-native';
@@ -25,7 +25,7 @@ import {
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { z } from 'zod';
-const LOGO_SOURCE = require('expo-app/assets/packrat-app-icon-gradient.png');
+const LOGO_SOURCE = require('@packrat/app/assets/packrat-app-icon-gradient.png');
const RE_HAS_UPPERCASE = /[A-Z]/;
const RE_HAS_LOWERCASE = /[a-z]/;
diff --git a/apps/expo/app/auth/(create-account)/index.tsx b/apps/expo/app/auth/(create-account)/index.tsx
index 912b5e1654..ffb5a9bbcc 100644
--- a/apps/expo/app/auth/(create-account)/index.tsx
+++ b/apps/expo/app/auth/(create-account)/index.tsx
@@ -1,8 +1,8 @@
'use client';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
import { Button, Form, FormItem, FormSection, Text, TextField } from '@packrat/ui/nativewindui';
import { useForm } from '@tanstack/react-form';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { router } from 'expo-router';
import * as React from 'react';
import { Image, Platform, View } from 'react-native';
@@ -14,7 +14,7 @@ import {
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { z } from 'zod';
-const LOGO_SOURCE = require('expo-app/assets/packrat-app-icon-gradient.png');
+const LOGO_SOURCE = require('@packrat/app/assets/packrat-app-icon-gradient.png');
// Define Zod schema for name validation
const nameFormSchema = z.object({
diff --git a/apps/expo/app/auth/(login)/forgot-password.tsx b/apps/expo/app/auth/(login)/forgot-password.tsx
index b0360ec93b..7f2c36a915 100644
--- a/apps/expo/app/auth/(login)/forgot-password.tsx
+++ b/apps/expo/app/auth/(login)/forgot-password.tsx
@@ -1,3 +1,6 @@
+import { needsReauthAtom } from '@packrat/app/auth/atoms/authAtoms';
+import { useAuthActions } from '@packrat/app/auth/hooks/useAuthActions';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
import type { AlertMethods } from '@packrat/ui/nativewindui';
import {
AlertAnchor,
@@ -9,9 +12,6 @@ import {
TextField,
} from '@packrat/ui/nativewindui';
import { useForm } from '@tanstack/react-form';
-import { needsReauthAtom } from 'expo-app/features/auth/atoms/authAtoms';
-import { useAuthActions } from 'expo-app/features/auth/hooks/useAuthActions';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { router, Stack } from 'expo-router';
import { useAtomValue } from 'jotai';
import * as React from 'react';
@@ -20,7 +20,7 @@ import { KeyboardAwareScrollView, KeyboardStickyView } from 'react-native-keyboa
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { z } from 'zod';
-const LOGO_SOURCE = require('expo-app/assets/packrat-app-icon-gradient.png');
+const LOGO_SOURCE = require('@packrat/app/assets/packrat-app-icon-gradient.png');
// Define Zod schema for email validation
const emailSchema = z.object({
diff --git a/apps/expo/app/auth/(login)/index.tsx b/apps/expo/app/auth/(login)/index.tsx
index 8222e1f208..5777b8f53d 100644
--- a/apps/expo/app/auth/(login)/index.tsx
+++ b/apps/expo/app/auth/(login)/index.tsx
@@ -1,9 +1,9 @@
+import { needsReauthAtom } from '@packrat/app/auth/atoms/authAtoms';
+import { useAuth } from '@packrat/app/auth/hooks/useAuth';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
+import { testIds } from '@packrat/app/lib/testIds';
import { Button, Form, FormItem, FormSection, Text, TextField } from '@packrat/ui/nativewindui';
import { useForm } from '@tanstack/react-form';
-import { needsReauthAtom } from 'expo-app/features/auth/atoms/authAtoms';
-import { useAuth } from 'expo-app/features/auth/hooks/useAuth';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
-import { testIds } from 'expo-app/lib/testIds';
import { Link, router, Stack, useLocalSearchParams } from 'expo-router';
import { useAtomValue } from 'jotai';
import * as React from 'react';
@@ -16,7 +16,7 @@ import {
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { z } from 'zod';
-const LOGO_SOURCE = require('expo-app/assets/packrat-app-icon-gradient.png');
+const LOGO_SOURCE = require('@packrat/app/assets/packrat-app-icon-gradient.png');
// Define Zod schema for login validation
const loginFormSchema = z.object({
diff --git a/apps/expo/app/auth/(login)/reset-password.tsx b/apps/expo/app/auth/(login)/reset-password.tsx
index dc553a4d91..b904e569a8 100644
--- a/apps/expo/app/auth/(login)/reset-password.tsx
+++ b/apps/expo/app/auth/(login)/reset-password.tsx
@@ -1,3 +1,7 @@
+import { useAuthActions } from '@packrat/app/auth/hooks/useAuthActions';
+import { Icon } from '@packrat/app/components/Icon';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
+import type { TranslationKeys } from '@packrat/app/lib/i18n/types';
import type { AlertMethods } from '@packrat/ui/nativewindui';
import {
AlertAnchor,
@@ -10,10 +14,6 @@ import {
TextField,
} from '@packrat/ui/nativewindui';
import { useForm } from '@tanstack/react-form';
-import { Icon } from 'expo-app/components/Icon';
-import { useAuthActions } from 'expo-app/features/auth/hooks/useAuthActions';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
-import type { TranslationKeys } from 'expo-app/lib/i18n/types';
import { router, Stack, useLocalSearchParams } from 'expo-router';
import * as React from 'react';
import { Alert, Image, Platform, View } from 'react-native';
@@ -25,7 +25,7 @@ import {
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { z } from 'zod';
-const LOGO_SOURCE = require('expo-app/assets/packrat-app-icon-gradient.png');
+const LOGO_SOURCE = require('@packrat/app/assets/packrat-app-icon-gradient.png');
const RE_HAS_UPPERCASE = /[A-Z]/;
const RE_HAS_LOWERCASE = /[a-z]/;
diff --git a/apps/expo/app/auth/_layout.tsx b/apps/expo/app/auth/_layout.tsx
index dc4a2981df..d064cc00a7 100644
--- a/apps/expo/app/auth/_layout.tsx
+++ b/apps/expo/app/auth/_layout.tsx
@@ -1,5 +1,5 @@
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
import { Button, Text } from '@packrat/ui/nativewindui';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { router, Stack } from 'expo-router';
import { Platform } from 'react-native';
diff --git a/apps/expo/app/auth/index.tsx b/apps/expo/app/auth/index.tsx
index 1b8ba307d1..ea0f41f0d1 100644
--- a/apps/expo/app/auth/index.tsx
+++ b/apps/expo/app/auth/index.tsx
@@ -1,22 +1,18 @@
+import { isLoadingAtom, needsReauthAtom, redirectToAtom } from '@packrat/app/auth/atoms/authAtoms';
+import { useAuth } from '@packrat/app/auth/hooks/useAuth';
+import { featureFlags } from '@packrat/app/config';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
+import { testIds } from '@packrat/app/lib/testIds';
import type { AlertMethods } from '@packrat/ui/nativewindui';
import { ActivityIndicator, AlertAnchor, Button, Text } from '@packrat/ui/nativewindui';
import AsyncStorage from '@react-native-async-storage/async-storage';
-import { featureFlags } from 'expo-app/config';
-import {
- isLoadingAtom,
- needsReauthAtom,
- redirectToAtom,
-} from 'expo-app/features/auth/atoms/authAtoms';
-import { useAuth } from 'expo-app/features/auth/hooks/useAuth';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
-import { testIds } from 'expo-app/lib/testIds';
import { Link, router, useLocalSearchParams } from 'expo-router';
import { useAtomValue, useSetAtom } from 'jotai';
import * as React from 'react';
import { Image, Platform, View } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
-const LOGO_SOURCE = require('expo-app/assets/packrat-app-icon-gradient.png');
+const LOGO_SOURCE = require('@packrat/app/assets/packrat-app-icon-gradient.png');
const GOOGLE_SOURCE = {
uri: 'https://www.pngall.com/wp-content/uploads/13/Google-Logo.png',
diff --git a/apps/expo/app/auth/one-time-password.tsx b/apps/expo/app/auth/one-time-password.tsx
index 7702a0cedc..309ecdf9be 100644
--- a/apps/expo/app/auth/one-time-password.tsx
+++ b/apps/expo/app/auth/one-time-password.tsx
@@ -1,11 +1,11 @@
+import { useAuthActions } from '@packrat/app/auth/hooks/useAuthActions';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { useKeyboardHideBlur } from '@packrat/app/lib/hooks/useKeyboardHideBlur';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
+import { asNonNullableRef } from '@packrat/app/lib/utils/asNonNullableRef';
import type { AlertMethods } from '@packrat/ui/nativewindui';
import { ActivityIndicator, AlertAnchor, Button, Text, TextField } from '@packrat/ui/nativewindui';
import { useHeaderHeight } from '@react-navigation/elements';
-import { useAuthActions } from 'expo-app/features/auth/hooks/useAuthActions';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { useKeyboardHideBlur } from 'expo-app/lib/hooks/useKeyboardHideBlur';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
-import { asNonNullableRef } from 'expo-app/lib/utils/asNonNullableRef';
import { router, Stack, useLocalSearchParams } from 'expo-router';
import * as React from 'react';
import {
@@ -24,7 +24,7 @@ import { KeyboardAwareScrollView, KeyboardController } from 'react-native-keyboa
import Animated, { FadeIn, LinearTransition } from 'react-native-reanimated';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
-const LOGO_SOURCE = require('expo-app/assets/packrat-app-icon-gradient.png');
+const LOGO_SOURCE = require('@packrat/app/assets/packrat-app-icon-gradient.png');
const COUNTDOWN_SECONDS_TO_RESEND_CODE = 60;
const NUM_OF_CODE_CHARACTERS = 5;
diff --git a/apps/expo/features/auth/README.md b/apps/expo/features/auth/README.md
deleted file mode 100644
index de6eaebcc1..0000000000
--- a/apps/expo/features/auth/README.md
+++ /dev/null
@@ -1,87 +0,0 @@
-# Authentication System
-
-This directory contains the authentication system for the PackRat mobile application.
-
-## Re-authentication Flow
-
-When a user's session expires (access token and refresh token both expire), the app automatically triggers a re-authentication flow with a non-blocking "Sync Paused" banner. This fits the local-first model where users can continue working offline while being notified they need to re-authenticate to sync changes.
-
-### How It Works
-
-1. **Token Expiration Detection**
- - The axios interceptor in `lib/api/client.ts` intercepts all API requests
- - When a 401 Unauthorized error occurs, it attempts to refresh the access token
- - If refresh fails, it triggers re-authentication
-
-2. **Re-authentication State**
- - `needsReauthAtom`: Boolean flag indicating re-authentication is required
-
-3. **User Experience**
- - A non-blocking banner (`SyncBanner`) appears at the top of the screen
- - Banner displays a message prompting user to sign in again
- - User can continue working with local data while offline
- - User can tap the banner to navigate to the auth screen
- - After successful sign-in, user is redirected back to their previous location
-
-4. **State Management**
- - Current path is automatically passed for post-authentication redirect
- - After successful sign-in / logout, all re-auth state is cleared
-
-### Files
-
-- `components/SyncBanner.tsx`: Banner component displayed when session expires
-- `atoms/authAtoms.ts`: Jotai atoms for authentication state including re-auth flags
-- `hooks/useAuthActions.ts`: Actions for sign-in, sign-out, and state management
-- `lib/api/client.ts`: Axios interceptor handling token refresh and re-auth triggering
-- `lib/api/rpcTransport.ts`: Hono RPC fetch adapter mirroring the same refresh and re-auth behavior
-- `lib/api/rpcClient.ts`: Shared Expo RPC client entry point backed by `@packrat/api-client`
-
-## RPC Migration Status
-
-The app is currently in a coexistence period:
-
-- Legacy feature modules still use `lib/api/client.ts` and `axiosInstance`
-- New Hono RPC infrastructure lives in `lib/api/rpcTransport.ts` and `lib/api/rpcClient.ts`
-- The RPC transport is intended to preserve the same auth contract as axios:
- bearer token attachment, refresh retry queueing, and `needsReauthAtom` on hard failure
-
-Until feature consumers are migrated, both transport layers may exist side-by-side. Changes to auth behavior should keep them aligned.
-
-### Example Flow
-
-```typescript
-// 1. User makes API request with expired token
-await axiosInstance.get('/api/user/profile');
-
-// 2. Server returns 401, interceptor tries to refresh
-// 3. Refresh token is also expired - 401
-// 4. Interceptor sets re-auth state
-await store.set(needsReauthAtom, true);
-
-// 5. SyncBanner appears at top of screen
-// 6. User can continue working offline OR tap banner to sign in
-// 7. User taps banner and completes sign-in
-await signIn(email, password);
-
-// 8. Re-auth state is cleared
-await setNeedsReauth(false);
-
-// 9. User is redirected to saved path
-redirect(redirectTo);
-```
-
-## Token Lifecycle
-
-- **Access Token**: Valid for 7 days, stored in `access_token` key
-- **Refresh Token**: Valid for 30 days, stored in `refresh_token` key
-- Both tokens are stored using `expo-sqlite/kv-store` for persistence
-- Tokens are automatically attached to requests via axios interceptor
-
-## Sign-In Methods
-
-The app supports multiple authentication methods:
-- Email/Password (`signIn`)
-- Google Sign-In (`signInWithGoogle`)
-- Apple Sign-In (`signInWithApple`)
-
-All methods reset re-authentication state upon successful authentication.
diff --git a/apps/expo/features/feed/index.ts b/apps/expo/features/feed/index.ts
deleted file mode 100644
index 6c529ab1e4..0000000000
--- a/apps/expo/features/feed/index.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-export * from './components';
-export * from './hooks';
-export * from './screens';
-export * from './types';
-export * from './utils';
diff --git a/apps/expo/features/pack-templates/index.ts b/apps/expo/features/pack-templates/index.ts
deleted file mode 100644
index a9a0a249f5..0000000000
--- a/apps/expo/features/pack-templates/index.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-export * from './hooks';
-export * from './types';
diff --git a/apps/expo/features/packs/index.ts b/apps/expo/features/packs/index.ts
deleted file mode 100644
index a9a0a249f5..0000000000
--- a/apps/expo/features/packs/index.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-export * from './hooks';
-export * from './types';
diff --git a/apps/expo/features/packs/utils/index.ts b/apps/expo/features/packs/utils/index.ts
deleted file mode 100644
index f3e71bfe10..0000000000
--- a/apps/expo/features/packs/utils/index.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-export * from './computeCategories';
-export * from './computePackWeights';
-export * from './convertFromGrams';
-export * from './convertToGrams';
-export * from './uploadImage';
diff --git a/apps/expo/features/trail-conditions/index.ts b/apps/expo/features/trail-conditions/index.ts
deleted file mode 100644
index 2103701b18..0000000000
--- a/apps/expo/features/trail-conditions/index.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-export * from './components';
-export * from './hooks';
-export * from './types';
diff --git a/apps/expo/features/trips/index.ts b/apps/expo/features/trips/index.ts
deleted file mode 100644
index a9a0a249f5..0000000000
--- a/apps/expo/features/trips/index.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-export * from './hooks';
-export * from './types';
diff --git a/apps/expo/features/wildlife/index.ts b/apps/expo/features/wildlife/index.ts
deleted file mode 100644
index 3c129c6b10..0000000000
--- a/apps/expo/features/wildlife/index.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-export * from './components';
-export * from './hooks';
-export * from './screens';
-export * from './types';
diff --git a/apps/expo/package.json b/apps/expo/package.json
index cbf86a55d9..cd67a162c6 100644
--- a/apps/expo/package.json
+++ b/apps/expo/package.json
@@ -51,6 +51,7 @@
"@gorhom/bottom-sheet": "^5.1.2",
"@legendapp/state": "^3.0.0-beta.30",
"@packrat/api-client": "workspace:*",
+ "@packrat/app": "workspace:*",
"@packrat/config": "workspace:*",
"@packrat/env": "workspace:*",
"@packrat/guards": "workspace:*",
diff --git a/biome.json b/biome.json
index e4bce1e6dc..b276ea1e8e 100644
--- a/biome.json
+++ b/biome.json
@@ -61,8 +61,8 @@
"overrides": [
{
"includes": [
- "apps/expo/atoms/atomWith*.ts",
- "apps/expo/features/weather/atoms/locationsAtoms.ts",
+ "packages/app/src/atoms/atomWith*.ts",
+ "packages/app/src/weather/atoms/locationsAtoms.ts",
"apps/expo/lib/api/client.ts",
"packages/api/src/index.ts",
"packages/api/src/routes/admin/index.ts",
@@ -70,7 +70,7 @@
"packages/api/src/services/etl/processCatalogEtl.ts",
"packages/api/test/guides.test.ts",
"packages/api/test/setup.ts",
- "apps/expo/utils/weight.ts",
+ "packages/app/src/utils/weight.ts",
"packages/api/src/utils/weight.ts",
"packages/mcp/src/index.ts",
"scripts/lint/**",
diff --git a/compound-engineering.local.md b/compound-engineering.local.md
new file mode 100644
index 0000000000..a9a62e0b25
--- /dev/null
+++ b/compound-engineering.local.md
@@ -0,0 +1,10 @@
+---
+review_agents:
+ - kieran-typescript-reviewer
+ - architecture-strategist
+ - performance-oracle
+ - security-sentinel
+ - code-simplicity-reviewer
+---
+
+This is a React Native / Expo monorepo (Bun workspaces) with a web layer powered by Metro `.web.tsx` platform shims that map `@packrat-ai/nativewindui` to `@packrat/web-ui` (shadcn/Radix UI components). TypeScript strict mode. Biome for linting/formatting. The primary concern is correctness of the shim layer and ensuring no regressions to the native mobile builds.
diff --git a/docs/brainstorms/2026-05-01-shared-app-package-extraction-brainstorm.md b/docs/brainstorms/2026-05-01-shared-app-package-extraction-brainstorm.md
new file mode 100644
index 0000000000..8987a04142
--- /dev/null
+++ b/docs/brainstorms/2026-05-01-shared-app-package-extraction-brainstorm.md
@@ -0,0 +1,69 @@
+---
+date: 2026-05-01
+topic: shared-app-package-extraction
+---
+
+# Shared Logic Extraction — How Much Can Move to a `packages/domain` Package?
+
+## What We're Building
+
+A shared `packages/domain` package (or `packages/app`) containing platform-agnostic business logic: types, domain utils, data-fetching hooks, and Jotai store definitions — usable by both the Expo app today and a future Next.js web app without changes to either consumer.
+
+## Why This Approach
+
+The monorepo already proves the pattern works: `@packrat/api-client` is fully platform-agnostic because it injects auth adapters from the consumer layer rather than importing them directly. The same injection pattern can unlock 35–40% of feature code in `apps/expo/features/` for sharing. Screens and components (the irreducibly platform-specific 40%) stay in each app. Logic moves to shared packages.
+
+## Current State (from audit)
+
+**Already shared:** `api-client`, `guards`, `env`, `config` — well-architected boundaries.
+
+**`apps/expo/features/` breakdown (307 files):**
+
+| Category | Files | Shareable? |
+|---|---|---|
+| Screens (`screens/*.tsx`) | 39 | No — expo-router, RN layout primitives |
+| Components (`components/*.tsx`) | 84 | No — View, Text, TouchableOpacity |
+| Hooks (`hooks/*.ts`) | 98 | ~30% clean now, ~70% light surgery |
+| Stores/Atoms | 18 | Medium — logic clean, persistence RN |
+| Utils + Types | ~68 | ~60% clean, ~40% RN/Expo |
+
+**Rough extractable: 35–40% of feature code.**
+
+## Key Decisions
+
+**1. The injection pattern is the unlock.**
+`@packrat/api-client` already does this for auth. Apply the same to:
+- **Navigation**: hooks pass callbacks (`onSuccess: () => router.push(...)`) rather than importing `expo-router` directly
+- **Storage**: Jotai atom persistence layer injected at app startup, not inside the atom definition
+- **Image picking**: `useImagePicker` wraps `expo-image-picker`; extract the mutation logic, inject the picker as a callback
+
+**2. Easy wins (zero refactor, move tomorrow):**
+- `packs/utils/convertToGrams.ts`, `convertFromGrams.ts`, `computePackWeights.ts` — unit math with tests
+- `catalog/lib/normalizeDescription.ts` — string formatter
+- All domain types: `packs/types.ts`, `trips/types.ts`, `catalog/types.ts`
+- ~29 hooks that grep clean (no `react-native`/`expo` imports): `useAllPacks`, `useRecentPacks`, `useUserPackItems`, `useCategoriesCount`, etc.
+
+**3. Medium effort (~30 min per hook/store):**
+- `useCreatePack`, `useUpdatePack` etc.: query logic is clean; navigation side-effects are the only entanglement. Lift navigation out as an `onSuccess` callback parameter.
+- Jotai stores: atom *definitions* are platform-agnostic; only `persistPlugin` + `kvStorage` are Expo-specific. Split: `domain` exports the atom shape, Expo app wraps with persistence.
+- `authAtoms.ts`: imports `kvStorage` (MMKV). Same pattern — inject the storage adapter.
+
+**4. Screens and components do NOT move.** They are irreducibly platform-specific by definition. This is fine — the value is in sharing the logic layer, not the view layer.
+
+**5. JSX/logic split assessment:**
+The split already mostly exists structurally (`.ts` hooks vs `.tsx` screens). The entanglement is in **platform side-effects used inside otherwise pure hooks** — specifically `expo-router` for navigation callbacks and `expo-*` for device APIs. These can be lifted out via the injection pattern without restructuring anything major.
+
+## What This Enables
+
+A future Next.js app can `import { useAllPacks, computePackWeights } from '@packrat/domain'` and get all the data-fetching and business logic for free. It builds its own screens and components on top. No shared JSX, no shared navigation — just shared models and queries.
+
+## Open Questions
+
+- **Package name**: `packages/domain`, `packages/app`, or `packages/core`? Domain is most accurate.
+- **Migration strategy**: move all at once vs feature-by-feature? Feature-by-feature (start with `packs`) reduces risk.
+- **Jotai on Next.js**: Jotai works fine in Next.js App Router with a Provider. Not a blocker.
+- **tRPC/eden client on Next.js**: `@packrat/api-client` already platform-agnostic. Needs HTTP transport config, not a code change.
+
+## Next Steps
+
+→ `/ce:plan` to create `packages/domain` with the packs feature as the first migration target.
diff --git a/packages/app/package.json b/packages/app/package.json
new file mode 100644
index 0000000000..7f9b8ae65c
--- /dev/null
+++ b/packages/app/package.json
@@ -0,0 +1,26 @@
+{
+ "name": "@packrat/app",
+ "version": "0.1.0",
+ "private": true,
+ "type": "module",
+ "exports": {
+ ".": "./src/index.ts",
+ "./ai": "./src/ai/index.ts",
+ "./ai-packs": "./src/ai-packs/index.ts",
+ "./catalog": "./src/catalog/index.ts",
+ "./feed": "./src/feed/index.ts",
+ "./guides": "./src/guides/index.ts",
+ "./pack-templates": "./src/pack-templates/index.ts",
+ "./packs": "./src/packs/index.ts",
+ "./profile": "./src/profile/index.ts",
+ "./trail-conditions": "./src/trail-conditions/index.ts",
+ "./trips": "./src/trips/index.ts",
+ "./wildlife": "./src/wildlife/index.ts"
+ },
+ "main": "./src/index.ts",
+ "types": "./src/index.ts",
+ "dependencies": {
+ "@packrat/api": "workspace:*",
+ "ai": "catalog:"
+ }
+}
diff --git a/apps/expo/features/ai-packs/components/AIPacksTile.tsx b/packages/app/src/ai-packs/components/AIPacksTile.tsx
similarity index 82%
rename from apps/expo/features/ai-packs/components/AIPacksTile.tsx
rename to packages/app/src/ai-packs/components/AIPacksTile.tsx
index bf6d228ec4..af3de1bcea 100644
--- a/apps/expo/features/ai-packs/components/AIPacksTile.tsx
+++ b/packages/app/src/ai-packs/components/AIPacksTile.tsx
@@ -1,8 +1,8 @@
+import { useUser } from '@packrat/app/auth/hooks/useUser';
+import { Icon } from '@packrat/app/components/Icon';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
import { ListItem } from '@packrat/ui/nativewindui';
-import { Icon } from 'expo-app/components/Icon';
-import { useUser } from 'expo-app/features/auth/hooks/useUser';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { useRouter } from 'expo-router';
import { View } from 'react-native';
diff --git a/apps/expo/features/ai-packs/hooks/useGeneratedPacks.ts b/packages/app/src/ai-packs/hooks/useGeneratedPacks.ts
similarity index 75%
rename from apps/expo/features/ai-packs/hooks/useGeneratedPacks.ts
rename to packages/app/src/ai-packs/hooks/useGeneratedPacks.ts
index 2e6c304fac..1eba09f01c 100644
--- a/apps/expo/features/ai-packs/hooks/useGeneratedPacks.ts
+++ b/packages/app/src/ai-packs/hooks/useGeneratedPacks.ts
@@ -1,10 +1,10 @@
import { use$ } from '@legendapp/state/react';
+import type { GenerationRequest } from '@packrat/app/ai-packs';
+import { apiClient } from '@packrat/app/lib/api/packrat';
+import { obs } from '@packrat/app/lib/store';
+import type { Pack } from '@packrat/app/packs';
+import { packsStore } from '@packrat/app/packs/store';
import { useMutation } from '@tanstack/react-query';
-import type { Pack } from 'expo-app/features/packs';
-import { packsStore } from 'expo-app/features/packs/store';
-import { apiClient } from 'expo-app/lib/api/packrat';
-import { obs } from 'expo-app/lib/store';
-import type { GenerationRequest } from '../types';
const generatePacks = async (request: GenerationRequest): Promise => {
const { data, error } = await apiClient.packs['generate-packs'].post(request);
diff --git a/packages/app/src/ai-packs/index.ts b/packages/app/src/ai-packs/index.ts
new file mode 100644
index 0000000000..b8e16be7cc
--- /dev/null
+++ b/packages/app/src/ai-packs/index.ts
@@ -0,0 +1 @@
+export type { GenerationRequest } from './types';
diff --git a/apps/expo/features/ai-packs/screens/AIPacksScreen.tsx b/packages/app/src/ai-packs/screens/AIPacksScreen.tsx
similarity index 94%
rename from apps/expo/features/ai-packs/screens/AIPacksScreen.tsx
rename to packages/app/src/ai-packs/screens/AIPacksScreen.tsx
index e07cc33606..678c1113c1 100644
--- a/apps/expo/features/ai-packs/screens/AIPacksScreen.tsx
+++ b/packages/app/src/ai-packs/screens/AIPacksScreen.tsx
@@ -1,3 +1,8 @@
+import { Icon } from '@packrat/app/components/Icon';
+import { TextInput } from '@packrat/app/components/TextInput';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
+import { PackCard } from '@packrat/app/packs/components/PackCard';
import {
ActivityIndicator,
Alert,
@@ -7,11 +12,6 @@ import {
Text,
} from '@packrat/ui/nativewindui';
import { useForm } from '@tanstack/react-form';
-import { Icon } from 'expo-app/components/Icon';
-import { TextInput } from 'expo-app/components/TextInput';
-import { PackCard } from 'expo-app/features/packs/components/PackCard';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { useRouter } from 'expo-router';
import { useRef, useState } from 'react';
import { Modal, Platform, ScrollView, TouchableOpacity, View } from 'react-native';
diff --git a/apps/expo/features/ai-packs/types.ts b/packages/app/src/ai-packs/types.ts
similarity index 100%
rename from apps/expo/features/ai-packs/types.ts
rename to packages/app/src/ai-packs/types.ts
diff --git a/apps/expo/features/ai/atoms/aiModeAtoms.ts b/packages/app/src/ai/atoms/aiModeAtoms.ts
similarity index 90%
rename from apps/expo/features/ai/atoms/aiModeAtoms.ts
rename to packages/app/src/ai/atoms/aiModeAtoms.ts
index dac4395bcd..c0a7838529 100644
--- a/apps/expo/features/ai/atoms/aiModeAtoms.ts
+++ b/packages/app/src/ai/atoms/aiModeAtoms.ts
@@ -1,4 +1,4 @@
-import { atomWithAsyncStorage } from 'expo-app/atoms/atomWithAsyncStorage';
+import { atomWithAsyncStorage } from '@packrat/app/atoms/atomWithAsyncStorage';
import { atom } from 'jotai';
export type AIMode = 'cloud' | 'local';
diff --git a/apps/expo/features/ai/atoms/chatStorageAtoms.ts b/packages/app/src/ai/atoms/chatStorageAtoms.ts
similarity index 100%
rename from apps/expo/features/ai/atoms/chatStorageAtoms.ts
rename to packages/app/src/ai/atoms/chatStorageAtoms.ts
diff --git a/apps/expo/features/ai/components/AIChatTile.tsx b/packages/app/src/ai/components/AIChatTile.tsx
similarity index 87%
rename from apps/expo/features/ai/components/AIChatTile.tsx
rename to packages/app/src/ai/components/AIChatTile.tsx
index 85c2bfe907..1b11e4aa9d 100644
--- a/apps/expo/features/ai/components/AIChatTile.tsx
+++ b/packages/app/src/ai/components/AIChatTile.tsx
@@ -1,7 +1,7 @@
+import { Icon } from '@packrat/app/components/Icon';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
import { ListItem, Text } from '@packrat/ui/nativewindui';
-import { Icon } from 'expo-app/components/Icon';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { type Href, useRouter } from 'expo-router';
import { Platform, View } from 'react-native';
diff --git a/apps/expo/features/ai/components/AIModeSelector.tsx b/packages/app/src/ai/components/AIModeSelector.tsx
similarity index 85%
rename from apps/expo/features/ai/components/AIModeSelector.tsx
rename to packages/app/src/ai/components/AIModeSelector.tsx
index 5a4b819280..7a7dff0c94 100644
--- a/apps/expo/features/ai/components/AIModeSelector.tsx
+++ b/packages/app/src/ai/components/AIModeSelector.tsx
@@ -1,9 +1,9 @@
import type { BottomSheetModal } from '@gorhom/bottom-sheet';
+import { Icon } from '@packrat/app/components/Icon';
+import { featureFlags } from '@packrat/app/config';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
import { ActivityIndicator, Text } from '@packrat/ui/nativewindui';
-import { Icon } from 'expo-app/components/Icon';
-import { featureFlags } from 'expo-app/config';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { useAtomValue } from 'jotai';
import * as React from 'react';
import { TouchableOpacity } from 'react-native';
diff --git a/apps/expo/features/ai/components/AIModeSheet.tsx b/packages/app/src/ai/components/AIModeSheet.tsx
similarity index 95%
rename from apps/expo/features/ai/components/AIModeSheet.tsx
rename to packages/app/src/ai/components/AIModeSheet.tsx
index 44cf9ce611..4e71a1a490 100644
--- a/apps/expo/features/ai/components/AIModeSheet.tsx
+++ b/packages/app/src/ai/components/AIModeSheet.tsx
@@ -1,11 +1,12 @@
import type { BottomSheetModal } from '@gorhom/bottom-sheet';
import { BottomSheetView } from '@gorhom/bottom-sheet';
+import { LLAMA_MODEL_SIZE } from '@packrat/app/ai';
+import { useAuthState } from '@packrat/app/auth/hooks/useAuthState';
+import { Icon } from '@packrat/app/components/Icon';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
import { isFunction } from '@packrat/guards';
import { Sheet, Text } from '@packrat/ui/nativewindui';
-import { Icon } from 'expo-app/components/Icon';
-import { useAuthState } from 'expo-app/features/auth/hooks/useAuthState';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { useAtom, useAtomValue } from 'jotai';
import * as React from 'react';
import { TouchableOpacity, View } from 'react-native';
@@ -16,7 +17,6 @@ import {
localModelProgressAtom,
localModelStatusAtom,
} from '../atoms/aiModeAtoms';
-import { LLAMA_MODEL_SIZE } from '../lib/constants';
import {
cancelLocalModelDownload,
downloadLocalModel,
diff --git a/apps/expo/features/ai/components/CatalogItemsGenerativeUI.tsx b/packages/app/src/ai/components/CatalogItemsGenerativeUI.tsx
similarity index 92%
rename from apps/expo/features/ai/components/CatalogItemsGenerativeUI.tsx
rename to packages/app/src/ai/components/CatalogItemsGenerativeUI.tsx
index 7fa2daad4d..bb959b42ea 100644
--- a/apps/expo/features/ai/components/CatalogItemsGenerativeUI.tsx
+++ b/packages/app/src/ai/components/CatalogItemsGenerativeUI.tsx
@@ -1,10 +1,10 @@
+import type { ToolInvocation } from '@packrat/app/ai';
+import type { CatalogItem } from '@packrat/app/catalog';
+import { CatalogItemCard } from '@packrat/app/catalog/components';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
import { Text } from '@packrat/ui/nativewindui';
-import { CatalogItemCard } from 'expo-app/features/catalog/components';
-import type { CatalogItem } from 'expo-app/features/catalog/types';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { useRouter } from 'expo-router';
import { Dimensions, ScrollView, View } from 'react-native';
-import type { ToolInvocation } from '../types';
import { ToolCard } from './ToolCard';
type CatalogItemsToolOutput =
diff --git a/apps/expo/features/ai/components/ChatBubble.tsx b/packages/app/src/ai/components/ChatBubble.tsx
similarity index 95%
rename from apps/expo/features/ai/components/ChatBubble.tsx
rename to packages/app/src/ai/components/ChatBubble.tsx
index fd65130483..00d5297b1c 100644
--- a/apps/expo/features/ai/components/ChatBubble.tsx
+++ b/packages/app/src/ai/components/ChatBubble.tsx
@@ -1,13 +1,13 @@
import { BottomSheetScrollView } from '@gorhom/bottom-sheet';
+import { Icon } from '@packrat/app/components/Icon';
+import { Markdown } from '@packrat/app/components/Markdown';
+import { cn } from '@packrat/app/lib/cn';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
+import { formatAIResponse } from '@packrat/app/utils/format-ai-response';
import { keyIn } from '@packrat/guards';
import { Sheet, Text, useColorScheme, useSheetRef } from '@packrat/ui/nativewindui';
import type { ToolUIPart, UIMessage } from 'ai';
import * as Burnt from 'burnt';
-import { Icon } from 'expo-app/components/Icon';
-import { Markdown } from 'expo-app/components/Markdown';
-import { cn } from 'expo-app/lib/cn';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
-import { formatAIResponse } from 'expo-app/utils/format-ai-response';
import * as Clipboard from 'expo-clipboard';
import * as Haptics from 'expo-haptics';
import React, { useCallback, useState } from 'react';
diff --git a/apps/expo/features/ai/components/CircularDownloadButton.tsx b/packages/app/src/ai/components/CircularDownloadButton.tsx
similarity index 97%
rename from apps/expo/features/ai/components/CircularDownloadButton.tsx
rename to packages/app/src/ai/components/CircularDownloadButton.tsx
index 4707567605..474fc75568 100644
--- a/apps/expo/features/ai/components/CircularDownloadButton.tsx
+++ b/packages/app/src/ai/components/CircularDownloadButton.tsx
@@ -1,5 +1,5 @@
-import { Icon } from 'expo-app/components/Icon';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
+import { Icon } from '@packrat/app/components/Icon';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
import { TouchableOpacity, View } from 'react-native';
type Props = {
diff --git a/apps/expo/features/ai/components/ErrorState.tsx b/packages/app/src/ai/components/ErrorState.tsx
similarity index 87%
rename from apps/expo/features/ai/components/ErrorState.tsx
rename to packages/app/src/ai/components/ErrorState.tsx
index b23e4d6049..8a3c2778bf 100644
--- a/apps/expo/features/ai/components/ErrorState.tsx
+++ b/packages/app/src/ai/components/ErrorState.tsx
@@ -1,7 +1,7 @@
import { EvilIcons, Ionicons } from '@expo/vector-icons';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
import { Card, CardContent, Text } from '@packrat/ui/nativewindui';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { Pressable, View } from 'react-native';
interface ErrorStateProps {
diff --git a/apps/expo/features/ai/components/GuidesRAGGenerativeUI.tsx b/packages/app/src/ai/components/GuidesRAGGenerativeUI.tsx
similarity index 97%
rename from apps/expo/features/ai/components/GuidesRAGGenerativeUI.tsx
rename to packages/app/src/ai/components/GuidesRAGGenerativeUI.tsx
index a8d2f0858f..ba735ba709 100644
--- a/apps/expo/features/ai/components/GuidesRAGGenerativeUI.tsx
+++ b/packages/app/src/ai/components/GuidesRAGGenerativeUI.tsx
@@ -1,6 +1,7 @@
-import { Icon } from 'expo-app/components/Icon';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
+import type { ToolInvocation } from '@packrat/app/ai';
+import { Icon } from '@packrat/app/components/Icon';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
import { useRef, useState } from 'react';
import {
Dimensions,
@@ -12,7 +13,6 @@ import {
TouchableOpacity,
View,
} from 'react-native';
-import type { ToolInvocation } from '../types';
import { ToolCard } from './ToolCard';
interface GuideSearchResult {
diff --git a/apps/expo/features/ai/components/LocationContext.tsx b/packages/app/src/ai/components/LocationContext.tsx
similarity index 90%
rename from apps/expo/features/ai/components/LocationContext.tsx
rename to packages/app/src/ai/components/LocationContext.tsx
index 0dc79f0cc7..119852eac7 100644
--- a/apps/expo/features/ai/components/LocationContext.tsx
+++ b/packages/app/src/ai/components/LocationContext.tsx
@@ -1,7 +1,7 @@
+import { Icon } from '@packrat/app/components/Icon';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
+import { LocationPicker } from '@packrat/app/weather/components/LocationPicker';
import { Text, useColorScheme } from '@packrat/ui/nativewindui';
-import { Icon } from 'expo-app/components/Icon';
-import { LocationPicker } from 'expo-app/features/weather/components/LocationPicker';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { router } from 'expo-router';
import { useState } from 'react';
import { TouchableOpacity, View } from 'react-native';
diff --git a/apps/expo/features/ai/components/PackDetailsGenerativeUI.tsx b/packages/app/src/ai/components/PackDetailsGenerativeUI.tsx
similarity index 82%
rename from apps/expo/features/ai/components/PackDetailsGenerativeUI.tsx
rename to packages/app/src/ai/components/PackDetailsGenerativeUI.tsx
index 32a89cd47f..69b28b6bde 100644
--- a/apps/expo/features/ai/components/PackDetailsGenerativeUI.tsx
+++ b/packages/app/src/ai/components/PackDetailsGenerativeUI.tsx
@@ -1,7 +1,7 @@
-import { PackCard } from 'expo-app/features/packs/components/PackCard';
-import type { Pack } from 'expo-app/features/packs/types';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
-import type { ToolInvocation } from '../types';
+import type { ToolInvocation } from '@packrat/app/ai';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
+import type { Pack } from '@packrat/app/packs';
+import { PackCard } from '@packrat/app/packs/components/PackCard';
import { ToolCard } from './ToolCard';
type PackDetailsToolOutput =
diff --git a/apps/expo/features/ai/components/PackItemDetailsGenerativeUI.tsx b/packages/app/src/ai/components/PackItemDetailsGenerativeUI.tsx
similarity index 81%
rename from apps/expo/features/ai/components/PackItemDetailsGenerativeUI.tsx
rename to packages/app/src/ai/components/PackItemDetailsGenerativeUI.tsx
index eb73ac483e..c17ae7c2b4 100644
--- a/apps/expo/features/ai/components/PackItemDetailsGenerativeUI.tsx
+++ b/packages/app/src/ai/components/PackItemDetailsGenerativeUI.tsx
@@ -1,7 +1,7 @@
-import { PackItemCard } from 'expo-app/features/packs/components/PackItemCard';
-import type { PackItem } from 'expo-app/features/packs/types';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
-import type { ToolInvocation } from '../types';
+import type { ToolInvocation } from '@packrat/app/ai';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
+import type { PackItem } from '@packrat/app/packs';
+import { PackItemCard } from '@packrat/app/packs/components/PackItemCard';
import { ToolCard } from './ToolCard';
type PackItemToolInput = {
diff --git a/apps/expo/features/ai/components/ReportModal.tsx b/packages/app/src/ai/components/ReportModal.tsx
similarity index 93%
rename from apps/expo/features/ai/components/ReportModal.tsx
rename to packages/app/src/ai/components/ReportModal.tsx
index 02a4ca1fbf..9641c03dbc 100644
--- a/apps/expo/features/ai/components/ReportModal.tsx
+++ b/packages/app/src/ai/components/ReportModal.tsx
@@ -1,9 +1,9 @@
+import { Icon } from '@packrat/app/components/Icon';
+import { TextInput } from '@packrat/app/components/TextInput';
+import { cn } from '@packrat/app/lib/cn';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
import { Button, Text } from '@packrat/ui/nativewindui';
-import { Icon } from 'expo-app/components/Icon';
-import { TextInput } from 'expo-app/components/TextInput';
-import { cn } from 'expo-app/lib/cn';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { useState } from 'react';
import { Modal, ScrollView, TouchableOpacity, View } from 'react-native';
import { KeyboardStickyView } from 'react-native-keyboard-controller';
diff --git a/apps/expo/features/ai/components/ReportedContentTile.tsx b/packages/app/src/ai/components/ReportedContentTile.tsx
similarity index 87%
rename from apps/expo/features/ai/components/ReportedContentTile.tsx
rename to packages/app/src/ai/components/ReportedContentTile.tsx
index dff622e0a8..2147e1ecac 100644
--- a/apps/expo/features/ai/components/ReportedContentTile.tsx
+++ b/packages/app/src/ai/components/ReportedContentTile.tsx
@@ -1,8 +1,8 @@
+import { useUser } from '@packrat/app/auth/hooks/useUser';
+import { Icon } from '@packrat/app/components/Icon';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
import { ListItem, Text } from '@packrat/ui/nativewindui';
-import { Icon } from 'expo-app/components/Icon';
-import { useUser } from 'expo-app/features/auth/hooks/useUser';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { type Href, useRouter } from 'expo-router';
import { View } from 'react-native';
import { useReportedContentCount } from '../hooks/useReportedContent';
diff --git a/apps/expo/features/ai/components/ToolCard.tsx b/packages/app/src/ai/components/ToolCard.tsx
similarity index 100%
rename from apps/expo/features/ai/components/ToolCard.tsx
rename to packages/app/src/ai/components/ToolCard.tsx
diff --git a/apps/expo/features/ai/components/ToolInvocationRenderer.tsx b/packages/app/src/ai/components/ToolInvocationRenderer.tsx
similarity index 100%
rename from apps/expo/features/ai/components/ToolInvocationRenderer.tsx
rename to packages/app/src/ai/components/ToolInvocationRenderer.tsx
diff --git a/apps/expo/features/ai/components/WeatherGenerativeUI.tsx b/packages/app/src/ai/components/WeatherGenerativeUI.tsx
similarity index 95%
rename from apps/expo/features/ai/components/WeatherGenerativeUI.tsx
rename to packages/app/src/ai/components/WeatherGenerativeUI.tsx
index 28d010c0c5..7f7a953b08 100644
--- a/apps/expo/features/ai/components/WeatherGenerativeUI.tsx
+++ b/packages/app/src/ai/components/WeatherGenerativeUI.tsx
@@ -1,9 +1,9 @@
+import type { ToolInvocation } from '@packrat/app/ai';
+import { Icon } from '@packrat/app/components/Icon';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
+import { getWeatherIconByCondition } from '@packrat/app/weather/lib/weatherIcons';
import { Text, useColorScheme } from '@packrat/ui/nativewindui';
-import { Icon } from 'expo-app/components/Icon';
-import { getWeatherIconByCondition } from 'expo-app/features/weather/lib/weatherIcons';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { View } from 'react-native';
-import type { ToolInvocation } from '../types';
import { ToolCard } from './ToolCard';
type WeatherToolOutput =
diff --git a/apps/expo/features/ai/components/WebSearchGenerativeUI.tsx b/packages/app/src/ai/components/WebSearchGenerativeUI.tsx
similarity index 96%
rename from apps/expo/features/ai/components/WebSearchGenerativeUI.tsx
rename to packages/app/src/ai/components/WebSearchGenerativeUI.tsx
index 91e0883022..d78e3f648d 100644
--- a/apps/expo/features/ai/components/WebSearchGenerativeUI.tsx
+++ b/packages/app/src/ai/components/WebSearchGenerativeUI.tsx
@@ -2,13 +2,13 @@ import EvilIcons from '@expo/vector-icons/EvilIcons';
import Fontisto from '@expo/vector-icons/Fontisto';
import Ionicons from '@expo/vector-icons/Ionicons';
import { BottomSheetScrollView } from '@gorhom/bottom-sheet';
+import type { ToolInvocation } from '@packrat/app/ai';
+import { Icon } from '@packrat/app/components/Icon';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
import { Card, CardContent, Sheet, Text, useSheetRef } from '@packrat/ui/nativewindui';
-import { Icon } from 'expo-app/components/Icon';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { Linking, Pressable, View } from 'react-native';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
-import type { ToolInvocation } from '../types';
import { ToolCard } from './ToolCard';
type WebSearchToolInput = {
diff --git a/apps/expo/features/ai/hooks/useReportContent.ts b/packages/app/src/ai/hooks/useReportContent.ts
similarity index 95%
rename from apps/expo/features/ai/hooks/useReportContent.ts
rename to packages/app/src/ai/hooks/useReportContent.ts
index 6b0f792c52..7879abe118 100644
--- a/apps/expo/features/ai/hooks/useReportContent.ts
+++ b/packages/app/src/ai/hooks/useReportContent.ts
@@ -1,5 +1,5 @@
+import { apiClient } from '@packrat/app/lib/api/packrat';
import { useMutation } from '@tanstack/react-query';
-import { apiClient } from 'expo-app/lib/api/packrat';
import type { ReportReason } from '../lib/reportReasons';
type ReportContentPayload = {
diff --git a/apps/expo/features/ai/hooks/useReportedContent.ts b/packages/app/src/ai/hooks/useReportedContent.ts
similarity index 86%
rename from apps/expo/features/ai/hooks/useReportedContent.ts
rename to packages/app/src/ai/hooks/useReportedContent.ts
index 204ea674a4..466f64b1aa 100644
--- a/apps/expo/features/ai/hooks/useReportedContent.ts
+++ b/packages/app/src/ai/hooks/useReportedContent.ts
@@ -1,8 +1,8 @@
+import { useUser } from '@packrat/app/auth/hooks/useUser';
+import { apiClient } from '@packrat/app/lib/api/packrat';
+import { useAuthenticatedQueryToolkit } from '@packrat/app/lib/hooks/useAuthenticatedQueryToolkit';
+import type { User } from '@packrat/app/profile';
import { useQuery } from '@tanstack/react-query';
-import { useUser } from 'expo-app/features/auth/hooks/useUser';
-import type { User } from 'expo-app/features/profile/types';
-import { apiClient } from 'expo-app/lib/api/packrat';
-import { useAuthenticatedQueryToolkit } from 'expo-app/lib/hooks/useAuthenticatedQueryToolkit';
type ReportedContentResponse = {
reportedItems: Array<{
diff --git a/apps/expo/features/ai/hooks/useUpdateReportStatus.ts b/packages/app/src/ai/hooks/useUpdateReportStatus.ts
similarity index 94%
rename from apps/expo/features/ai/hooks/useUpdateReportStatus.ts
rename to packages/app/src/ai/hooks/useUpdateReportStatus.ts
index 27ea2cf28a..f07aec2b5c 100644
--- a/apps/expo/features/ai/hooks/useUpdateReportStatus.ts
+++ b/packages/app/src/ai/hooks/useUpdateReportStatus.ts
@@ -1,5 +1,5 @@
+import { apiClient } from '@packrat/app/lib/api/packrat';
import { useMutation, useQueryClient } from '@tanstack/react-query';
-import { apiClient } from 'expo-app/lib/api/packrat';
type UpdateReportStatusPayload = {
id: string;
diff --git a/packages/app/src/ai/index.ts b/packages/app/src/ai/index.ts
new file mode 100644
index 0000000000..64fb24813d
--- /dev/null
+++ b/packages/app/src/ai/index.ts
@@ -0,0 +1,2 @@
+export { LLAMA_MODEL_ID, LLAMA_MODEL_SIZE, LLAMA_MODEL_SIZE_BYTES } from './lib/constants';
+export type { ToolInvocation, ToolOutput } from './types';
diff --git a/apps/expo/features/ai/lib/CustomChatTransport.ts b/packages/app/src/ai/lib/CustomChatTransport.ts
similarity index 100%
rename from apps/expo/features/ai/lib/CustomChatTransport.ts
rename to packages/app/src/ai/lib/CustomChatTransport.ts
diff --git a/apps/expo/features/ai/lib/constants.ts b/packages/app/src/ai/lib/constants.ts
similarity index 100%
rename from apps/expo/features/ai/lib/constants.ts
rename to packages/app/src/ai/lib/constants.ts
diff --git a/apps/expo/features/ai/lib/localModelManager.ts b/packages/app/src/ai/lib/localModelManager.ts
similarity index 98%
rename from apps/expo/features/ai/lib/localModelManager.ts
rename to packages/app/src/ai/lib/localModelManager.ts
index a72ca60236..fe34b5f713 100644
--- a/apps/expo/features/ai/lib/localModelManager.ts
+++ b/packages/app/src/ai/lib/localModelManager.ts
@@ -8,9 +8,10 @@
* from any component, even while the bottom sheet is closed.
*/
+import { LLAMA_MODEL_ID, LLAMA_MODEL_SIZE_BYTES } from '@packrat/app/ai';
+import { store } from '@packrat/app/atoms/store';
import { isString } from '@packrat/guards';
import { type LlamaLanguageModel, llama } from '@react-native-ai/llama';
-import { store } from 'expo-app/atoms/store';
import { Platform } from 'react-native';
import RNBlobUtil from 'react-native-blob-util';
import {
@@ -19,8 +20,6 @@ import {
localModelProgressAtom,
localModelStatusAtom,
} from '../atoms/aiModeAtoms';
-
-import { LLAMA_MODEL_ID, LLAMA_MODEL_SIZE_BYTES } from './constants';
import { createLocalTools } from './tools';
const LLAMA_MODEL_FILENAME = 'SmolLM3-Q4_K_M.gguf';
diff --git a/apps/expo/features/ai/lib/reportReasons.ts b/packages/app/src/ai/lib/reportReasons.ts
similarity index 91%
rename from apps/expo/features/ai/lib/reportReasons.ts
rename to packages/app/src/ai/lib/reportReasons.ts
index 8ad018de21..38f4d84d08 100644
--- a/apps/expo/features/ai/lib/reportReasons.ts
+++ b/packages/app/src/ai/lib/reportReasons.ts
@@ -1,4 +1,4 @@
-import type { TranslationKeys } from 'expo-app/lib/i18n/types';
+import type { TranslationKeys } from '@packrat/app/lib/i18n/types';
export const reportReasons = [
'inappropriate_content',
diff --git a/apps/expo/features/ai/lib/tools.ts b/packages/app/src/ai/lib/tools.ts
similarity index 96%
rename from apps/expo/features/ai/lib/tools.ts
rename to packages/app/src/ai/lib/tools.ts
index b627a5c26b..0bdbbfb178 100644
--- a/apps/expo/features/ai/lib/tools.ts
+++ b/packages/app/src/ai/lib/tools.ts
@@ -10,11 +10,11 @@
* through authenticated API endpoints.
*/
+import { apiClient } from '@packrat/app/lib/api/packrat';
+import { getPackItems, packItemsStore } from '@packrat/app/packs/store/packItems';
+import { packsStore } from '@packrat/app/packs/store/packs';
+import { getWeatherData, searchLocations } from '@packrat/app/weather/lib/weatherService';
import { tool } from 'ai';
-import { getPackItems, packItemsStore } from 'expo-app/features/packs/store/packItems';
-import { packsStore } from 'expo-app/features/packs/store/packs';
-import { getWeatherData, searchLocations } from 'expo-app/features/weather/lib/weatherService';
-import { apiClient } from 'expo-app/lib/api/packrat';
import { z } from 'zod';
export function createLocalTools() {
diff --git a/apps/expo/features/ai/screens/ReportedContentScreen.tsx b/packages/app/src/ai/screens/ReportedContentScreen.tsx
similarity index 96%
rename from apps/expo/features/ai/screens/ReportedContentScreen.tsx
rename to packages/app/src/ai/screens/ReportedContentScreen.tsx
index b7e98df99d..b297191d61 100644
--- a/apps/expo/features/ai/screens/ReportedContentScreen.tsx
+++ b/packages/app/src/ai/screens/ReportedContentScreen.tsx
@@ -1,10 +1,10 @@
'use client';
+import { Icon } from '@packrat/app/components/Icon';
+import { cn } from '@packrat/app/lib/cn';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
import { Button, LargeTitleHeader, Text } from '@packrat/ui/nativewindui';
-import { Icon } from 'expo-app/components/Icon';
-import { cn } from 'expo-app/lib/cn';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { useState } from 'react';
import { ActivityIndicator, FlatList, TouchableOpacity, View } from 'react-native';
import { useReportedContent } from '../hooks/useReportedContent';
diff --git a/apps/expo/features/ai/types.ts b/packages/app/src/ai/types.ts
similarity index 100%
rename from apps/expo/features/ai/types.ts
rename to packages/app/src/ai/types.ts
diff --git a/apps/expo/assets/adaptive-icon.png b/packages/app/src/assets/adaptive-icon.png
similarity index 100%
rename from apps/expo/assets/adaptive-icon.png
rename to packages/app/src/assets/adaptive-icon.png
diff --git a/apps/expo/assets/packrat-app-icon-gradient.png b/packages/app/src/assets/packrat-app-icon-gradient.png
similarity index 100%
rename from apps/expo/assets/packrat-app-icon-gradient.png
rename to packages/app/src/assets/packrat-app-icon-gradient.png
diff --git a/apps/expo/atoms/atomWithAsyncStorage.ts b/packages/app/src/atoms/atomWithAsyncStorage.ts
similarity index 100%
rename from apps/expo/atoms/atomWithAsyncStorage.ts
rename to packages/app/src/atoms/atomWithAsyncStorage.ts
diff --git a/apps/expo/atoms/atomWithKvStorage.ts b/packages/app/src/atoms/atomWithKvStorage.ts
similarity index 100%
rename from apps/expo/atoms/atomWithKvStorage.ts
rename to packages/app/src/atoms/atomWithKvStorage.ts
diff --git a/apps/expo/atoms/atomWithSecureStorage.ts b/packages/app/src/atoms/atomWithSecureStorage.ts
similarity index 100%
rename from apps/expo/atoms/atomWithSecureStorage.ts
rename to packages/app/src/atoms/atomWithSecureStorage.ts
diff --git a/apps/expo/atoms/itemListAtoms.ts b/packages/app/src/atoms/itemListAtoms.ts
similarity index 100%
rename from apps/expo/atoms/itemListAtoms.ts
rename to packages/app/src/atoms/itemListAtoms.ts
diff --git a/apps/expo/atoms/recentlyUsedCatalogItemsAtom.ts b/packages/app/src/atoms/recentlyUsedCatalogItemsAtom.ts
similarity index 88%
rename from apps/expo/atoms/recentlyUsedCatalogItemsAtom.ts
rename to packages/app/src/atoms/recentlyUsedCatalogItemsAtom.ts
index 701ccea101..f2ca69800b 100644
--- a/apps/expo/atoms/recentlyUsedCatalogItemsAtom.ts
+++ b/packages/app/src/atoms/recentlyUsedCatalogItemsAtom.ts
@@ -1,4 +1,4 @@
-import type { CatalogItem } from 'expo-app/features/catalog/types';
+import type { CatalogItem } from '@packrat/app/catalog';
import { atomWithAsyncStorage } from './atomWithAsyncStorage';
const MAX_RECENTLY_USED = 10;
diff --git a/apps/expo/atoms/store.ts b/packages/app/src/atoms/store.ts
similarity index 100%
rename from apps/expo/atoms/store.ts
rename to packages/app/src/atoms/store.ts
diff --git a/apps/expo/features/auth/atoms/authAtoms.ts b/packages/app/src/auth/atoms/authAtoms.ts
similarity index 93%
rename from apps/expo/features/auth/atoms/authAtoms.ts
rename to packages/app/src/auth/atoms/authAtoms.ts
index f745f8b176..7d821c2b01 100644
--- a/apps/expo/features/auth/atoms/authAtoms.ts
+++ b/packages/app/src/auth/atoms/authAtoms.ts
@@ -1,4 +1,4 @@
-import kvStorage from 'expo-app/lib/kvStorage';
+import kvStorage from '@packrat/app/lib/kvStorage';
import { atom } from 'jotai';
import { atomWithStorage } from 'jotai/utils';
diff --git a/apps/expo/features/auth/components/DeleteAccountButton.tsx b/packages/app/src/auth/components/DeleteAccountButton.tsx
similarity index 91%
rename from apps/expo/features/auth/components/DeleteAccountButton.tsx
rename to packages/app/src/auth/components/DeleteAccountButton.tsx
index 43654984d9..4133e53c56 100644
--- a/apps/expo/features/auth/components/DeleteAccountButton.tsx
+++ b/packages/app/src/auth/components/DeleteAccountButton.tsx
@@ -1,9 +1,9 @@
+import { useAuth } from '@packrat/app/auth/hooks/useAuth';
+import { Icon } from '@packrat/app/components/Icon';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
import type { AlertMethods } from '@packrat/ui/nativewindui';
import { ActivityIndicator, Alert, Button, Text } from '@packrat/ui/nativewindui';
-import { Icon } from 'expo-app/components/Icon';
-import { useAuth } from 'expo-app/features/auth/hooks/useAuth';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { useRef, useState } from 'react';
import { View } from 'react-native';
diff --git a/apps/expo/features/auth/hocs/index.ts b/packages/app/src/auth/hocs/index.ts
similarity index 100%
rename from apps/expo/features/auth/hocs/index.ts
rename to packages/app/src/auth/hocs/index.ts
diff --git a/apps/expo/features/auth/hocs/withAuthWall.tsx b/packages/app/src/auth/hocs/withAuthWall.tsx
similarity index 100%
rename from apps/expo/features/auth/hocs/withAuthWall.tsx
rename to packages/app/src/auth/hocs/withAuthWall.tsx
diff --git a/apps/expo/features/auth/hooks/useAuth.ts b/packages/app/src/auth/hooks/useAuth.ts
similarity index 100%
rename from apps/expo/features/auth/hooks/useAuth.ts
rename to packages/app/src/auth/hooks/useAuth.ts
diff --git a/apps/expo/features/auth/hooks/useAuthActions.ts b/packages/app/src/auth/hooks/useAuthActions.ts
similarity index 97%
rename from apps/expo/features/auth/hooks/useAuthActions.ts
rename to packages/app/src/auth/hooks/useAuthActions.ts
index 289605f5c0..392de07915 100644
--- a/apps/expo/features/auth/hooks/useAuthActions.ts
+++ b/packages/app/src/auth/hooks/useAuthActions.ts
@@ -1,3 +1,8 @@
+import { isAuthed, userStore } from '@packrat/app/auth/store';
+import { apiClient } from '@packrat/app/lib/api/packrat';
+import { t } from '@packrat/app/lib/i18n';
+import ImageCacheManager from '@packrat/app/lib/utils/ImageCacheManager';
+import type { User } from '@packrat/app/profile';
import { isObject } from '@packrat/guards';
import AsyncStorage from '@react-native-async-storage/async-storage';
import {
@@ -5,11 +10,6 @@ import {
isErrorWithCode,
statusCodes,
} from '@react-native-google-signin/google-signin';
-import { isAuthed, userStore } from 'expo-app/features/auth/store';
-import type { User } from 'expo-app/features/profile/types';
-import { apiClient } from 'expo-app/lib/api/packrat';
-import { t } from 'expo-app/lib/i18n';
-import ImageCacheManager from 'expo-app/lib/utils/ImageCacheManager';
import * as AppleAuthentication from 'expo-apple-authentication';
import { type Href, router } from 'expo-router';
import Storage from 'expo-sqlite/kv-store';
diff --git a/apps/expo/features/auth/hooks/useAuthInit.ts b/packages/app/src/auth/hooks/useAuthInit.ts
similarity index 97%
rename from apps/expo/features/auth/hooks/useAuthInit.ts
rename to packages/app/src/auth/hooks/useAuthInit.ts
index aa790aec35..eef32e0cfd 100644
--- a/apps/expo/features/auth/hooks/useAuthInit.ts
+++ b/packages/app/src/auth/hooks/useAuthInit.ts
@@ -1,7 +1,7 @@
+import { store } from '@packrat/app/atoms/store';
import { clientEnvs } from '@packrat/env/expo-client';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { GoogleSignin } from '@react-native-google-signin/google-signin';
-import { store } from 'expo-app/atoms/store';
import { router } from 'expo-router';
import Storage from 'expo-sqlite/kv-store';
import { useEffect, useState } from 'react';
diff --git a/apps/expo/features/auth/hooks/useAuthState.ts b/packages/app/src/auth/hooks/useAuthState.ts
similarity index 100%
rename from apps/expo/features/auth/hooks/useAuthState.ts
rename to packages/app/src/auth/hooks/useAuthState.ts
diff --git a/apps/expo/features/auth/hooks/useUser.ts b/packages/app/src/auth/hooks/useUser.ts
similarity index 100%
rename from apps/expo/features/auth/hooks/useUser.ts
rename to packages/app/src/auth/hooks/useUser.ts
diff --git a/apps/expo/features/auth/store/index.ts b/packages/app/src/auth/store/index.ts
similarity index 100%
rename from apps/expo/features/auth/store/index.ts
rename to packages/app/src/auth/store/index.ts
diff --git a/apps/expo/features/auth/store/user.ts b/packages/app/src/auth/store/user.ts
similarity index 78%
rename from apps/expo/features/auth/store/user.ts
rename to packages/app/src/auth/store/user.ts
index 7991b3f0b3..c6a9d503fb 100644
--- a/apps/expo/features/auth/store/user.ts
+++ b/packages/app/src/auth/store/user.ts
@@ -1,8 +1,8 @@
import { observable, syncState } from '@legendapp/state';
import { syncObservable } from '@legendapp/state/sync';
import { syncedCrud } from '@legendapp/state/sync-plugins/crud';
-import type { User } from 'expo-app/features/profile/types';
-import { persistPlugin } from 'expo-app/lib/persist-plugin';
+import { persistPlugin } from '@packrat/app/lib/persist-plugin';
+import type { User } from '@packrat/app/profile';
export const userStore = observable(null);
diff --git a/apps/expo/features/catalog/components/CatalogBrowserModal.tsx b/packages/app/src/catalog/components/CatalogBrowserModal.tsx
similarity index 96%
rename from apps/expo/features/catalog/components/CatalogBrowserModal.tsx
rename to packages/app/src/catalog/components/CatalogBrowserModal.tsx
index 38a40684fe..d5d23793e5 100644
--- a/apps/expo/features/catalog/components/CatalogBrowserModal.tsx
+++ b/packages/app/src/catalog/components/CatalogBrowserModal.tsx
@@ -1,11 +1,12 @@
+import { searchValueAtom } from '@packrat/app/atoms/itemListAtoms';
+import type { CatalogItem } from '@packrat/app/catalog';
+import { CategoriesFilter } from '@packrat/app/components/CategoriesFilter';
+import { Icon } from '@packrat/app/components/Icon';
+import { SearchInput } from '@packrat/app/components/SearchInput';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
+import { HorizontalCatalogItemCard } from '@packrat/app/packs/components/HorizontalCatalogItemCard';
import { Button, Text } from '@packrat/ui/nativewindui';
-import { searchValueAtom } from 'expo-app/atoms/itemListAtoms';
-import { CategoriesFilter } from 'expo-app/components/CategoriesFilter';
-import { Icon } from 'expo-app/components/Icon';
-import { SearchInput } from 'expo-app/components/SearchInput';
-import { HorizontalCatalogItemCard } from 'expo-app/features/packs/components/HorizontalCatalogItemCard';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { useAtom } from 'jotai';
import { useCallback, useMemo, useState } from 'react';
import {
@@ -24,7 +25,6 @@ import { useCatalogItemsCategories } from '../hooks/useCatalogItemsCategories';
import { usePopularCatalogItems } from '../hooks/usePopularCatalogItems';
import { useRecentlyUsedCatalogItems } from '../hooks/useRecentlyUsedCatalogItems';
import { useVectorSearch } from '../hooks/useVectorSearch';
-import type { CatalogItem } from '../types';
type CatalogBrowserModalProps = {
visible: boolean;
diff --git a/apps/expo/features/catalog/components/CatalogCategoriesFilter.tsx b/packages/app/src/catalog/components/CatalogCategoriesFilter.tsx
similarity index 94%
rename from apps/expo/features/catalog/components/CatalogCategoriesFilter.tsx
rename to packages/app/src/catalog/components/CatalogCategoriesFilter.tsx
index 34b4218d00..407ba733bf 100644
--- a/apps/expo/features/catalog/components/CatalogCategoriesFilter.tsx
+++ b/packages/app/src/catalog/components/CatalogCategoriesFilter.tsx
@@ -1,5 +1,5 @@
+import { decodeHtmlEntities } from '@packrat/app/lib/utils/decodeHtmlEntities';
import { Text } from '@packrat/ui/nativewindui';
-import { decodeHtmlEntities } from 'expo-app/lib/utils/decodeHtmlEntities';
import { ScrollView, TouchableOpacity, View } from 'react-native';
import { useCatalogItemsCategories } from '../hooks/useCatalogItemsCategories';
diff --git a/apps/expo/features/catalog/components/CatalogItemCard.tsx b/packages/app/src/catalog/components/CatalogItemCard.tsx
similarity index 89%
rename from apps/expo/features/catalog/components/CatalogItemCard.tsx
rename to packages/app/src/catalog/components/CatalogItemCard.tsx
index b56a45cee4..e7478ad960 100644
--- a/apps/expo/features/catalog/components/CatalogItemCard.tsx
+++ b/packages/app/src/catalog/components/CatalogItemCard.tsx
@@ -1,3 +1,8 @@
+import type { CatalogItem } from '@packrat/app/catalog';
+import { normalizeDescription } from '@packrat/app/catalog';
+import { Icon } from '@packrat/app/components/Icon';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
import {
Card,
CardContent,
@@ -7,12 +12,7 @@ import {
CardTitle,
Text,
} from '@packrat/ui/nativewindui';
-import { Icon } from 'expo-app/components/Icon';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { TouchableWithoutFeedback, View } from 'react-native';
-import { normalizeDescription } from '../lib/normalizeDescription';
-import type { CatalogItem } from '../types';
import { CatalogItemImage } from './CatalogItemImage';
type CatalogItemCardProps = {
diff --git a/apps/expo/features/catalog/components/CatalogItemImage.tsx b/packages/app/src/catalog/components/CatalogItemImage.tsx
similarity index 94%
rename from apps/expo/features/catalog/components/CatalogItemImage.tsx
rename to packages/app/src/catalog/components/CatalogItemImage.tsx
index f5360f33f1..0a5852cc12 100644
--- a/apps/expo/features/catalog/components/CatalogItemImage.tsx
+++ b/packages/app/src/catalog/components/CatalogItemImage.tsx
@@ -1,5 +1,5 @@
+import { Icon } from '@packrat/app/components/Icon';
import { useColorScheme } from '@packrat/ui/nativewindui';
-import { Icon } from 'expo-app/components/Icon';
import { Image, type ImageProps, View } from 'react-native';
interface PackItemImageProps extends Omit {
diff --git a/apps/expo/features/catalog/components/CatalogItemSelectCard.tsx b/packages/app/src/catalog/components/CatalogItemSelectCard.tsx
similarity index 93%
rename from apps/expo/features/catalog/components/CatalogItemSelectCard.tsx
rename to packages/app/src/catalog/components/CatalogItemSelectCard.tsx
index 9c15459cfb..5766f8d622 100644
--- a/apps/expo/features/catalog/components/CatalogItemSelectCard.tsx
+++ b/packages/app/src/catalog/components/CatalogItemSelectCard.tsx
@@ -1,3 +1,7 @@
+import type { CatalogItem } from '@packrat/app/catalog';
+import { Icon } from '@packrat/app/components/Icon';
+import { cn } from '@packrat/app/lib/cn';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
import {
Card,
CardContent,
@@ -7,11 +11,7 @@ import {
CardTitle,
Text,
} from '@packrat/ui/nativewindui';
-import { Icon } from 'expo-app/components/Icon';
-import { cn } from 'expo-app/lib/cn';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
import { TouchableWithoutFeedback, View } from 'react-native';
-import type { CatalogItem } from '../types';
import { CatalogItemImage } from './CatalogItemImage';
type CatalogItemSelectCardProps = {
diff --git a/apps/expo/features/catalog/components/CatalogItemsAuthWall.tsx b/packages/app/src/catalog/components/CatalogItemsAuthWall.tsx
similarity index 91%
rename from apps/expo/features/catalog/components/CatalogItemsAuthWall.tsx
rename to packages/app/src/catalog/components/CatalogItemsAuthWall.tsx
index c9929a0171..279b5b8a4f 100644
--- a/apps/expo/features/catalog/components/CatalogItemsAuthWall.tsx
+++ b/packages/app/src/catalog/components/CatalogItemsAuthWall.tsx
@@ -1,6 +1,6 @@
+import { Icon } from '@packrat/app/components/Icon';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
import { Button, Text } from '@packrat/ui/nativewindui';
-import { Icon } from 'expo-app/components/Icon';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { Stack, usePathname, useRouter } from 'expo-router';
import { View } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
diff --git a/apps/expo/features/catalog/components/ItemLinks.tsx b/packages/app/src/catalog/components/ItemLinks.tsx
similarity index 85%
rename from apps/expo/features/catalog/components/ItemLinks.tsx
rename to packages/app/src/catalog/components/ItemLinks.tsx
index 8172e2426d..6f3177a9c1 100644
--- a/apps/expo/features/catalog/components/ItemLinks.tsx
+++ b/packages/app/src/catalog/components/ItemLinks.tsx
@@ -1,8 +1,8 @@
+import type { CatalogItem } from '@packrat/app/catalog';
+import { Icon } from '@packrat/app/components/Icon';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
import { Text } from '@packrat/ui/nativewindui';
-import { Icon } from 'expo-app/components/Icon';
-import type { CatalogItem } from 'expo-app/features/catalog/types';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { Linking, TouchableOpacity, View } from 'react-native';
type ItemLinksProps = {
diff --git a/apps/expo/features/catalog/components/ItemReviews.tsx b/packages/app/src/catalog/components/ItemReviews.tsx
similarity index 93%
rename from apps/expo/features/catalog/components/ItemReviews.tsx
rename to packages/app/src/catalog/components/ItemReviews.tsx
index 37e023f35d..4b07abf444 100644
--- a/apps/expo/features/catalog/components/ItemReviews.tsx
+++ b/packages/app/src/catalog/components/ItemReviews.tsx
@@ -1,12 +1,12 @@
'use client';
+import type { CatalogItem } from '@packrat/app/catalog';
+import { Icon } from '@packrat/app/components/Icon';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
import { Text } from '@packrat/ui/nativewindui';
-import { Icon } from 'expo-app/components/Icon';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { useState } from 'react';
import { Image, TouchableOpacity, View } from 'react-native';
-import type { CatalogItem } from '../types';
type ItemReviewsProps = {
reviews: CatalogItem['reviews'];
diff --git a/apps/expo/features/catalog/components/SimilarItems.tsx b/packages/app/src/catalog/components/SimilarItems.tsx
similarity index 95%
rename from apps/expo/features/catalog/components/SimilarItems.tsx
rename to packages/app/src/catalog/components/SimilarItems.tsx
index 6883533197..51311889b2 100644
--- a/apps/expo/features/catalog/components/SimilarItems.tsx
+++ b/packages/app/src/catalog/components/SimilarItems.tsx
@@ -1,6 +1,6 @@
+import { type SimilarItem, useSimilarCatalogItems } from '@packrat/app/catalog/hooks';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
import { Text } from '@packrat/ui/nativewindui';
-import { type SimilarItem, useSimilarCatalogItems } from 'expo-app/features/catalog/hooks';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { useRouter } from 'expo-router';
import type React from 'react';
import { FlatList, Pressable, View } from 'react-native';
diff --git a/apps/expo/features/catalog/components/index.ts b/packages/app/src/catalog/components/index.ts
similarity index 100%
rename from apps/expo/features/catalog/components/index.ts
rename to packages/app/src/catalog/components/index.ts
diff --git a/apps/expo/features/catalog/hooks/index.ts b/packages/app/src/catalog/hooks/index.ts
similarity index 100%
rename from apps/expo/features/catalog/hooks/index.ts
rename to packages/app/src/catalog/hooks/index.ts
diff --git a/apps/expo/features/catalog/hooks/useCatalogItemDetails.ts b/packages/app/src/catalog/hooks/useCatalogItemDetails.ts
similarity index 77%
rename from apps/expo/features/catalog/hooks/useCatalogItemDetails.ts
rename to packages/app/src/catalog/hooks/useCatalogItemDetails.ts
index 1881874c01..a881cd56d5 100644
--- a/apps/expo/features/catalog/hooks/useCatalogItemDetails.ts
+++ b/packages/app/src/catalog/hooks/useCatalogItemDetails.ts
@@ -1,6 +1,6 @@
+import { apiClient } from '@packrat/app/lib/api/packrat';
+import { useAuthenticatedQueryToolkit } from '@packrat/app/lib/hooks/useAuthenticatedQueryToolkit';
import { useQuery } from '@tanstack/react-query';
-import { apiClient } from 'expo-app/lib/api/packrat';
-import { useAuthenticatedQueryToolkit } from 'expo-app/lib/hooks/useAuthenticatedQueryToolkit';
export const getCatalogItem = async (id: string) => {
const { data, error } = await apiClient.catalog({ id }).get();
diff --git a/apps/expo/features/catalog/hooks/useCatalogItems.ts b/packages/app/src/catalog/hooks/useCatalogItems.ts
similarity index 97%
rename from apps/expo/features/catalog/hooks/useCatalogItems.ts
rename to packages/app/src/catalog/hooks/useCatalogItems.ts
index 50e9e0e7c9..5494547bfb 100644
--- a/apps/expo/features/catalog/hooks/useCatalogItems.ts
+++ b/packages/app/src/catalog/hooks/useCatalogItems.ts
@@ -1,6 +1,6 @@
import { CatalogItemsResponseSchema } from '@packrat/api/schemas/catalog';
+import { apiClient } from '@packrat/app/lib/api/packrat';
import { useInfiniteQuery } from '@tanstack/react-query';
-import { apiClient } from 'expo-app/lib/api/packrat';
type CatalogSortField =
| 'name'
diff --git a/apps/expo/features/catalog/hooks/useCatalogItemsCategories.ts b/packages/app/src/catalog/hooks/useCatalogItemsCategories.ts
similarity index 85%
rename from apps/expo/features/catalog/hooks/useCatalogItemsCategories.ts
rename to packages/app/src/catalog/hooks/useCatalogItemsCategories.ts
index cb68f8976f..fe4594bf28 100644
--- a/apps/expo/features/catalog/hooks/useCatalogItemsCategories.ts
+++ b/packages/app/src/catalog/hooks/useCatalogItemsCategories.ts
@@ -1,6 +1,6 @@
+import { apiClient } from '@packrat/app/lib/api/packrat';
+import { useAuthenticatedQueryToolkit } from '@packrat/app/lib/hooks/useAuthenticatedQueryToolkit';
import { useQuery } from '@tanstack/react-query';
-import { apiClient } from 'expo-app/lib/api/packrat';
-import { useAuthenticatedQueryToolkit } from 'expo-app/lib/hooks/useAuthenticatedQueryToolkit';
const getCategories = async (): Promise => {
// Treaty types `limit` as required despite Zod's `.default(10)` — pass it
diff --git a/apps/expo/features/catalog/hooks/usePopularCatalogItems.ts b/packages/app/src/catalog/hooks/usePopularCatalogItems.ts
similarity index 100%
rename from apps/expo/features/catalog/hooks/usePopularCatalogItems.ts
rename to packages/app/src/catalog/hooks/usePopularCatalogItems.ts
diff --git a/apps/expo/features/catalog/hooks/useRecentlyUsedCatalogItems.ts b/packages/app/src/catalog/hooks/useRecentlyUsedCatalogItems.ts
similarity index 81%
rename from apps/expo/features/catalog/hooks/useRecentlyUsedCatalogItems.ts
rename to packages/app/src/catalog/hooks/useRecentlyUsedCatalogItems.ts
index f5db02667c..77eab615c4 100644
--- a/apps/expo/features/catalog/hooks/useRecentlyUsedCatalogItems.ts
+++ b/packages/app/src/catalog/hooks/useRecentlyUsedCatalogItems.ts
@@ -1,10 +1,10 @@
import {
buildUpdatedRecentlyUsed,
recentlyUsedCatalogItemsAtom,
-} from 'expo-app/atoms/recentlyUsedCatalogItemsAtom';
+} from '@packrat/app/atoms/recentlyUsedCatalogItemsAtom';
+import type { CatalogItem } from '@packrat/app/catalog';
import { useAtom } from 'jotai';
import { useCallback } from 'react';
-import type { CatalogItem } from '../types';
export function useRecentlyUsedCatalogItems() {
const [recentItems, setRecentItems] = useAtom(recentlyUsedCatalogItemsAtom);
diff --git a/apps/expo/features/catalog/hooks/useSimilarItems.ts b/packages/app/src/catalog/hooks/useSimilarItems.ts
similarity index 91%
rename from apps/expo/features/catalog/hooks/useSimilarItems.ts
rename to packages/app/src/catalog/hooks/useSimilarItems.ts
index 430fe66173..32c923f249 100644
--- a/apps/expo/features/catalog/hooks/useSimilarItems.ts
+++ b/packages/app/src/catalog/hooks/useSimilarItems.ts
@@ -1,7 +1,7 @@
+import type { CatalogItem } from '@packrat/app/catalog';
+import { apiClient } from '@packrat/app/lib/api/packrat';
+import { useAuthenticatedQueryToolkit } from '@packrat/app/lib/hooks/useAuthenticatedQueryToolkit';
import { useQuery } from '@tanstack/react-query';
-import { apiClient } from 'expo-app/lib/api/packrat';
-import { useAuthenticatedQueryToolkit } from 'expo-app/lib/hooks/useAuthenticatedQueryToolkit';
-import type { CatalogItem } from '../types';
export interface SimilarItem extends Omit {
similarity: number;
diff --git a/apps/expo/features/catalog/hooks/useVectorSearch.ts b/packages/app/src/catalog/hooks/useVectorSearch.ts
similarity index 84%
rename from apps/expo/features/catalog/hooks/useVectorSearch.ts
rename to packages/app/src/catalog/hooks/useVectorSearch.ts
index 70680e8f7b..d9bb4997ff 100644
--- a/apps/expo/features/catalog/hooks/useVectorSearch.ts
+++ b/packages/app/src/catalog/hooks/useVectorSearch.ts
@@ -1,6 +1,6 @@
+import { apiClient } from '@packrat/app/lib/api/packrat';
+import { useAuthenticatedQueryToolkit } from '@packrat/app/lib/hooks/useAuthenticatedQueryToolkit';
import { useQuery } from '@tanstack/react-query';
-import { apiClient } from 'expo-app/lib/api/packrat';
-import { useAuthenticatedQueryToolkit } from 'expo-app/lib/hooks/useAuthenticatedQueryToolkit';
const vectorSearchApi = async (query: string, limit?: number) => {
const { data, error } = await apiClient.catalog['vector-search'].get({
diff --git a/packages/app/src/catalog/index.ts b/packages/app/src/catalog/index.ts
new file mode 100644
index 0000000000..6a02bd2ff8
--- /dev/null
+++ b/packages/app/src/catalog/index.ts
@@ -0,0 +1,10 @@
+export type {
+ CatalogItem,
+ CatalogItemInput,
+ CatalogItemLink,
+ CatalogItemReview,
+ CatalogItemWithPackItemFields,
+ CatalogItemWithQuantity,
+ PaginatedCatalogItemsResponse,
+} from './types';
+export { normalizeDescription } from './utils/normalizeDescription';
diff --git a/apps/expo/features/catalog/lib/cacheCatalogItemImage.ts b/packages/app/src/catalog/lib/cacheCatalogItemImage.ts
similarity index 75%
rename from apps/expo/features/catalog/lib/cacheCatalogItemImage.ts
rename to packages/app/src/catalog/lib/cacheCatalogItemImage.ts
index e33322022c..bcc68d55b7 100644
--- a/apps/expo/features/catalog/lib/cacheCatalogItemImage.ts
+++ b/packages/app/src/catalog/lib/cacheCatalogItemImage.ts
@@ -1,5 +1,5 @@
-import ImageCacheManager from 'expo-app/lib/utils/ImageCacheManager';
-import { getImageExtension } from 'expo-app/lib/utils/imageUtils';
+import ImageCacheManager from '@packrat/app/lib/utils/ImageCacheManager';
+import { getImageExtension } from '@packrat/app/lib/utils/imageUtils';
import { nanoid } from 'nanoid';
export async function cacheCatalogItemImage(imageUrl?: string): Promise {
diff --git a/apps/expo/features/catalog/screens/AddCatalogItemDetailsScreen.tsx b/packages/app/src/catalog/screens/AddCatalogItemDetailsScreen.tsx
similarity index 95%
rename from apps/expo/features/catalog/screens/AddCatalogItemDetailsScreen.tsx
rename to packages/app/src/catalog/screens/AddCatalogItemDetailsScreen.tsx
index 80aec8997c..df05412781 100644
--- a/apps/expo/features/catalog/screens/AddCatalogItemDetailsScreen.tsx
+++ b/packages/app/src/catalog/screens/AddCatalogItemDetailsScreen.tsx
@@ -1,14 +1,16 @@
import { WeightUnitSchema } from '@packrat/api/types';
+import type { CatalogItem } from '@packrat/app/catalog';
+import { Icon } from '@packrat/app/components/Icon';
+import { TextInput } from '@packrat/app/components/TextInput';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
+import { useCreatePackItem } from '@packrat/app/packs/hooks/useCreatePackItem';
+import { usePackDetailsFromStore } from '@packrat/app/packs/hooks/usePackDetailsFromStore';
+import { ErrorScreen } from '@packrat/app/screens/ErrorScreen';
import { assertDefined, fromZod } from '@packrat/guards';
import { Button, Text } from '@packrat/ui/nativewindui';
import { useQueryClient } from '@tanstack/react-query';
import * as Burnt from 'burnt';
-import { Icon } from 'expo-app/components/Icon';
-import { TextInput } from 'expo-app/components/TextInput';
-import { useCreatePackItem, usePackDetailsFromStore } from 'expo-app/features/packs';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
-import { ErrorScreen } from 'expo-app/screens/ErrorScreen';
import { useLocalSearchParams, useRouter } from 'expo-router';
import { useEffect, useState } from 'react';
import {
@@ -24,7 +26,6 @@ import {
import { SafeAreaView } from 'react-native-safe-area-context';
import { useCatalogItemDetails } from '../hooks';
import { cacheCatalogItemImage } from '../lib/cacheCatalogItemImage';
-import type { CatalogItem } from '../types';
export function AddCatalogItemDetailsScreen() {
const router = useRouter();
diff --git a/apps/expo/features/catalog/screens/CatalogItemDetailScreen.tsx b/packages/app/src/catalog/screens/CatalogItemDetailScreen.tsx
similarity index 89%
rename from apps/expo/features/catalog/screens/CatalogItemDetailScreen.tsx
rename to packages/app/src/catalog/screens/CatalogItemDetailScreen.tsx
index 94a693bbc8..a8b39ec764 100644
--- a/apps/expo/features/catalog/screens/CatalogItemDetailScreen.tsx
+++ b/packages/app/src/catalog/screens/CatalogItemDetailScreen.tsx
@@ -1,24 +1,24 @@
import { Ionicons } from '@expo/vector-icons';
+import { normalizeDescription } from '@packrat/app/catalog';
+import { ItemLinks } from '@packrat/app/catalog/components/ItemLinks';
+import { ItemReviews } from '@packrat/app/catalog/components/ItemReviews';
+import { SimilarItems } from '@packrat/app/catalog/components/SimilarItems';
+import { Icon } from '@packrat/app/components/Icon';
+import { Chip } from '@packrat/app/components/initial/Chip';
+import { ExpandableText } from '@packrat/app/components/initial/ExpandableText';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
+import { testIds } from '@packrat/app/lib/testIds';
+import { decodeHtmlEntities } from '@packrat/app/lib/utils/decodeHtmlEntities';
+import { ErrorScreen } from '@packrat/app/screens/ErrorScreen';
+import { LoadingSpinnerScreen } from '@packrat/app/screens/LoadingSpinnerScreen';
+import { NotFoundScreen } from '@packrat/app/screens/NotFoundScreen';
import { Button, Text } from '@packrat/ui/nativewindui';
-import { Icon } from 'expo-app/components/Icon';
-import { Chip } from 'expo-app/components/initial/Chip';
-import { ExpandableText } from 'expo-app/components/initial/ExpandableText';
-import { ItemLinks } from 'expo-app/features/catalog/components/ItemLinks';
-import { ItemReviews } from 'expo-app/features/catalog/components/ItemReviews';
-import { SimilarItems } from 'expo-app/features/catalog/components/SimilarItems';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
-import { testIds } from 'expo-app/lib/testIds';
-import { decodeHtmlEntities } from 'expo-app/lib/utils/decodeHtmlEntities';
-import { ErrorScreen } from 'expo-app/screens/ErrorScreen';
-import { LoadingSpinnerScreen } from 'expo-app/screens/LoadingSpinnerScreen';
-import { NotFoundScreen } from 'expo-app/screens/NotFoundScreen';
import { useLocalSearchParams, useRouter } from 'expo-router';
import { Linking, Text as RNText, ScrollView, View } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { CatalogItemImage } from '../components/CatalogItemImage';
import { useCatalogItemDetails } from '../hooks';
-import { normalizeDescription } from '../lib/normalizeDescription';
export function CatalogItemDetailScreen() {
const router = useRouter();
diff --git a/apps/expo/features/catalog/screens/CatalogItemsScreen.tsx b/packages/app/src/catalog/screens/CatalogItemsScreen.tsx
similarity index 92%
rename from apps/expo/features/catalog/screens/CatalogItemsScreen.tsx
rename to packages/app/src/catalog/screens/CatalogItemsScreen.tsx
index 2cd1451a64..78753f3117 100644
--- a/apps/expo/features/catalog/screens/CatalogItemsScreen.tsx
+++ b/packages/app/src/catalog/screens/CatalogItemsScreen.tsx
@@ -1,15 +1,16 @@
+import { searchValueAtom } from '@packrat/app/atoms/itemListAtoms';
+import { withAuthWall } from '@packrat/app/auth/hocs';
+import type { CatalogItem } from '@packrat/app/catalog';
+import { AndroidTabBarInsetFix } from '@packrat/app/components/AndroidTabBarInsetFix';
+import { CategoriesFilter } from '@packrat/app/components/CategoriesFilter';
+import { Icon } from '@packrat/app/components/Icon';
+import { LargeTitleHeaderOverlapFixIOS } from '@packrat/app/components/LargeTitleHeaderOverlapFixIOS';
+import { LargeTitleHeaderSearchContentContainer } from '@packrat/app/components/LargeTitleHeaderSearchContentContainer';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
+import { testIds } from '@packrat/app/lib/testIds';
+import { asNonNullableRef } from '@packrat/app/lib/utils/asNonNullableRef';
import { LargeTitleHeader, type LargeTitleSearchBarMethods, Text } from '@packrat/ui/nativewindui';
-import { searchValueAtom } from 'expo-app/atoms/itemListAtoms';
-import { AndroidTabBarInsetFix } from 'expo-app/components/AndroidTabBarInsetFix';
-import { CategoriesFilter } from 'expo-app/components/CategoriesFilter';
-import { Icon } from 'expo-app/components/Icon';
-import { LargeTitleHeaderOverlapFixIOS } from 'expo-app/components/LargeTitleHeaderOverlapFixIOS';
-import { LargeTitleHeaderSearchContentContainer } from 'expo-app/components/LargeTitleHeaderSearchContentContainer';
-import { withAuthWall } from 'expo-app/features/auth/hocs';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
-import { testIds } from 'expo-app/lib/testIds';
-import { asNonNullableRef } from 'expo-app/lib/utils/asNonNullableRef';
import { useRouter } from 'expo-router';
import { useAtom } from 'jotai';
import { useMemo, useRef, useState } from 'react';
@@ -27,7 +28,6 @@ import { CatalogItemCard } from '../components/CatalogItemCard';
import { useCatalogItemsInfinite } from '../hooks';
import { useCatalogItemsCategories } from '../hooks/useCatalogItemsCategories';
import { useVectorSearch } from '../hooks/useVectorSearch';
-import type { CatalogItem } from '../types';
function CatalogItemsScreen() {
const router = useRouter();
diff --git a/apps/expo/features/catalog/screens/PackSelectionScreen.tsx b/packages/app/src/catalog/screens/PackSelectionScreen.tsx
similarity index 94%
rename from apps/expo/features/catalog/screens/PackSelectionScreen.tsx
rename to packages/app/src/catalog/screens/PackSelectionScreen.tsx
index 3ed7205a48..1d93722b78 100644
--- a/apps/expo/features/catalog/screens/PackSelectionScreen.tsx
+++ b/packages/app/src/catalog/screens/PackSelectionScreen.tsx
@@ -1,9 +1,9 @@
+import { Icon } from '@packrat/app/components/Icon';
+import { SearchInput } from '@packrat/app/components/SearchInput';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
+import { useDetailedPacks } from '@packrat/app/packs/hooks/useDetailedPacks';
import { Button, Text } from '@packrat/ui/nativewindui';
-import { Icon } from 'expo-app/components/Icon';
-import { SearchInput } from 'expo-app/components/SearchInput';
-import { useDetailedPacks } from 'expo-app/features/packs';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { useLocalSearchParams, useRouter } from 'expo-router';
import { useMemo, useState } from 'react';
import { FlatList, TouchableOpacity, View } from 'react-native';
diff --git a/apps/expo/features/catalog/types.ts b/packages/app/src/catalog/types.ts
similarity index 98%
rename from apps/expo/features/catalog/types.ts
rename to packages/app/src/catalog/types.ts
index 6c86cb7679..581c111235 100644
--- a/apps/expo/features/catalog/types.ts
+++ b/packages/app/src/catalog/types.ts
@@ -1,4 +1,4 @@
-import type { WeightUnit } from 'expo-app/types';
+import type { WeightUnit } from '@packrat/api/types';
import type { PackItemInput } from '../packs/input';
export interface CatalogItemLink {
diff --git a/apps/expo/features/catalog/lib/normalizeDescription.ts b/packages/app/src/catalog/utils/normalizeDescription.ts
similarity index 100%
rename from apps/expo/features/catalog/lib/normalizeDescription.ts
rename to packages/app/src/catalog/utils/normalizeDescription.ts
diff --git a/apps/expo/components/AndroidTabBarInsetFix.tsx b/packages/app/src/components/AndroidTabBarInsetFix.tsx
similarity index 100%
rename from apps/expo/components/AndroidTabBarInsetFix.tsx
rename to packages/app/src/components/AndroidTabBarInsetFix.tsx
diff --git a/apps/expo/components/BackButton.tsx b/packages/app/src/components/BackButton.tsx
similarity index 89%
rename from apps/expo/components/BackButton.tsx
rename to packages/app/src/components/BackButton.tsx
index a746b6c08b..a885f869e9 100644
--- a/apps/expo/components/BackButton.tsx
+++ b/packages/app/src/components/BackButton.tsx
@@ -1,5 +1,5 @@
import { Feather } from '@expo/vector-icons';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
import { StyleSheet, Text, View } from 'react-native';
export const BackButton = ({ onPress }: { onPress: () => void }) => {
diff --git a/apps/expo/components/Button.tsx b/packages/app/src/components/Button.tsx
similarity index 100%
rename from apps/expo/components/Button.tsx
rename to packages/app/src/components/Button.tsx
diff --git a/apps/expo/components/Card.tsx b/packages/app/src/components/Card.tsx
similarity index 100%
rename from apps/expo/components/Card.tsx
rename to packages/app/src/components/Card.tsx
diff --git a/apps/expo/components/CategoriesFilter.tsx b/packages/app/src/components/CategoriesFilter.tsx
similarity index 93%
rename from apps/expo/components/CategoriesFilter.tsx
rename to packages/app/src/components/CategoriesFilter.tsx
index b70144247a..a781ab05f7 100644
--- a/apps/expo/components/CategoriesFilter.tsx
+++ b/packages/app/src/components/CategoriesFilter.tsx
@@ -1,7 +1,7 @@
import { Ionicons } from '@expo/vector-icons';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
+import { decodeHtmlEntities } from '@packrat/app/lib/utils/decodeHtmlEntities';
import { Text } from '@packrat/ui/nativewindui';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
-import { decodeHtmlEntities } from 'expo-app/lib/utils/decodeHtmlEntities';
import { ScrollView, TouchableOpacity, View } from 'react-native';
export function CategoriesFilter({
diff --git a/apps/expo/components/Container.tsx b/packages/app/src/components/Container.tsx
similarity index 100%
rename from apps/expo/components/Container.tsx
rename to packages/app/src/components/Container.tsx
diff --git a/apps/expo/components/EditScreenInfo.tsx b/packages/app/src/components/EditScreenInfo.tsx
similarity index 100%
rename from apps/expo/components/EditScreenInfo.tsx
rename to packages/app/src/components/EditScreenInfo.tsx
diff --git a/apps/expo/components/ErrorState.tsx b/packages/app/src/components/ErrorState.tsx
similarity index 90%
rename from apps/expo/components/ErrorState.tsx
rename to packages/app/src/components/ErrorState.tsx
index efcfb200d1..b428b4a1b8 100644
--- a/apps/expo/components/ErrorState.tsx
+++ b/packages/app/src/components/ErrorState.tsx
@@ -1,6 +1,6 @@
+import { Icon } from '@packrat/app/components/Icon';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
import { Button, cn, Text, useColorScheme } from '@packrat/ui/nativewindui';
-import { Icon } from 'expo-app/components/Icon';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { View } from 'react-native';
interface ErrorStateProps {
diff --git a/apps/expo/components/HeaderButton.tsx b/packages/app/src/components/HeaderButton.tsx
similarity index 100%
rename from apps/expo/components/HeaderButton.tsx
rename to packages/app/src/components/HeaderButton.tsx
diff --git a/apps/expo/components/Icon/Icon.ios.tsx b/packages/app/src/components/Icon/Icon.ios.tsx
similarity index 100%
rename from apps/expo/components/Icon/Icon.ios.tsx
rename to packages/app/src/components/Icon/Icon.ios.tsx
diff --git a/apps/expo/components/Icon/Icon.tsx b/packages/app/src/components/Icon/Icon.tsx
similarity index 100%
rename from apps/expo/components/Icon/Icon.tsx
rename to packages/app/src/components/Icon/Icon.tsx
diff --git a/apps/expo/components/Icon/get-icon-names.ts b/packages/app/src/components/Icon/get-icon-names.ts
similarity index 100%
rename from apps/expo/components/Icon/get-icon-names.ts
rename to packages/app/src/components/Icon/get-icon-names.ts
diff --git a/apps/expo/components/Icon/index.ts b/packages/app/src/components/Icon/index.ts
similarity index 100%
rename from apps/expo/components/Icon/index.ts
rename to packages/app/src/components/Icon/index.ts
diff --git a/apps/expo/components/Icon/types.ts b/packages/app/src/components/Icon/types.ts
similarity index 100%
rename from apps/expo/components/Icon/types.ts
rename to packages/app/src/components/Icon/types.ts
diff --git a/apps/expo/components/LargeTitleHeaderOverlapFixIOS.tsx b/packages/app/src/components/LargeTitleHeaderOverlapFixIOS.tsx
similarity index 100%
rename from apps/expo/components/LargeTitleHeaderOverlapFixIOS.tsx
rename to packages/app/src/components/LargeTitleHeaderOverlapFixIOS.tsx
diff --git a/apps/expo/components/LargeTitleHeaderSearchContentContainer.tsx b/packages/app/src/components/LargeTitleHeaderSearchContentContainer.tsx
similarity index 100%
rename from apps/expo/components/LargeTitleHeaderSearchContentContainer.tsx
rename to packages/app/src/components/LargeTitleHeaderSearchContentContainer.tsx
diff --git a/apps/expo/components/Markdown.tsx b/packages/app/src/components/Markdown.tsx
similarity index 100%
rename from apps/expo/components/Markdown.tsx
rename to packages/app/src/components/Markdown.tsx
diff --git a/apps/expo/components/ScreenContent.tsx b/packages/app/src/components/ScreenContent.tsx
similarity index 100%
rename from apps/expo/components/ScreenContent.tsx
rename to packages/app/src/components/ScreenContent.tsx
diff --git a/apps/expo/components/SearchInput.tsx b/packages/app/src/components/SearchInput.tsx
similarity index 86%
rename from apps/expo/components/SearchInput.tsx
rename to packages/app/src/components/SearchInput.tsx
index a0eda25565..bc82aaeaa8 100644
--- a/apps/expo/components/SearchInput.tsx
+++ b/packages/app/src/components/SearchInput.tsx
@@ -1,7 +1,7 @@
+import { useKeyboardHideBlur } from '@packrat/app/lib/hooks/useKeyboardHideBlur';
+import { asNonNullableRef } from '@packrat/app/lib/utils/asNonNullableRef';
import { assertPresent } from '@packrat/guards';
import { SearchInput as NativeWindUISearchInput } from '@packrat/ui/nativewindui';
-import { useKeyboardHideBlur } from 'expo-app/lib/hooks/useKeyboardHideBlur';
-import { asNonNullableRef } from 'expo-app/lib/utils/asNonNullableRef';
import { forwardRef, useImperativeHandle, useRef } from 'react';
/**
diff --git a/apps/expo/components/TabBarIcon.tsx b/packages/app/src/components/TabBarIcon.tsx
similarity index 100%
rename from apps/expo/components/TabBarIcon.tsx
rename to packages/app/src/components/TabBarIcon.tsx
diff --git a/apps/expo/components/TextInput.tsx b/packages/app/src/components/TextInput.tsx
similarity index 84%
rename from apps/expo/components/TextInput.tsx
rename to packages/app/src/components/TextInput.tsx
index 8dd0219ed2..9b6eb24fe7 100644
--- a/apps/expo/components/TextInput.tsx
+++ b/packages/app/src/components/TextInput.tsx
@@ -1,6 +1,6 @@
+import { useKeyboardHideBlur } from '@packrat/app/lib/hooks/useKeyboardHideBlur';
+import { asNonNullableRef } from '@packrat/app/lib/utils/asNonNullableRef';
import { assertPresent } from '@packrat/guards';
-import { useKeyboardHideBlur } from 'expo-app/lib/hooks/useKeyboardHideBlur';
-import { asNonNullableRef } from 'expo-app/lib/utils/asNonNullableRef';
import { forwardRef, useImperativeHandle, useRef } from 'react';
import { TextInput as RNTextInput, type TextInputProps } from 'react-native';
diff --git a/apps/expo/components/ThemeToggle.tsx b/packages/app/src/components/ThemeToggle.tsx
similarity index 83%
rename from apps/expo/components/ThemeToggle.tsx
rename to packages/app/src/components/ThemeToggle.tsx
index dbea2cfd10..560f0f6f28 100644
--- a/apps/expo/components/ThemeToggle.tsx
+++ b/packages/app/src/components/ThemeToggle.tsx
@@ -1,7 +1,7 @@
-import { Icon } from 'expo-app/components/Icon';
-import { cn } from 'expo-app/lib/cn';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { COLORS } from 'expo-app/theme/colors';
+import { Icon } from '@packrat/app/components/Icon';
+import { cn } from '@packrat/app/lib/cn';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { COLORS } from '@packrat/app/theme/colors';
import { Pressable, View } from 'react-native';
import Animated, { LayoutAnimationConfig, ZoomInRotate } from 'react-native-reanimated';
diff --git a/apps/expo/components/ai-chatHeader.tsx b/packages/app/src/components/ai-chatHeader.tsx
similarity index 91%
rename from apps/expo/components/ai-chatHeader.tsx
rename to packages/app/src/components/ai-chatHeader.tsx
index 56455b6e53..b8df194b05 100644
--- a/apps/expo/components/ai-chatHeader.tsx
+++ b/packages/app/src/components/ai-chatHeader.tsx
@@ -1,8 +1,8 @@
+import { AIModeSelector } from '@packrat/app/ai/components/AIModeSelector';
+import { Icon } from '@packrat/app/components/Icon';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
import { Button, Text } from '@packrat/ui/nativewindui';
-import { Icon } from 'expo-app/components/Icon';
-import { AIModeSelector } from 'expo-app/features/ai/components/AIModeSelector';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { BlurView } from 'expo-blur';
import { router } from 'expo-router';
import { Platform, View } from 'react-native';
diff --git a/apps/expo/components/initial/Chip.tsx b/packages/app/src/components/initial/Chip.tsx
similarity index 97%
rename from apps/expo/components/initial/Chip.tsx
rename to packages/app/src/components/initial/Chip.tsx
index 8c532da624..ff60c44b89 100644
--- a/apps/expo/components/initial/Chip.tsx
+++ b/packages/app/src/components/initial/Chip.tsx
@@ -1,4 +1,4 @@
-import { cn } from 'expo-app/lib/cn';
+import { cn } from '@packrat/app/lib/cn';
import type React from 'react';
import { Text, View } from 'react-native';
diff --git a/apps/expo/components/initial/ErrorBoundary.tsx b/packages/app/src/components/initial/ErrorBoundary.tsx
similarity index 92%
rename from apps/expo/components/initial/ErrorBoundary.tsx
rename to packages/app/src/components/initial/ErrorBoundary.tsx
index 9f9f893418..c0c631cd12 100644
--- a/apps/expo/components/initial/ErrorBoundary.tsx
+++ b/packages/app/src/components/initial/ErrorBoundary.tsx
@@ -1,7 +1,7 @@
+import { Icon } from '@packrat/app/components/Icon';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
import * as Sentry from '@sentry/react-native';
-import { Icon } from 'expo-app/components/Icon';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { router } from 'expo-router';
import type { ErrorInfo, ReactElement } from 'react';
import { Pressable, Text, View } from 'react-native';
diff --git a/apps/expo/components/initial/ExpandableText.tsx b/packages/app/src/components/initial/ExpandableText.tsx
similarity index 100%
rename from apps/expo/components/initial/ExpandableText.tsx
rename to packages/app/src/components/initial/ExpandableText.tsx
diff --git a/apps/expo/components/initial/UserAvatar.tsx b/packages/app/src/components/initial/UserAvatar.tsx
similarity index 95%
rename from apps/expo/components/initial/UserAvatar.tsx
rename to packages/app/src/components/initial/UserAvatar.tsx
index 7ced670e65..25a5419192 100644
--- a/apps/expo/components/initial/UserAvatar.tsx
+++ b/packages/app/src/components/initial/UserAvatar.tsx
@@ -1,4 +1,4 @@
-import type { User } from 'expo-app/types';
+import type { User } from '@packrat/app/types';
import { Image, Text, View } from 'react-native';
type UserAvatarProps = {
diff --git a/apps/expo/components/initial/WeightBadge.tsx b/packages/app/src/components/initial/WeightBadge.tsx
similarity index 87%
rename from apps/expo/components/initial/WeightBadge.tsx
rename to packages/app/src/components/initial/WeightBadge.tsx
index 83c5326a36..19dade223d 100644
--- a/apps/expo/components/initial/WeightBadge.tsx
+++ b/packages/app/src/components/initial/WeightBadge.tsx
@@ -1,7 +1,7 @@
+import { cn } from '@packrat/app/lib/cn';
+import type { WeightUnit } from '@packrat/app/types';
+import { formatWeight } from '@packrat/app/utils/weight';
import { isString } from '@packrat/guards';
-import { cn } from 'expo-app/lib/cn';
-import type { WeightUnit } from 'expo-app/types';
-import { formatWeight } from 'expo-app/utils/weight';
import { Text, View } from 'react-native';
type WeightBadgeProps = {
diff --git a/apps/expo/config.ts b/packages/app/src/config.ts
similarity index 100%
rename from apps/expo/config.ts
rename to packages/app/src/config.ts
diff --git a/apps/expo/data/mockData.ts b/packages/app/src/data/mockData.ts
similarity index 98%
rename from apps/expo/data/mockData.ts
rename to packages/app/src/data/mockData.ts
index f39d3619e8..0345336cb8 100644
--- a/apps/expo/data/mockData.ts
+++ b/packages/app/src/data/mockData.ts
@@ -1,4 +1,4 @@
-import type { User } from 'expo-app/types';
+import type { User } from '@packrat/app/types';
// --- Users ---
export const mockUsers: [User, ...User[]] = [
diff --git a/apps/expo/features/feed/components/CommentItem.tsx b/packages/app/src/feed/components/CommentItem.tsx
similarity index 92%
rename from apps/expo/features/feed/components/CommentItem.tsx
rename to packages/app/src/feed/components/CommentItem.tsx
index 44aab46fa7..c2f6b65de9 100644
--- a/apps/expo/features/feed/components/CommentItem.tsx
+++ b/packages/app/src/feed/components/CommentItem.tsx
@@ -1,8 +1,8 @@
+import { Icon } from '@packrat/app/components/Icon';
+import type { Comment } from '@packrat/app/feed';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
import { Text } from '@packrat/ui/nativewindui';
-import { Icon } from 'expo-app/components/Icon';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
import { TouchableOpacity, View } from 'react-native';
-import type { Comment } from '../types';
import { formatAuthorName, formatRelativeDate } from '../utils';
interface CommentItemProps {
diff --git a/apps/expo/features/feed/components/FeedTile.tsx b/packages/app/src/feed/components/FeedTile.tsx
similarity index 86%
rename from apps/expo/features/feed/components/FeedTile.tsx
rename to packages/app/src/feed/components/FeedTile.tsx
index 86cd2ae911..5894a12f9c 100644
--- a/apps/expo/features/feed/components/FeedTile.tsx
+++ b/packages/app/src/feed/components/FeedTile.tsx
@@ -1,7 +1,7 @@
+import { Icon } from '@packrat/app/components/Icon';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
import { ListItem, Text } from '@packrat/ui/nativewindui';
-import { Icon } from 'expo-app/components/Icon';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { useRouter } from 'expo-router';
import { View } from 'react-native';
diff --git a/apps/expo/features/feed/components/PostCard.tsx b/packages/app/src/feed/components/PostCard.tsx
similarity index 95%
rename from apps/expo/features/feed/components/PostCard.tsx
rename to packages/app/src/feed/components/PostCard.tsx
index 66dc234f8f..ab6067178e 100644
--- a/apps/expo/features/feed/components/PostCard.tsx
+++ b/packages/app/src/feed/components/PostCard.tsx
@@ -1,6 +1,7 @@
+import { Icon } from '@packrat/app/components/Icon';
+import type { Post } from '@packrat/app/feed';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
import { Text } from '@packrat/ui/nativewindui';
-import { Icon } from 'expo-app/components/Icon';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
import { useRouter } from 'expo-router';
import { useCallback } from 'react';
import {
@@ -12,7 +13,6 @@ import {
TouchableOpacity,
View,
} from 'react-native';
-import type { Post } from '../types';
import { buildPostImageUrl, formatAuthorName, formatRelativeDate } from '../utils';
const SCREEN_WIDTH = Dimensions.get('window').width;
diff --git a/apps/expo/features/feed/components/index.ts b/packages/app/src/feed/components/index.ts
similarity index 100%
rename from apps/expo/features/feed/components/index.ts
rename to packages/app/src/feed/components/index.ts
diff --git a/apps/expo/features/feed/hooks/index.ts b/packages/app/src/feed/hooks/index.ts
similarity index 100%
rename from apps/expo/features/feed/hooks/index.ts
rename to packages/app/src/feed/hooks/index.ts
diff --git a/apps/expo/features/feed/hooks/useAddComment.ts b/packages/app/src/feed/hooks/useAddComment.ts
similarity index 93%
rename from apps/expo/features/feed/hooks/useAddComment.ts
rename to packages/app/src/feed/hooks/useAddComment.ts
index 12264fa19a..b911e58c20 100644
--- a/apps/expo/features/feed/hooks/useAddComment.ts
+++ b/packages/app/src/feed/hooks/useAddComment.ts
@@ -1,5 +1,5 @@
+import { apiClient } from '@packrat/app/lib/api/packrat';
import { useMutation, useQueryClient } from '@tanstack/react-query';
-import { apiClient } from 'expo-app/lib/api/packrat';
interface AddCommentInput {
postId: number;
diff --git a/apps/expo/features/feed/hooks/useCreatePost.ts b/packages/app/src/feed/hooks/useCreatePost.ts
similarity index 90%
rename from apps/expo/features/feed/hooks/useCreatePost.ts
rename to packages/app/src/feed/hooks/useCreatePost.ts
index 485af1d50c..a80a7f3fa5 100644
--- a/apps/expo/features/feed/hooks/useCreatePost.ts
+++ b/packages/app/src/feed/hooks/useCreatePost.ts
@@ -1,5 +1,5 @@
+import { apiClient } from '@packrat/app/lib/api/packrat';
import { useMutation, useQueryClient } from '@tanstack/react-query';
-import { apiClient } from 'expo-app/lib/api/packrat';
interface CreatePostInput {
caption?: string;
diff --git a/apps/expo/features/feed/hooks/useDeleteComment.ts b/packages/app/src/feed/hooks/useDeleteComment.ts
similarity index 92%
rename from apps/expo/features/feed/hooks/useDeleteComment.ts
rename to packages/app/src/feed/hooks/useDeleteComment.ts
index 75a7a852b2..8972936ee1 100644
--- a/apps/expo/features/feed/hooks/useDeleteComment.ts
+++ b/packages/app/src/feed/hooks/useDeleteComment.ts
@@ -1,5 +1,5 @@
+import { apiClient } from '@packrat/app/lib/api/packrat';
import { useMutation, useQueryClient } from '@tanstack/react-query';
-import { apiClient } from 'expo-app/lib/api/packrat';
export const useDeleteComment = () => {
const queryClient = useQueryClient();
diff --git a/apps/expo/features/feed/hooks/useDeletePost.ts b/packages/app/src/feed/hooks/useDeletePost.ts
similarity index 89%
rename from apps/expo/features/feed/hooks/useDeletePost.ts
rename to packages/app/src/feed/hooks/useDeletePost.ts
index d9347d7cbc..4e6880d9b7 100644
--- a/apps/expo/features/feed/hooks/useDeletePost.ts
+++ b/packages/app/src/feed/hooks/useDeletePost.ts
@@ -1,5 +1,5 @@
+import { apiClient } from '@packrat/app/lib/api/packrat';
import { useMutation, useQueryClient } from '@tanstack/react-query';
-import { apiClient } from 'expo-app/lib/api/packrat';
export const useDeletePost = () => {
const queryClient = useQueryClient();
diff --git a/apps/expo/features/feed/hooks/useFeed.ts b/packages/app/src/feed/hooks/useFeed.ts
similarity index 91%
rename from apps/expo/features/feed/hooks/useFeed.ts
rename to packages/app/src/feed/hooks/useFeed.ts
index ffed652b87..dceee116b0 100644
--- a/apps/expo/features/feed/hooks/useFeed.ts
+++ b/packages/app/src/feed/hooks/useFeed.ts
@@ -1,5 +1,5 @@
+import { apiClient } from '@packrat/app/lib/api/packrat';
import { useInfiniteQuery } from '@tanstack/react-query';
-import { apiClient } from 'expo-app/lib/api/packrat';
export const useFeed = () => {
return useInfiniteQuery({
diff --git a/apps/expo/features/feed/hooks/usePostComments.ts b/packages/app/src/feed/hooks/usePostComments.ts
similarity index 92%
rename from apps/expo/features/feed/hooks/usePostComments.ts
rename to packages/app/src/feed/hooks/usePostComments.ts
index 7354d7f33e..4fb826d91f 100644
--- a/apps/expo/features/feed/hooks/usePostComments.ts
+++ b/packages/app/src/feed/hooks/usePostComments.ts
@@ -1,5 +1,5 @@
+import { apiClient } from '@packrat/app/lib/api/packrat';
import { useInfiniteQuery } from '@tanstack/react-query';
-import { apiClient } from 'expo-app/lib/api/packrat';
export const usePostComments = (postId: number) => {
return useInfiniteQuery({
diff --git a/apps/expo/features/feed/hooks/useToggleCommentLike.ts b/packages/app/src/feed/hooks/useToggleCommentLike.ts
similarity index 92%
rename from apps/expo/features/feed/hooks/useToggleCommentLike.ts
rename to packages/app/src/feed/hooks/useToggleCommentLike.ts
index 72c50c5482..e6d19d9797 100644
--- a/apps/expo/features/feed/hooks/useToggleCommentLike.ts
+++ b/packages/app/src/feed/hooks/useToggleCommentLike.ts
@@ -1,5 +1,5 @@
+import { apiClient } from '@packrat/app/lib/api/packrat';
import { useMutation, useQueryClient } from '@tanstack/react-query';
-import { apiClient } from 'expo-app/lib/api/packrat';
export const useToggleCommentLike = () => {
const queryClient = useQueryClient();
diff --git a/apps/expo/features/feed/hooks/useTogglePostLike.ts b/packages/app/src/feed/hooks/useTogglePostLike.ts
similarity index 89%
rename from apps/expo/features/feed/hooks/useTogglePostLike.ts
rename to packages/app/src/feed/hooks/useTogglePostLike.ts
index bc0a1e1d06..e3e63e2c0e 100644
--- a/apps/expo/features/feed/hooks/useTogglePostLike.ts
+++ b/packages/app/src/feed/hooks/useTogglePostLike.ts
@@ -1,5 +1,5 @@
+import { apiClient } from '@packrat/app/lib/api/packrat';
import { useMutation, useQueryClient } from '@tanstack/react-query';
-import { apiClient } from 'expo-app/lib/api/packrat';
export const useTogglePostLike = () => {
const queryClient = useQueryClient();
diff --git a/packages/app/src/feed/index.ts b/packages/app/src/feed/index.ts
new file mode 100644
index 0000000000..ce93f81b87
--- /dev/null
+++ b/packages/app/src/feed/index.ts
@@ -0,0 +1,8 @@
+export type {
+ Comment,
+ CommentsResponse,
+ FeedResponse,
+ LikeToggleResponse,
+ Post,
+ PostAuthor,
+} from './types';
diff --git a/apps/expo/features/feed/screens/CreatePostScreen.tsx b/packages/app/src/feed/screens/CreatePostScreen.tsx
similarity index 95%
rename from apps/expo/features/feed/screens/CreatePostScreen.tsx
rename to packages/app/src/feed/screens/CreatePostScreen.tsx
index 68fa39c5e1..c79f21b41d 100644
--- a/apps/expo/features/feed/screens/CreatePostScreen.tsx
+++ b/packages/app/src/feed/screens/CreatePostScreen.tsx
@@ -1,9 +1,9 @@
+import { Icon } from '@packrat/app/components/Icon';
+import { TextInput } from '@packrat/app/components/TextInput';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
+import { uploadImage } from '@packrat/app/packs/utils/uploadImage';
import { ActivityIndicator, Button, Text } from '@packrat/ui/nativewindui';
-import { Icon } from 'expo-app/components/Icon';
-import { TextInput } from 'expo-app/components/TextInput';
-import { uploadImage } from 'expo-app/features/packs/utils/uploadImage';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import * as ImagePicker from 'expo-image-picker';
import { nanoid } from 'nanoid';
import { useCallback, useState } from 'react';
diff --git a/apps/expo/features/feed/screens/FeedScreen.tsx b/packages/app/src/feed/screens/FeedScreen.tsx
similarity index 92%
rename from apps/expo/features/feed/screens/FeedScreen.tsx
rename to packages/app/src/feed/screens/FeedScreen.tsx
index 43067d4019..35eafa3d0b 100644
--- a/apps/expo/features/feed/screens/FeedScreen.tsx
+++ b/packages/app/src/feed/screens/FeedScreen.tsx
@@ -1,14 +1,14 @@
+import { userStore } from '@packrat/app/auth/store';
+import { Icon } from '@packrat/app/components/Icon';
+import type { Post } from '@packrat/app/feed';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
import { ActivityIndicator, Button, LargeTitleHeader, Text } from '@packrat/ui/nativewindui';
-import { Icon } from 'expo-app/components/Icon';
-import { userStore } from 'expo-app/features/auth/store';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { useRouter } from 'expo-router';
import { useCallback } from 'react';
import { FlatList, RefreshControl, View } from 'react-native';
import { PostCard } from '../components/PostCard';
import { useDeletePost, useFeed, useTogglePostLike } from '../hooks';
-import type { Post } from '../types';
export const FeedScreen = () => {
const { t } = useTranslation();
diff --git a/apps/expo/features/feed/screens/PostDetailScreen.tsx b/packages/app/src/feed/screens/PostDetailScreen.tsx
similarity index 95%
rename from apps/expo/features/feed/screens/PostDetailScreen.tsx
rename to packages/app/src/feed/screens/PostDetailScreen.tsx
index 890aa55f62..da0f0fac15 100644
--- a/apps/expo/features/feed/screens/PostDetailScreen.tsx
+++ b/packages/app/src/feed/screens/PostDetailScreen.tsx
@@ -1,8 +1,9 @@
+import { Icon } from '@packrat/app/components/Icon';
+import { TextInput } from '@packrat/app/components/TextInput';
+import type { Comment, Post } from '@packrat/app/feed';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
import { ActivityIndicator, Text } from '@packrat/ui/nativewindui';
-import { Icon } from 'expo-app/components/Icon';
-import { TextInput } from 'expo-app/components/TextInput';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { useCallback, useRef, useState } from 'react';
import {
Dimensions,
@@ -24,7 +25,6 @@ import {
useToggleCommentLike,
useTogglePostLike,
} from '../hooks';
-import type { Comment, Post } from '../types';
import { buildPostImageUrl } from '../utils';
interface PostDetailScreenProps {
diff --git a/apps/expo/features/feed/screens/index.ts b/packages/app/src/feed/screens/index.ts
similarity index 100%
rename from apps/expo/features/feed/screens/index.ts
rename to packages/app/src/feed/screens/index.ts
diff --git a/apps/expo/features/feed/types.ts b/packages/app/src/feed/types.ts
similarity index 100%
rename from apps/expo/features/feed/types.ts
rename to packages/app/src/feed/types.ts
diff --git a/apps/expo/features/feed/utils/__tests__/feedUtils.test.ts b/packages/app/src/feed/utils/__tests__/feedUtils.test.ts
similarity index 97%
rename from apps/expo/features/feed/utils/__tests__/feedUtils.test.ts
rename to packages/app/src/feed/utils/__tests__/feedUtils.test.ts
index bcc717f084..76a94a9410 100644
--- a/apps/expo/features/feed/utils/__tests__/feedUtils.test.ts
+++ b/packages/app/src/feed/utils/__tests__/feedUtils.test.ts
@@ -10,11 +10,11 @@ vi.mock('@packrat/env/expo-client', () => ({
// Also mock getRelativeTime so that formatRelativeDate has a predictable
// alias target that does not depend on the current clock.
-vi.mock('expo-app/lib/utils/getRelativeTime', () => ({
+vi.mock('@packrat/app/lib/utils/getRelativeTime', () => ({
getRelativeTime: (input: string | Date) => `relative(${String(input)})`,
}));
-import type { Comment, Post } from '../../types';
+import type { Comment, Post } from '@packrat/app/feed';
import { buildPostImageUrl, formatAuthorName, formatRelativeDate } from '../index';
const basePost: Post = {
diff --git a/apps/expo/features/feed/utils/index.ts b/packages/app/src/feed/utils/index.ts
similarity index 71%
rename from apps/expo/features/feed/utils/index.ts
rename to packages/app/src/feed/utils/index.ts
index fdfca54b8c..00fe4636ff 100644
--- a/apps/expo/features/feed/utils/index.ts
+++ b/packages/app/src/feed/utils/index.ts
@@ -1,6 +1,6 @@
+import type { Comment, Post } from '@packrat/app/feed';
+import { getRelativeTime } from '@packrat/app/lib/utils/getRelativeTime';
import { clientEnvs } from '@packrat/env/expo-client';
-import { getRelativeTime } from 'expo-app/lib/utils/getRelativeTime';
-import type { Comment, Post } from '../types';
export function buildPostImageUrl(imageKey: string): string {
return `${clientEnvs.EXPO_PUBLIC_R2_PUBLIC_URL}/${imageKey}`;
@@ -16,6 +16,6 @@ export function formatAuthorName(entity: Post | Comment): string {
}
/**
- * @deprecated Use getRelativeTime from 'expo-app/lib/utils/getRelativeTime' directly.
+ * @deprecated Use getRelativeTime from '@packrat/app/lib/utils/getRelativeTime' directly.
*/
export const formatRelativeDate = getRelativeTime;
diff --git a/apps/expo/features/guides/components/GuideCard.tsx b/packages/app/src/guides/components/GuideCard.tsx
similarity index 96%
rename from apps/expo/features/guides/components/GuideCard.tsx
rename to packages/app/src/guides/components/GuideCard.tsx
index 48b695be1e..4ef5ce14dd 100644
--- a/apps/expo/features/guides/components/GuideCard.tsx
+++ b/packages/app/src/guides/components/GuideCard.tsx
@@ -1,7 +1,7 @@
+import type { Guide } from '@packrat/app/guides';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
import { Card, CardContent, CardTitle, Text } from '@packrat/ui/nativewindui';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { TouchableOpacity, View } from 'react-native';
-import type { Guide } from '../types';
interface GuideCardProps {
guide: Guide;
diff --git a/apps/expo/features/guides/components/GuidesTile.tsx b/packages/app/src/guides/components/GuidesTile.tsx
similarity index 86%
rename from apps/expo/features/guides/components/GuidesTile.tsx
rename to packages/app/src/guides/components/GuidesTile.tsx
index 7bb23bab64..80c3c30ded 100644
--- a/apps/expo/features/guides/components/GuidesTile.tsx
+++ b/packages/app/src/guides/components/GuidesTile.tsx
@@ -1,7 +1,7 @@
+import { Icon } from '@packrat/app/components/Icon';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
import { ListItem, Text } from '@packrat/ui/nativewindui';
-import { Icon } from 'expo-app/components/Icon';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { useRouter } from 'expo-router';
import { View } from 'react-native';
diff --git a/apps/expo/features/guides/components/index.ts b/packages/app/src/guides/components/index.ts
similarity index 100%
rename from apps/expo/features/guides/components/index.ts
rename to packages/app/src/guides/components/index.ts
diff --git a/apps/expo/features/guides/hooks/index.ts b/packages/app/src/guides/hooks/index.ts
similarity index 100%
rename from apps/expo/features/guides/hooks/index.ts
rename to packages/app/src/guides/hooks/index.ts
diff --git a/apps/expo/features/guides/hooks/useGuideCategories.ts b/packages/app/src/guides/hooks/useGuideCategories.ts
similarity index 84%
rename from apps/expo/features/guides/hooks/useGuideCategories.ts
rename to packages/app/src/guides/hooks/useGuideCategories.ts
index 25027a9e53..7a4ae62992 100644
--- a/apps/expo/features/guides/hooks/useGuideCategories.ts
+++ b/packages/app/src/guides/hooks/useGuideCategories.ts
@@ -1,6 +1,6 @@
+import { apiClient } from '@packrat/app/lib/api/packrat';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
import { useQuery } from '@tanstack/react-query';
-import { apiClient } from 'expo-app/lib/api/packrat';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
export const useGuideCategories = () => {
const { t } = useTranslation();
diff --git a/apps/expo/features/guides/hooks/useGuideDetails.ts b/packages/app/src/guides/hooks/useGuideDetails.ts
similarity index 86%
rename from apps/expo/features/guides/hooks/useGuideDetails.ts
rename to packages/app/src/guides/hooks/useGuideDetails.ts
index 2013cbb1df..428663292b 100644
--- a/apps/expo/features/guides/hooks/useGuideDetails.ts
+++ b/packages/app/src/guides/hooks/useGuideDetails.ts
@@ -1,5 +1,5 @@
+import { apiClient } from '@packrat/app/lib/api/packrat';
import { useQuery } from '@tanstack/react-query';
-import { apiClient } from 'expo-app/lib/api/packrat';
export const useGuideDetails = (id: string) => {
return useQuery({
diff --git a/apps/expo/features/guides/hooks/useGuides.ts b/packages/app/src/guides/hooks/useGuides.ts
similarity index 94%
rename from apps/expo/features/guides/hooks/useGuides.ts
rename to packages/app/src/guides/hooks/useGuides.ts
index 962c637db3..6bfdd1e01e 100644
--- a/apps/expo/features/guides/hooks/useGuides.ts
+++ b/packages/app/src/guides/hooks/useGuides.ts
@@ -1,5 +1,5 @@
+import { apiClient } from '@packrat/app/lib/api/packrat';
import { useInfiniteQuery } from '@tanstack/react-query';
-import { apiClient } from 'expo-app/lib/api/packrat';
interface UseGuidesParams {
category?: string;
diff --git a/apps/expo/features/guides/hooks/useSearchGuides.ts b/packages/app/src/guides/hooks/useSearchGuides.ts
similarity index 93%
rename from apps/expo/features/guides/hooks/useSearchGuides.ts
rename to packages/app/src/guides/hooks/useSearchGuides.ts
index ae64cfb708..fa09cae647 100644
--- a/apps/expo/features/guides/hooks/useSearchGuides.ts
+++ b/packages/app/src/guides/hooks/useSearchGuides.ts
@@ -1,5 +1,5 @@
+import { apiClient } from '@packrat/app/lib/api/packrat';
import { useInfiniteQuery } from '@tanstack/react-query';
-import { apiClient } from 'expo-app/lib/api/packrat';
interface UseSearchGuidesParams {
query: string;
diff --git a/packages/app/src/guides/index.ts b/packages/app/src/guides/index.ts
new file mode 100644
index 0000000000..aa4b0f3b22
--- /dev/null
+++ b/packages/app/src/guides/index.ts
@@ -0,0 +1 @@
+export type { Guide, GuidesListResponse, GuidesSearchResponse } from './types';
diff --git a/apps/expo/features/guides/screens/GuideDetailScreen.tsx b/packages/app/src/guides/screens/GuideDetailScreen.tsx
similarity index 92%
rename from apps/expo/features/guides/screens/GuideDetailScreen.tsx
rename to packages/app/src/guides/screens/GuideDetailScreen.tsx
index 2cd50eabe2..0bc8d486ae 100644
--- a/apps/expo/features/guides/screens/GuideDetailScreen.tsx
+++ b/packages/app/src/guides/screens/GuideDetailScreen.tsx
@@ -1,8 +1,8 @@
+import { Chip } from '@packrat/app/components/initial/Chip';
+import { Markdown } from '@packrat/app/components/Markdown';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
import { Text } from '@packrat/ui/nativewindui';
-import { Chip } from 'expo-app/components/initial/Chip';
-import { Markdown } from 'expo-app/components/Markdown';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { useLocalSearchParams, useNavigation } from 'expo-router';
import { useLayoutEffect } from 'react';
import { ActivityIndicator, ScrollView, View } from 'react-native';
diff --git a/apps/expo/features/guides/screens/GuidesListScreen.tsx b/packages/app/src/guides/screens/GuidesListScreen.tsx
similarity index 92%
rename from apps/expo/features/guides/screens/GuidesListScreen.tsx
rename to packages/app/src/guides/screens/GuidesListScreen.tsx
index 171a247400..bcc6271f27 100644
--- a/apps/expo/features/guides/screens/GuidesListScreen.tsx
+++ b/packages/app/src/guides/screens/GuidesListScreen.tsx
@@ -1,16 +1,16 @@
+import { CategoriesFilter } from '@packrat/app/components/CategoriesFilter';
+import { LargeTitleHeaderOverlapFixIOS } from '@packrat/app/components/LargeTitleHeaderOverlapFixIOS';
+import { LargeTitleHeaderSearchContentContainer } from '@packrat/app/components/LargeTitleHeaderSearchContentContainer';
+import type { Guide } from '@packrat/app/guides';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
+import { asNonNullableRef } from '@packrat/app/lib/utils/asNonNullableRef';
import { LargeTitleHeader, type LargeTitleSearchBarMethods, Text } from '@packrat/ui/nativewindui';
-import { CategoriesFilter } from 'expo-app/components/CategoriesFilter';
-import { LargeTitleHeaderOverlapFixIOS } from 'expo-app/components/LargeTitleHeaderOverlapFixIOS';
-import { LargeTitleHeaderSearchContentContainer } from 'expo-app/components/LargeTitleHeaderSearchContentContainer';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
-import { asNonNullableRef } from 'expo-app/lib/utils/asNonNullableRef';
import { useRouter } from 'expo-router';
import { useCallback, useRef, useState } from 'react';
import { ActivityIndicator, FlatList, RefreshControl, View } from 'react-native';
import { GuideCard } from '../components/GuideCard';
import { useGuideCategories, useGuides, useSearchGuides } from '../hooks';
-import type { Guide } from '../types';
export const GuidesListScreen = () => {
const router = useRouter();
diff --git a/apps/expo/features/guides/screens/index.ts b/packages/app/src/guides/screens/index.ts
similarity index 100%
rename from apps/expo/features/guides/screens/index.ts
rename to packages/app/src/guides/screens/index.ts
diff --git a/apps/expo/features/guides/types.ts b/packages/app/src/guides/types.ts
similarity index 100%
rename from apps/expo/features/guides/types.ts
rename to packages/app/src/guides/types.ts
diff --git a/packages/app/src/index.ts b/packages/app/src/index.ts
new file mode 100644
index 0000000000..58f0f60f7c
--- /dev/null
+++ b/packages/app/src/index.ts
@@ -0,0 +1,12 @@
+export * from './ai/index';
+export * from './ai-packs/index';
+export * from './catalog/index';
+export * from './feed/index';
+export * from './guides/index';
+export * from './pack-templates/index';
+export * from './packs/index';
+export * from './profile/index';
+export * from './trail-conditions/index';
+export * from './trips/index';
+export * from './wildlife/index';
+export * from './wildlife/index';
diff --git a/apps/expo/lib/api/index.ts b/packages/app/src/lib/api/index.ts
similarity index 100%
rename from apps/expo/lib/api/index.ts
rename to packages/app/src/lib/api/index.ts
diff --git a/apps/expo/lib/api/packrat.ts b/packages/app/src/lib/api/packrat.ts
similarity index 87%
rename from apps/expo/lib/api/packrat.ts
rename to packages/app/src/lib/api/packrat.ts
index 88151d08cf..5f6f22b06e 100644
--- a/apps/expo/lib/api/packrat.ts
+++ b/packages/app/src/lib/api/packrat.ts
@@ -1,12 +1,8 @@
import { createApiClient } from '@packrat/api-client';
+import { store } from '@packrat/app/atoms/store';
+import { needsReauthAtom, refreshTokenAtom, tokenAtom } from '@packrat/app/auth/atoms/authAtoms';
+import { isAuthed } from '@packrat/app/auth/store';
import { clientEnvs } from '@packrat/env/expo-client';
-import { store } from 'expo-app/atoms/store';
-import {
- needsReauthAtom,
- refreshTokenAtom,
- tokenAtom,
-} from 'expo-app/features/auth/atoms/authAtoms';
-import { isAuthed } from 'expo-app/features/auth/store';
import Storage from 'expo-sqlite/kv-store';
/**
@@ -19,7 +15,7 @@ import Storage from 'expo-sqlite/kv-store';
*
* Usage:
* ```ts
- * import { apiClient } from 'expo-app/lib/api/packrat';
+ * import { apiClient } from '@packrat/app/lib/api/packrat';
* const { data, error } = await apiClient.catalog.get({ query: { limit: 10 } });
* ```
*/
diff --git a/packages/app/src/lib/appAlert.ts b/packages/app/src/lib/appAlert.ts
new file mode 100644
index 0000000000..7bcdf9bec5
--- /dev/null
+++ b/packages/app/src/lib/appAlert.ts
@@ -0,0 +1,8 @@
+import type { AlertMethods } from '@packrat/ui/nativewindui';
+import type React from 'react';
+
+export let appAlert: React.RefObject;
+
+export function registerAppAlert(ref: React.RefObject): void {
+ appAlert = ref;
+}
diff --git a/apps/expo/lib/cn.ts b/packages/app/src/lib/cn.ts
similarity index 100%
rename from apps/expo/lib/cn.ts
rename to packages/app/src/lib/cn.ts
diff --git a/apps/expo/lib/constants.ts b/packages/app/src/lib/constants.ts
similarity index 100%
rename from apps/expo/lib/constants.ts
rename to packages/app/src/lib/constants.ts
diff --git a/apps/expo/lib/hasUnsyncedChanges.ts b/packages/app/src/lib/hasUnsyncedChanges.ts
similarity index 64%
rename from apps/expo/lib/hasUnsyncedChanges.ts
rename to packages/app/src/lib/hasUnsyncedChanges.ts
index 6d7a80f026..55ff11dad1 100644
--- a/apps/expo/lib/hasUnsyncedChanges.ts
+++ b/packages/app/src/lib/hasUnsyncedChanges.ts
@@ -1,12 +1,12 @@
import type { Observable, ObservableSyncState } from '@legendapp/state';
-import { userSyncState } from 'expo-app/features/auth/store';
+import { userSyncState } from '@packrat/app/auth/store';
import {
packTemplateItemsSyncState,
packTemplatesSyncState,
-} from 'expo-app/features/pack-templates/store';
-import { packItemsSyncState, packsSyncState } from 'expo-app/features/packs/store';
-import { packWeigthHistorySyncState } from 'expo-app/features/packs/store/packWeightHistory';
-import { tripsSyncState } from 'expo-app/features/trips/store/trips';
+} from '@packrat/app/pack-templates/store';
+import { packItemsSyncState, packsSyncState } from '@packrat/app/packs/store';
+import { packWeigthHistorySyncState } from '@packrat/app/packs/store/packWeightHistory';
+import { tripsSyncState } from '@packrat/app/trips/store/trips';
const hasPendingChanges = (syncState: Observable): boolean =>
Object.keys(syncState.getPendingChanges() || {}).length !== 0;
diff --git a/apps/expo/lib/hooks/useAuthenticatedQueryToolkit.ts b/packages/app/src/lib/hooks/useAuthenticatedQueryToolkit.ts
similarity index 78%
rename from apps/expo/lib/hooks/useAuthenticatedQueryToolkit.ts
rename to packages/app/src/lib/hooks/useAuthenticatedQueryToolkit.ts
index 90d6fe0f4f..c794e835f5 100644
--- a/apps/expo/lib/hooks/useAuthenticatedQueryToolkit.ts
+++ b/packages/app/src/lib/hooks/useAuthenticatedQueryToolkit.ts
@@ -1,4 +1,4 @@
-import { tokenAtom } from 'expo-app/features/auth/atoms/authAtoms';
+import { tokenAtom } from '@packrat/app/auth/atoms/authAtoms';
import { useAtomValue } from 'jotai';
export const useAuthenticatedQueryToolkit = () => {
diff --git a/apps/expo/lib/hooks/useBottomSheetAction.ts b/packages/app/src/lib/hooks/useBottomSheetAction.ts
similarity index 100%
rename from apps/expo/lib/hooks/useBottomSheetAction.ts
rename to packages/app/src/lib/hooks/useBottomSheetAction.ts
diff --git a/apps/expo/lib/hooks/useColorScheme.tsx b/packages/app/src/lib/hooks/useColorScheme.tsx
similarity index 96%
rename from apps/expo/lib/hooks/useColorScheme.tsx
rename to packages/app/src/lib/hooks/useColorScheme.tsx
index 3c637c8480..3678b1690d 100644
--- a/apps/expo/lib/hooks/useColorScheme.tsx
+++ b/packages/app/src/lib/hooks/useColorScheme.tsx
@@ -1,4 +1,4 @@
-import { COLORS } from 'expo-app/theme/colors';
+import { COLORS } from '@packrat/app/theme/colors';
import * as NavigationBar from 'expo-navigation-bar';
import { useColorScheme as useNativewindColorScheme } from 'nativewind';
import * as React from 'react';
diff --git a/apps/expo/lib/hooks/useHeaderSearchBar.tsx b/packages/app/src/lib/hooks/useHeaderSearchBar.tsx
similarity index 95%
rename from apps/expo/lib/hooks/useHeaderSearchBar.tsx
rename to packages/app/src/lib/hooks/useHeaderSearchBar.tsx
index b659073cf1..fc223fe804 100644
--- a/apps/expo/lib/hooks/useHeaderSearchBar.tsx
+++ b/packages/app/src/lib/hooks/useHeaderSearchBar.tsx
@@ -1,4 +1,4 @@
-import { COLORS } from 'expo-app/theme/colors';
+import { COLORS } from '@packrat/app/theme/colors';
import { useNavigation } from 'expo-router';
import * as React from 'react';
import type { SearchBarProps } from 'react-native-screens';
diff --git a/apps/expo/lib/hooks/useKeyboardHideBlur.tsx b/packages/app/src/lib/hooks/useKeyboardHideBlur.tsx
similarity index 100%
rename from apps/expo/lib/hooks/useKeyboardHideBlur.tsx
rename to packages/app/src/lib/hooks/useKeyboardHideBlur.tsx
diff --git a/apps/expo/lib/hooks/useTranslation.ts b/packages/app/src/lib/hooks/useTranslation.ts
similarity index 92%
rename from apps/expo/lib/hooks/useTranslation.ts
rename to packages/app/src/lib/hooks/useTranslation.ts
index a2aca75c9e..03534070ba 100644
--- a/apps/expo/lib/hooks/useTranslation.ts
+++ b/packages/app/src/lib/hooks/useTranslation.ts
@@ -14,7 +14,7 @@
*
* Usage:
* ```tsx
- * import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
+ * import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
*
* function MyComponent() {
* const { t } = useTranslation();
diff --git a/apps/expo/lib/i18n/EXAMPLES.tsx b/packages/app/src/lib/i18n/EXAMPLES.tsx
similarity index 92%
rename from apps/expo/lib/i18n/EXAMPLES.tsx
rename to packages/app/src/lib/i18n/EXAMPLES.tsx
index fa61040bb5..8ae9f08b48 100644
--- a/apps/expo/lib/i18n/EXAMPLES.tsx
+++ b/packages/app/src/lib/i18n/EXAMPLES.tsx
@@ -5,8 +5,8 @@
* system in the PackRat app.
*/
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
-import { t } from 'expo-app/lib/i18n';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
+import { t } from '@packrat/app/lib/i18n';
import { Button, Text, View } from 'react-native';
/**
@@ -128,11 +128,11 @@ export function ListExample() {
/**
* Quick Reference:
*
- * 1. Import the hook: import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
+ * 1. Import the hook: import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
* 2. Use in component: const { t } = useTranslation();
* 3. Translate text: t('section.key')
* 4. With variables: t('key', { variable: value })
- * 5. Outside components: import { t } from 'expo-app/lib/i18n'; t('key')
+ * 5. Outside components: import { t } from '@packrat/app/lib/i18n'; t('key')
*
* Translation file location: apps/expo/lib/i18n/locales/en.json
* TypeScript types: apps/expo/lib/i18n/i18next.d.ts (module augmentation)
diff --git a/apps/expo/lib/i18n/MIGRATION.md b/packages/app/src/lib/i18n/MIGRATION.md
similarity index 100%
rename from apps/expo/lib/i18n/MIGRATION.md
rename to packages/app/src/lib/i18n/MIGRATION.md
diff --git a/apps/expo/lib/i18n/QUICKSTART.md b/packages/app/src/lib/i18n/QUICKSTART.md
similarity index 100%
rename from apps/expo/lib/i18n/QUICKSTART.md
rename to packages/app/src/lib/i18n/QUICKSTART.md
diff --git a/apps/expo/lib/i18n/README.md b/packages/app/src/lib/i18n/README.md
similarity index 100%
rename from apps/expo/lib/i18n/README.md
rename to packages/app/src/lib/i18n/README.md
diff --git a/apps/expo/lib/i18n/i18next.d.ts b/packages/app/src/lib/i18n/i18next.d.ts
similarity index 100%
rename from apps/expo/lib/i18n/i18next.d.ts
rename to packages/app/src/lib/i18n/i18next.d.ts
diff --git a/apps/expo/lib/i18n/index.ts b/packages/app/src/lib/i18n/index.ts
similarity index 96%
rename from apps/expo/lib/i18n/index.ts
rename to packages/app/src/lib/i18n/index.ts
index 633be3593a..d09ff84fb6 100644
--- a/apps/expo/lib/i18n/index.ts
+++ b/packages/app/src/lib/i18n/index.ts
@@ -35,7 +35,7 @@ if (!i18next.isInitialized) {
*
* Usage:
* ```ts
- * import { t } from 'expo-app/lib/i18n';
+ * import { t } from '@packrat/app/lib/i18n';
* const msg = t('errors.somethingWentWrong');
* ```
*/
diff --git a/apps/expo/lib/i18n/locales/en.json b/packages/app/src/lib/i18n/locales/en.json
similarity index 100%
rename from apps/expo/lib/i18n/locales/en.json
rename to packages/app/src/lib/i18n/locales/en.json
diff --git a/apps/expo/lib/i18n/types.ts b/packages/app/src/lib/i18n/types.ts
similarity index 92%
rename from apps/expo/lib/i18n/types.ts
rename to packages/app/src/lib/i18n/types.ts
index 7653b36a72..e9f11ab800 100644
--- a/apps/expo/lib/i18n/types.ts
+++ b/packages/app/src/lib/i18n/types.ts
@@ -22,7 +22,7 @@ export type TranslationKeys = ParseKeys;
*
* Example:
* ```ts
- * import type { TranslationFunction } from 'expo-app/lib/i18n/types';
+ * import type { TranslationFunction } from '@packrat/app/lib/i18n/types';
* function getOptions(t: TranslationFunction) { ... }
* ```
*/
diff --git a/apps/expo/lib/kvStorage.ts b/packages/app/src/lib/kvStorage.ts
similarity index 100%
rename from apps/expo/lib/kvStorage.ts
rename to packages/app/src/lib/kvStorage.ts
diff --git a/apps/expo/lib/kvStorage.web.ts b/packages/app/src/lib/kvStorage.web.ts
similarity index 100%
rename from apps/expo/lib/kvStorage.web.ts
rename to packages/app/src/lib/kvStorage.web.ts
diff --git a/apps/expo/lib/persist-plugin.ts b/packages/app/src/lib/persist-plugin.ts
similarity index 100%
rename from apps/expo/lib/persist-plugin.ts
rename to packages/app/src/lib/persist-plugin.ts
diff --git a/apps/expo/lib/persist-plugin.web.ts b/packages/app/src/lib/persist-plugin.web.ts
similarity index 100%
rename from apps/expo/lib/persist-plugin.web.ts
rename to packages/app/src/lib/persist-plugin.web.ts
diff --git a/apps/expo/lib/store.ts b/packages/app/src/lib/store.ts
similarity index 100%
rename from apps/expo/lib/store.ts
rename to packages/app/src/lib/store.ts
diff --git a/apps/expo/lib/testIds.ts b/packages/app/src/lib/testIds.ts
similarity index 99%
rename from apps/expo/lib/testIds.ts
rename to packages/app/src/lib/testIds.ts
index fe5448600c..f514807f24 100644
--- a/apps/expo/lib/testIds.ts
+++ b/packages/app/src/lib/testIds.ts
@@ -8,7 +8,7 @@
* never hand-interpolate strings.
*
* Usage in components:
- * import { testIds } from 'expo-app/lib/testIds';
+ * import { testIds } from '@packrat/app/lib/testIds';
*
*
* Usage in Playwright specs:
diff --git a/apps/expo/lib/utils/ImageCacheManager.ts b/packages/app/src/lib/utils/ImageCacheManager.ts
similarity index 100%
rename from apps/expo/lib/utils/ImageCacheManager.ts
rename to packages/app/src/lib/utils/ImageCacheManager.ts
diff --git a/apps/expo/lib/utils/__tests__/ImageCacheManager.test.ts b/packages/app/src/lib/utils/__tests__/ImageCacheManager.test.ts
similarity index 100%
rename from apps/expo/lib/utils/__tests__/ImageCacheManager.test.ts
rename to packages/app/src/lib/utils/__tests__/ImageCacheManager.test.ts
diff --git a/apps/expo/lib/utils/__tests__/asNonNullableRef.test.ts b/packages/app/src/lib/utils/__tests__/asNonNullableRef.test.ts
similarity index 100%
rename from apps/expo/lib/utils/__tests__/asNonNullableRef.test.ts
rename to packages/app/src/lib/utils/__tests__/asNonNullableRef.test.ts
diff --git a/apps/expo/lib/utils/__tests__/buildImageUrl.test.ts b/packages/app/src/lib/utils/__tests__/buildImageUrl.test.ts
similarity index 100%
rename from apps/expo/lib/utils/__tests__/buildImageUrl.test.ts
rename to packages/app/src/lib/utils/__tests__/buildImageUrl.test.ts
diff --git a/apps/expo/lib/utils/__tests__/buildPackTemplateItemImageUrl.test.ts b/packages/app/src/lib/utils/__tests__/buildPackTemplateItemImageUrl.test.ts
similarity index 100%
rename from apps/expo/lib/utils/__tests__/buildPackTemplateItemImageUrl.test.ts
rename to packages/app/src/lib/utils/__tests__/buildPackTemplateItemImageUrl.test.ts
diff --git a/apps/expo/lib/utils/__tests__/compute-pack.test.ts b/packages/app/src/lib/utils/__tests__/compute-pack.test.ts
similarity index 98%
rename from apps/expo/lib/utils/__tests__/compute-pack.test.ts
rename to packages/app/src/lib/utils/__tests__/compute-pack.test.ts
index 90825eac36..3c7ff85103 100644
--- a/apps/expo/lib/utils/__tests__/compute-pack.test.ts
+++ b/packages/app/src/lib/utils/__tests__/compute-pack.test.ts
@@ -1,4 +1,4 @@
-import type { Pack, PackItem } from 'expo-app/types';
+import type { Pack, PackItem } from '@packrat/app/types';
import { describe, expect, it } from 'vitest';
import { computePacksWeights, computePackWeights } from '../compute-pack';
diff --git a/apps/expo/lib/utils/__tests__/decodeHtmlEntities.test.ts b/packages/app/src/lib/utils/__tests__/decodeHtmlEntities.test.ts
similarity index 100%
rename from apps/expo/lib/utils/__tests__/decodeHtmlEntities.test.ts
rename to packages/app/src/lib/utils/__tests__/decodeHtmlEntities.test.ts
diff --git a/apps/expo/lib/utils/__tests__/domain-specific-extensions.test.ts b/packages/app/src/lib/utils/__tests__/domain-specific-extensions.test.ts
similarity index 100%
rename from apps/expo/lib/utils/__tests__/domain-specific-extensions.test.ts
rename to packages/app/src/lib/utils/__tests__/domain-specific-extensions.test.ts
diff --git a/apps/expo/lib/utils/__tests__/getRelativeTime.test.ts b/packages/app/src/lib/utils/__tests__/getRelativeTime.test.ts
similarity index 100%
rename from apps/expo/lib/utils/__tests__/getRelativeTime.test.ts
rename to packages/app/src/lib/utils/__tests__/getRelativeTime.test.ts
diff --git a/apps/expo/lib/utils/__tests__/imageUtils.test.ts b/packages/app/src/lib/utils/__tests__/imageUtils.test.ts
similarity index 100%
rename from apps/expo/lib/utils/__tests__/imageUtils.test.ts
rename to packages/app/src/lib/utils/__tests__/imageUtils.test.ts
diff --git a/apps/expo/lib/utils/__tests__/itemCalculations.test.ts b/packages/app/src/lib/utils/__tests__/itemCalculations.test.ts
similarity index 100%
rename from apps/expo/lib/utils/__tests__/itemCalculations.test.ts
rename to packages/app/src/lib/utils/__tests__/itemCalculations.test.ts
diff --git a/apps/expo/lib/utils/asNonNullableRef.ts b/packages/app/src/lib/utils/asNonNullableRef.ts
similarity index 100%
rename from apps/expo/lib/utils/asNonNullableRef.ts
rename to packages/app/src/lib/utils/asNonNullableRef.ts
diff --git a/apps/expo/lib/utils/buildImageUrl.ts b/packages/app/src/lib/utils/buildImageUrl.ts
similarity index 65%
rename from apps/expo/lib/utils/buildImageUrl.ts
rename to packages/app/src/lib/utils/buildImageUrl.ts
index 014b951e7c..2cd4153dae 100644
--- a/apps/expo/lib/utils/buildImageUrl.ts
+++ b/packages/app/src/lib/utils/buildImageUrl.ts
@@ -1,6 +1,6 @@
+import type { PackTemplateItem } from '@packrat/app/pack-templates';
+import type { PackItem } from '@packrat/app/packs';
import { clientEnvs } from '@packrat/env/expo-client';
-import type { PackTemplateItem } from 'expo-app/features/pack-templates';
-import type { PackItem } from 'expo-app/features/packs';
export function buildImageUrl({ userId, image }: PackTemplateItem | PackItem): string {
const baseUrl = clientEnvs.EXPO_PUBLIC_R2_PUBLIC_URL;
diff --git a/apps/expo/lib/utils/buildPackTemplateItemImageUrl.ts b/packages/app/src/lib/utils/buildPackTemplateItemImageUrl.ts
similarity index 100%
rename from apps/expo/lib/utils/buildPackTemplateItemImageUrl.ts
rename to packages/app/src/lib/utils/buildPackTemplateItemImageUrl.ts
diff --git a/apps/expo/lib/utils/compute-pack.ts b/packages/app/src/lib/utils/compute-pack.ts
similarity index 96%
rename from apps/expo/lib/utils/compute-pack.ts
rename to packages/app/src/lib/utils/compute-pack.ts
index 22766ac1ea..89cad0d14e 100644
--- a/apps/expo/lib/utils/compute-pack.ts
+++ b/packages/app/src/lib/utils/compute-pack.ts
@@ -1,4 +1,4 @@
-import type { Pack, WeightUnit } from 'expo-app/types';
+import type { Pack, WeightUnit } from '@packrat/app/types';
// Convert weights to a standard unit (grams) for calculations
const convertToGrams = (weight: number, unit: WeightUnit): number => {
diff --git a/apps/expo/lib/utils/dateUtils.ts b/packages/app/src/lib/utils/dateUtils.ts
similarity index 100%
rename from apps/expo/lib/utils/dateUtils.ts
rename to packages/app/src/lib/utils/dateUtils.ts
diff --git a/apps/expo/lib/utils/decodeHtmlEntities.ts b/packages/app/src/lib/utils/decodeHtmlEntities.ts
similarity index 100%
rename from apps/expo/lib/utils/decodeHtmlEntities.ts
rename to packages/app/src/lib/utils/decodeHtmlEntities.ts
diff --git a/apps/expo/lib/utils/domain-specific-extensions.ts b/packages/app/src/lib/utils/domain-specific-extensions.ts
similarity index 100%
rename from apps/expo/lib/utils/domain-specific-extensions.ts
rename to packages/app/src/lib/utils/domain-specific-extensions.ts
diff --git a/apps/expo/lib/utils/getRelativeTime.ts b/packages/app/src/lib/utils/getRelativeTime.ts
similarity index 95%
rename from apps/expo/lib/utils/getRelativeTime.ts
rename to packages/app/src/lib/utils/getRelativeTime.ts
index d77644a356..84c123613e 100644
--- a/apps/expo/lib/utils/getRelativeTime.ts
+++ b/packages/app/src/lib/utils/getRelativeTime.ts
@@ -1,5 +1,5 @@
+import type { TranslationFunction } from '@packrat/app/lib/i18n/types';
import { differenceInSeconds } from 'date-fns';
-import type { TranslationFunction } from 'expo-app/lib/i18n/types';
const UNITS: Array<{ key: string; seconds: number }> = [
{ key: 'months', seconds: 2592000 },
diff --git a/apps/expo/lib/utils/imageUtils.ts b/packages/app/src/lib/utils/imageUtils.ts
similarity index 100%
rename from apps/expo/lib/utils/imageUtils.ts
rename to packages/app/src/lib/utils/imageUtils.ts
diff --git a/apps/expo/lib/utils/itemCalculations.ts b/packages/app/src/lib/utils/itemCalculations.ts
similarity index 90%
rename from apps/expo/lib/utils/itemCalculations.ts
rename to packages/app/src/lib/utils/itemCalculations.ts
index db18761832..0876586a98 100644
--- a/apps/expo/lib/utils/itemCalculations.ts
+++ b/packages/app/src/lib/utils/itemCalculations.ts
@@ -1,9 +1,9 @@
-import { makeEnumGuard } from '@packrat/guards';
-import type { CatalogItem } from 'expo-app/features/catalog/types';
+import type { CatalogItem } from '@packrat/app/catalog';
// Leaf import (not the barrel) — barrel re-exports hooks, one of which
// imports back into this file, which would be a circular dependency.
-import type { PackTemplateItem } from 'expo-app/features/pack-templates/types';
-import type { PackItem, WeightUnit } from 'expo-app/features/packs';
+import type { PackTemplateItem } from '@packrat/app/pack-templates';
+import type { PackItem, WeightUnit } from '@packrat/app/packs';
+import { makeEnumGuard } from '@packrat/guards';
type Item = CatalogItem | PackItem | PackTemplateItem;
diff --git a/apps/expo/features/offline-ai/__tests__/offline-ai.test.ts b/packages/app/src/offline-ai/offline-ai/__tests__/offline-ai.test.ts
similarity index 100%
rename from apps/expo/features/offline-ai/__tests__/offline-ai.test.ts
rename to packages/app/src/offline-ai/offline-ai/__tests__/offline-ai.test.ts
diff --git a/apps/expo/features/offline-ai/lib/MockLLMProvider.ts b/packages/app/src/offline-ai/offline-ai/lib/MockLLMProvider.ts
similarity index 100%
rename from apps/expo/features/offline-ai/lib/MockLLMProvider.ts
rename to packages/app/src/offline-ai/offline-ai/lib/MockLLMProvider.ts
diff --git a/apps/expo/features/pack-templates/components/AddPackTemplateItemActions.tsx b/packages/app/src/pack-templates/components/AddPackTemplateItemActions.tsx
similarity index 92%
rename from apps/expo/features/pack-templates/components/AddPackTemplateItemActions.tsx
rename to packages/app/src/pack-templates/components/AddPackTemplateItemActions.tsx
index e056c88c17..acb6dcf073 100644
--- a/apps/expo/features/pack-templates/components/AddPackTemplateItemActions.tsx
+++ b/packages/app/src/pack-templates/components/AddPackTemplateItemActions.tsx
@@ -1,17 +1,17 @@
import { useActionSheet } from '@expo/react-native-action-sheet';
import type { BottomSheetModal } from '@gorhom/bottom-sheet';
import { BottomSheetView } from '@gorhom/bottom-sheet';
+import { isAuthed } from '@packrat/app/auth/store';
+import type { CatalogItem } from '@packrat/app/catalog';
+import { CatalogBrowserModal } from '@packrat/app/catalog/components';
+import { useRecentlyUsedCatalogItems } from '@packrat/app/catalog/hooks/useRecentlyUsedCatalogItems';
+import { Icon } from '@packrat/app/components/Icon';
+import { appAlert } from '@packrat/app/lib/appAlert';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
+import { useImagePicker } from '@packrat/app/packs/hooks/useImagePicker';
import { isFunction, nullToUndefined } from '@packrat/guards';
import { Sheet, Text, useColorScheme } from '@packrat/ui/nativewindui';
import * as Burnt from 'burnt';
-import { appAlert } from 'expo-app/app/_layout';
-import { Icon } from 'expo-app/components/Icon';
-import { isAuthed } from 'expo-app/features/auth/store';
-import { CatalogBrowserModal } from 'expo-app/features/catalog/components';
-import { useRecentlyUsedCatalogItems } from 'expo-app/features/catalog/hooks/useRecentlyUsedCatalogItems';
-import type { CatalogItem } from 'expo-app/features/catalog/types';
-import { useImagePicker } from 'expo-app/features/packs';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { router } from 'expo-router';
import React from 'react';
import { TouchableOpacity, View } from 'react-native';
diff --git a/apps/expo/features/pack-templates/components/AppTemplateBadge.tsx b/packages/app/src/pack-templates/components/AppTemplateBadge.tsx
similarity index 77%
rename from apps/expo/features/pack-templates/components/AppTemplateBadge.tsx
rename to packages/app/src/pack-templates/components/AppTemplateBadge.tsx
index 8b7ddfca6d..3759ba714b 100644
--- a/apps/expo/features/pack-templates/components/AppTemplateBadge.tsx
+++ b/packages/app/src/pack-templates/components/AppTemplateBadge.tsx
@@ -1,8 +1,8 @@
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
import { Text } from '@packrat/ui/nativewindui';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { Image, View } from 'react-native';
-const LOGO_SOURCE = require('expo-app/assets/adaptive-icon.png');
+const LOGO_SOURCE = require('@packrat/app/assets/adaptive-icon.png');
export function AppTemplateBadge() {
const { t } = useTranslation();
diff --git a/apps/expo/features/pack-templates/components/FeaturedPacksSection.tsx b/packages/app/src/pack-templates/components/FeaturedPacksSection.tsx
similarity index 95%
rename from apps/expo/features/pack-templates/components/FeaturedPacksSection.tsx
rename to packages/app/src/pack-templates/components/FeaturedPacksSection.tsx
index 81355a0ef1..409e35f81d 100644
--- a/apps/expo/features/pack-templates/components/FeaturedPacksSection.tsx
+++ b/packages/app/src/pack-templates/components/FeaturedPacksSection.tsx
@@ -1,13 +1,13 @@
+import { WeightBadge } from '@packrat/app/components/initial/WeightBadge';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
+import type { PackTemplate, PackTemplateInStore } from '@packrat/app/pack-templates';
import { Text } from '@packrat/ui/nativewindui';
-import { WeightBadge } from 'expo-app/components/initial/WeightBadge';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { useRouter } from 'expo-router';
import { isArray } from 'radash';
import { useMemo } from 'react';
import { Image, Pressable, ScrollView, View } from 'react-native';
import { usePackTemplates } from '../hooks';
import { usePackTemplateSummaries } from '../hooks/usePackTemplateSummary';
-import type { PackTemplate, PackTemplateInStore } from '../types';
type FeaturedPackCardProps = {
template: PackTemplateInStore;
diff --git a/apps/expo/features/pack-templates/components/OnlineContentImportModal.tsx b/packages/app/src/pack-templates/components/OnlineContentImportModal.tsx
similarity index 96%
rename from apps/expo/features/pack-templates/components/OnlineContentImportModal.tsx
rename to packages/app/src/pack-templates/components/OnlineContentImportModal.tsx
index 2525a92451..ae2a26a43f 100644
--- a/apps/expo/features/pack-templates/components/OnlineContentImportModal.tsx
+++ b/packages/app/src/pack-templates/components/OnlineContentImportModal.tsx
@@ -1,8 +1,8 @@
+import { Icon } from '@packrat/app/components/Icon';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
import { ActivityIndicator, Text, TextField } from '@packrat/ui/nativewindui';
import * as Burnt from 'burnt';
-import { Icon } from 'expo-app/components/Icon';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { useRouter } from 'expo-router';
import { useState } from 'react';
import {
diff --git a/apps/expo/features/pack-templates/components/PackTemplateCard.tsx b/packages/app/src/pack-templates/components/PackTemplateCard.tsx
similarity index 92%
rename from apps/expo/features/pack-templates/components/PackTemplateCard.tsx
rename to packages/app/src/pack-templates/components/PackTemplateCard.tsx
index 1c59445b2e..47ee32129d 100644
--- a/apps/expo/features/pack-templates/components/PackTemplateCard.tsx
+++ b/packages/app/src/pack-templates/components/PackTemplateCard.tsx
@@ -1,17 +1,17 @@
import { useActionSheet } from '@expo/react-native-action-sheet';
+import { Icon } from '@packrat/app/components/Icon';
+import { WeightBadge } from '@packrat/app/components/initial/WeightBadge';
+import { appAlert } from '@packrat/app/lib/appAlert';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
+import type { PackTemplate } from '@packrat/app/pack-templates';
import { isArray } from '@packrat/guards';
import { Button, Text } from '@packrat/ui/nativewindui';
-import { appAlert } from 'expo-app/app/_layout';
-import { Icon } from 'expo-app/components/Icon';
-import { WeightBadge } from 'expo-app/components/initial/WeightBadge';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { router } from 'expo-router';
import { Image, Pressable, View } from 'react-native';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { useDeletePackTemplate, usePackTemplateDetails } from '../hooks';
import { useWritePermissionCheck } from '../hooks/useWritePermissionCheck';
-import type { PackTemplate } from '../types';
import { AppTemplateBadge } from './AppTemplateBadge';
type PackTemplateCard = {
diff --git a/apps/expo/features/pack-templates/components/PackTemplateForm.tsx b/packages/app/src/pack-templates/components/PackTemplateForm.tsx
similarity index 96%
rename from apps/expo/features/pack-templates/components/PackTemplateForm.tsx
rename to packages/app/src/pack-templates/components/PackTemplateForm.tsx
index a52eb54cb3..181efae69e 100644
--- a/apps/expo/features/pack-templates/components/PackTemplateForm.tsx
+++ b/packages/app/src/pack-templates/components/PackTemplateForm.tsx
@@ -1,4 +1,9 @@
import { PackCategorySchema } from '@packrat/api/types';
+import { useUser } from '@packrat/app/auth/hooks/useUser';
+import { Icon } from '@packrat/app/components/Icon';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
+import type { PackTemplate } from '@packrat/app/pack-templates';
import { fromZod } from '@packrat/guards';
import {
Button,
@@ -10,10 +15,6 @@ import {
TextField,
} from '@packrat/ui/nativewindui';
import { useForm } from '@tanstack/react-form';
-import { Icon } from 'expo-app/components/Icon';
-import { useUser } from 'expo-app/features/auth/hooks/useUser';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { useRouter } from 'expo-router';
import {
KeyboardAvoidingView,
@@ -27,7 +28,6 @@ import {
import { z } from 'zod';
import { useCreatePackTemplate } from '../hooks/useCreatePackTemplate';
import { useUpdatePackTemplate } from '../hooks/useUpdatePacktemplate';
-import type { PackTemplate } from '../types';
export const PackTemplateForm = ({ template }: { template?: PackTemplate }) => {
const router = useRouter();
diff --git a/apps/expo/features/pack-templates/components/PackTemplateItemCard.tsx b/packages/app/src/pack-templates/components/PackTemplateItemCard.tsx
similarity index 91%
rename from apps/expo/features/pack-templates/components/PackTemplateItemCard.tsx
rename to packages/app/src/pack-templates/components/PackTemplateItemCard.tsx
index e039899494..2c8a6c208d 100644
--- a/apps/expo/features/pack-templates/components/PackTemplateItemCard.tsx
+++ b/packages/app/src/pack-templates/components/PackTemplateItemCard.tsx
@@ -1,16 +1,16 @@
import { useActionSheet } from '@expo/react-native-action-sheet';
+import { useUser } from '@packrat/app/auth/hooks/useUser';
+import { Icon } from '@packrat/app/components/Icon';
+import { appAlert } from '@packrat/app/lib/appAlert';
+import { cn } from '@packrat/app/lib/cn';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
+import type { PackTemplateItem } from '@packrat/app/pack-templates';
import { Text } from '@packrat/ui/nativewindui';
-import { appAlert } from 'expo-app/app/_layout';
-import { Icon } from 'expo-app/components/Icon';
-import { useUser } from 'expo-app/features/auth/hooks/useUser';
-import { cn } from 'expo-app/lib/cn';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { useRouter } from 'expo-router';
import { Pressable, TouchableWithoutFeedback, View } from 'react-native';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { useDeletePackTemplateItem } from '../hooks';
-import type { PackTemplateItem } from '../types';
import { PackTemplateItemImage } from './PackTemplateItemImage';
type PackTemplateItemCardProps = {
diff --git a/apps/expo/features/pack-templates/components/PackTemplateItemImage.tsx b/packages/app/src/pack-templates/components/PackTemplateItemImage.tsx
similarity index 70%
rename from apps/expo/features/pack-templates/components/PackTemplateItemImage.tsx
rename to packages/app/src/pack-templates/components/PackTemplateItemImage.tsx
index 38d07369ba..a7806916cf 100644
--- a/apps/expo/features/pack-templates/components/PackTemplateItemImage.tsx
+++ b/packages/app/src/pack-templates/components/PackTemplateItemImage.tsx
@@ -1,10 +1,10 @@
+import { CatalogItemImage } from '@packrat/app/catalog/components/CatalogItemImage';
+import { Icon } from '@packrat/app/components/Icon';
+import { buildImageUrl } from '@packrat/app/lib/utils/buildImageUrl';
+import type { PackTemplateItem } from '@packrat/app/pack-templates';
+import { CachedImage } from '@packrat/app/packs/components/CachedImage';
import { useColorScheme } from '@packrat/ui/nativewindui';
-import { Icon } from 'expo-app/components/Icon';
-import { CatalogItemImage } from 'expo-app/features/catalog/components/CatalogItemImage';
-import { CachedImage } from 'expo-app/features/packs/components/CachedImage';
-import { buildImageUrl } from 'expo-app/lib/utils/buildImageUrl';
import { type ImageProps, View } from 'react-native';
-import type { PackTemplateItem } from '../types';
interface PackTemplateItemImageProps extends Omit {
item: PackTemplateItem;
diff --git a/apps/expo/features/pack-templates/components/PackTemplatesTile.tsx b/packages/app/src/pack-templates/components/PackTemplatesTile.tsx
similarity index 87%
rename from apps/expo/features/pack-templates/components/PackTemplatesTile.tsx
rename to packages/app/src/pack-templates/components/PackTemplatesTile.tsx
index 3b9c5f0a5e..33c9e02db6 100644
--- a/apps/expo/features/pack-templates/components/PackTemplatesTile.tsx
+++ b/packages/app/src/pack-templates/components/PackTemplatesTile.tsx
@@ -1,7 +1,7 @@
+import { Icon } from '@packrat/app/components/Icon';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
import { ListItem, Text } from '@packrat/ui/nativewindui';
-import { Icon } from 'expo-app/components/Icon';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { useRouter } from 'expo-router';
import { Platform, View } from 'react-native';
import { usePackTemplates } from '../hooks';
diff --git a/apps/expo/features/pack-templates/components/TemplateCreationOptions.tsx b/packages/app/src/pack-templates/components/TemplateCreationOptions.tsx
similarity index 91%
rename from apps/expo/features/pack-templates/components/TemplateCreationOptions.tsx
rename to packages/app/src/pack-templates/components/TemplateCreationOptions.tsx
index 514b3bf0ce..08cddc5761 100644
--- a/apps/expo/features/pack-templates/components/TemplateCreationOptions.tsx
+++ b/packages/app/src/pack-templates/components/TemplateCreationOptions.tsx
@@ -1,12 +1,12 @@
import type { BottomSheetModal } from '@gorhom/bottom-sheet';
import { BottomSheetView } from '@gorhom/bottom-sheet';
+import { useAuth } from '@packrat/app/auth/hooks/useAuth';
+import { useUser } from '@packrat/app/auth/hooks/useUser';
+import { Icon } from '@packrat/app/components/Icon';
+import { useBottomSheetAction } from '@packrat/app/lib/hooks/useBottomSheetAction';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
import { Sheet, Text } from '@packrat/ui/nativewindui';
-import { Icon } from 'expo-app/components/Icon';
-import { useAuth } from 'expo-app/features/auth/hooks/useAuth';
-import { useUser } from 'expo-app/features/auth/hooks/useUser';
-import { useBottomSheetAction } from 'expo-app/lib/hooks/useBottomSheetAction';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { useRouter } from 'expo-router';
import React, { useState } from 'react';
import { TouchableOpacity, View } from 'react-native';
diff --git a/apps/expo/features/pack-templates/hooks/index.ts b/packages/app/src/pack-templates/hooks/index.ts
similarity index 100%
rename from apps/expo/features/pack-templates/hooks/index.ts
rename to packages/app/src/pack-templates/hooks/index.ts
diff --git a/apps/expo/features/pack-templates/hooks/useBulkAddCatalogItems.ts b/packages/app/src/pack-templates/hooks/useBulkAddCatalogItems.ts
similarity index 90%
rename from apps/expo/features/pack-templates/hooks/useBulkAddCatalogItems.ts
rename to packages/app/src/pack-templates/hooks/useBulkAddCatalogItems.ts
index ffb1e49686..d5982875ae 100644
--- a/apps/expo/features/pack-templates/hooks/useBulkAddCatalogItems.ts
+++ b/packages/app/src/pack-templates/hooks/useBulkAddCatalogItems.ts
@@ -1,7 +1,7 @@
import { WeightUnitSchema } from '@packrat/api/types';
+import type { CatalogItemWithPackItemFields } from '@packrat/app/catalog';
+import { cacheCatalogItemImage } from '@packrat/app/catalog/lib/cacheCatalogItemImage';
import { fromZod } from '@packrat/guards';
-import { cacheCatalogItemImage } from 'expo-app/features/catalog/lib/cacheCatalogItemImage';
-import type { CatalogItemWithPackItemFields } from 'expo-app/features/catalog/types';
import { useState } from 'react';
import { useCreatePackTemplateItem } from './useCreatePackTemplateItem';
diff --git a/apps/expo/features/pack-templates/hooks/useCreatePackFromTemplate.ts b/packages/app/src/pack-templates/hooks/useCreatePackFromTemplate.ts
similarity index 70%
rename from apps/expo/features/pack-templates/hooks/useCreatePackFromTemplate.ts
rename to packages/app/src/pack-templates/hooks/useCreatePackFromTemplate.ts
index 7b246a336c..a874a9d1ad 100644
--- a/apps/expo/features/pack-templates/hooks/useCreatePackFromTemplate.ts
+++ b/packages/app/src/pack-templates/hooks/useCreatePackFromTemplate.ts
@@ -1,8 +1,9 @@
// hooks/useCreatePackFromTemplate.ts
-import { getTemplateItems } from 'expo-app/features/pack-templates/store';
-import { useCreatePack, useCreatePackItem } from 'expo-app/features/packs';
-import type { PackInput } from 'expo-app/features/packs/types';
+import { getTemplateItems } from '@packrat/app/pack-templates/store';
+import type { PackInput } from '@packrat/app/packs';
+import { useCreatePack } from '@packrat/app/packs/hooks/useCreatePack';
+import { useCreatePackItem } from '@packrat/app/packs/hooks/useCreatePackItem';
import { useCallback } from 'react';
export function useCreatePackFromTemplate() {
diff --git a/apps/expo/features/pack-templates/hooks/useCreatePackTemplate.ts b/packages/app/src/pack-templates/hooks/useCreatePackTemplate.ts
similarity index 80%
rename from apps/expo/features/pack-templates/hooks/useCreatePackTemplate.ts
rename to packages/app/src/pack-templates/hooks/useCreatePackTemplate.ts
index ea9fbc9155..6af5787fc5 100644
--- a/apps/expo/features/pack-templates/hooks/useCreatePackTemplate.ts
+++ b/packages/app/src/pack-templates/hooks/useCreatePackTemplate.ts
@@ -1,8 +1,8 @@
-import { obs } from 'expo-app/lib/store';
+import { obs } from '@packrat/app/lib/store';
+import type { PackTemplateInput, PackTemplateInStore } from '@packrat/app/pack-templates';
import { nanoid } from 'nanoid';
import { useCallback } from 'react';
import { packTemplatesStore } from '../store/packTemplates';
-import type { PackTemplateInput, PackTemplateInStore } from '../types';
export function useCreatePackTemplate() {
const createPackTemplate = useCallback((input: PackTemplateInput) => {
diff --git a/apps/expo/features/pack-templates/hooks/useCreatePackTemplateItem.ts b/packages/app/src/pack-templates/hooks/useCreatePackTemplateItem.ts
similarity index 85%
rename from apps/expo/features/pack-templates/hooks/useCreatePackTemplateItem.ts
rename to packages/app/src/pack-templates/hooks/useCreatePackTemplateItem.ts
index 8e4dd5e963..624ff3e488 100644
--- a/apps/expo/features/pack-templates/hooks/useCreatePackTemplateItem.ts
+++ b/packages/app/src/pack-templates/hooks/useCreatePackTemplateItem.ts
@@ -1,11 +1,11 @@
// useCreatePackTemplateItem.ts
-import { obs } from 'expo-app/lib/store';
+import { obs } from '@packrat/app/lib/store';
+import type { PackTemplateItem, PackTemplateItemInput } from '@packrat/app/pack-templates';
import { nanoid } from 'nanoid';
import { useCallback } from 'react';
import { packTemplateItemsStore } from '../store/packTemplateItems';
import { packTemplatesStore } from '../store/packTemplates';
-import type { PackTemplateItem, PackTemplateItemInput } from '../types';
export function useCreatePackTemplateItem() {
const createPackTemplateItem = useCallback(
diff --git a/apps/expo/features/pack-templates/hooks/useDeletePackTemplate.ts b/packages/app/src/pack-templates/hooks/useDeletePackTemplate.ts
similarity index 85%
rename from apps/expo/features/pack-templates/hooks/useDeletePackTemplate.ts
rename to packages/app/src/pack-templates/hooks/useDeletePackTemplate.ts
index 10c9f1f2ba..7aec95109f 100644
--- a/apps/expo/features/pack-templates/hooks/useDeletePackTemplate.ts
+++ b/packages/app/src/pack-templates/hooks/useDeletePackTemplate.ts
@@ -1,4 +1,4 @@
-import { obs } from 'expo-app/lib/store';
+import { obs } from '@packrat/app/lib/store';
import { useCallback } from 'react';
import { packTemplatesStore } from '../store/packTemplates';
diff --git a/apps/expo/features/pack-templates/hooks/useDeletePackTemplateItem.ts b/packages/app/src/pack-templates/hooks/useDeletePackTemplateItem.ts
similarity index 89%
rename from apps/expo/features/pack-templates/hooks/useDeletePackTemplateItem.ts
rename to packages/app/src/pack-templates/hooks/useDeletePackTemplateItem.ts
index 8c8d4c0b2c..1021463a20 100644
--- a/apps/expo/features/pack-templates/hooks/useDeletePackTemplateItem.ts
+++ b/packages/app/src/pack-templates/hooks/useDeletePackTemplateItem.ts
@@ -1,4 +1,4 @@
-import { obs } from 'expo-app/lib/store';
+import { obs } from '@packrat/app/lib/store';
import { useCallback } from 'react';
import { packTemplateItemsStore } from '../store/packTemplateItems';
diff --git a/apps/expo/features/pack-templates/hooks/useGenerateTemplateFromOnlineContent.ts b/packages/app/src/pack-templates/hooks/useGenerateTemplateFromOnlineContent.ts
similarity index 93%
rename from apps/expo/features/pack-templates/hooks/useGenerateTemplateFromOnlineContent.ts
rename to packages/app/src/pack-templates/hooks/useGenerateTemplateFromOnlineContent.ts
index 4276262235..178e577dfb 100644
--- a/apps/expo/features/pack-templates/hooks/useGenerateTemplateFromOnlineContent.ts
+++ b/packages/app/src/pack-templates/hooks/useGenerateTemplateFromOnlineContent.ts
@@ -1,11 +1,11 @@
+import { apiClient } from '@packrat/app/lib/api/packrat';
+import { obs } from '@packrat/app/lib/store';
+import { isWeightUnit } from '@packrat/app/lib/utils/itemCalculations';
+import type { PackTemplateInStore, PackTemplateItem } from '@packrat/app/pack-templates';
import { isObject } from '@packrat/guards';
import { useMutation } from '@tanstack/react-query';
-import { apiClient } from 'expo-app/lib/api/packrat';
-import { obs } from 'expo-app/lib/store';
-import { isWeightUnit } from 'expo-app/lib/utils/itemCalculations';
import { packTemplateItemsStore } from '../store/packTemplateItems';
import { packTemplatesStore } from '../store/packTemplates';
-import type { PackTemplateInStore, PackTemplateItem } from '../types';
export interface GenerateFromOnlineContentInput {
contentUrl: string;
diff --git a/apps/expo/features/pack-templates/hooks/usePackTemplateItem.ts b/packages/app/src/pack-templates/hooks/usePackTemplateItem.ts
similarity index 100%
rename from apps/expo/features/pack-templates/hooks/usePackTemplateItem.ts
rename to packages/app/src/pack-templates/hooks/usePackTemplateItem.ts
diff --git a/apps/expo/features/pack-templates/hooks/usePackTemplateSummary.ts b/packages/app/src/pack-templates/hooks/usePackTemplateSummary.ts
similarity index 95%
rename from apps/expo/features/pack-templates/hooks/usePackTemplateSummary.ts
rename to packages/app/src/pack-templates/hooks/usePackTemplateSummary.ts
index 56a92edf6a..ee2cf5f7f9 100644
--- a/apps/expo/features/pack-templates/hooks/usePackTemplateSummary.ts
+++ b/packages/app/src/pack-templates/hooks/usePackTemplateSummary.ts
@@ -1,7 +1,7 @@
import { use$ } from '@legendapp/state/react';
-import { convertFromGrams, convertToGrams } from 'expo-app/features/packs/utils';
+import type { WeightUnit } from '@packrat/app/pack-templates';
+import { convertFromGrams, convertToGrams } from '@packrat/app/packs/utils';
import { packTemplateItemsStore } from '../store/packTemplateItems';
-import type { WeightUnit } from '../types';
export type PackTemplateSummary = {
itemCount: number;
diff --git a/apps/expo/features/pack-templates/hooks/usePackTemplates.ts b/packages/app/src/pack-templates/hooks/usePackTemplates.ts
similarity index 100%
rename from apps/expo/features/pack-templates/hooks/usePackTemplates.ts
rename to packages/app/src/pack-templates/hooks/usePackTemplates.ts
diff --git a/apps/expo/features/pack-templates/hooks/usePackTemplatesDetails.ts b/packages/app/src/pack-templates/hooks/usePackTemplatesDetails.ts
similarity index 80%
rename from apps/expo/features/pack-templates/hooks/usePackTemplatesDetails.ts
rename to packages/app/src/pack-templates/hooks/usePackTemplatesDetails.ts
index df62273388..890270d0d4 100644
--- a/apps/expo/features/pack-templates/hooks/usePackTemplatesDetails.ts
+++ b/packages/app/src/pack-templates/hooks/usePackTemplatesDetails.ts
@@ -1,8 +1,8 @@
import { use$ } from '@legendapp/state/react';
-import { obs } from 'expo-app/lib/store';
+import { obs } from '@packrat/app/lib/store';
+import { computePackTemplateWeights } from '@packrat/app/pack-templates';
import { getTemplateItems } from '../store/packTemplateItems';
import { packTemplatesStore } from '../store/packTemplates';
-import { computePackTemplateWeights } from '../utils/computePacktemplateWeight';
// Hook to get a single pack template
export function usePackTemplateDetails(id: string) {
diff --git a/apps/expo/features/pack-templates/hooks/useUpdatePackTemplateItem.ts b/packages/app/src/pack-templates/hooks/useUpdatePackTemplateItem.ts
similarity index 79%
rename from apps/expo/features/pack-templates/hooks/useUpdatePackTemplateItem.ts
rename to packages/app/src/pack-templates/hooks/useUpdatePackTemplateItem.ts
index 993ad63919..bb61ea1a5b 100644
--- a/apps/expo/features/pack-templates/hooks/useUpdatePackTemplateItem.ts
+++ b/packages/app/src/pack-templates/hooks/useUpdatePackTemplateItem.ts
@@ -1,9 +1,9 @@
// useUpdatePackTemplateItem.ts
-import { obs } from 'expo-app/lib/store';
+import { obs } from '@packrat/app/lib/store';
+import type { PackTemplateItem } from '@packrat/app/pack-templates';
import { useCallback } from 'react';
import { packTemplateItemsStore, packTemplatesStore } from '../store';
-import type { PackTemplateItem } from '../types';
export function useUpdatePackTemplateItem() {
const updatePackTemplateItem = useCallback((item: PackTemplateItem) => {
diff --git a/apps/expo/features/pack-templates/hooks/useUpdatePacktemplate.ts b/packages/app/src/pack-templates/hooks/useUpdatePacktemplate.ts
similarity index 76%
rename from apps/expo/features/pack-templates/hooks/useUpdatePacktemplate.ts
rename to packages/app/src/pack-templates/hooks/useUpdatePacktemplate.ts
index 7ac5376c59..062a45789a 100644
--- a/apps/expo/features/pack-templates/hooks/useUpdatePacktemplate.ts
+++ b/packages/app/src/pack-templates/hooks/useUpdatePacktemplate.ts
@@ -1,7 +1,7 @@
-import { obs } from 'expo-app/lib/store';
+import { obs } from '@packrat/app/lib/store';
+import type { PackTemplate } from '@packrat/app/pack-templates';
import { useCallback } from 'react';
import { packTemplatesStore } from '../store/packTemplates';
-import type { PackTemplate } from '../types';
export function useUpdatePackTemplate() {
const update = useCallback((template: PackTemplate) => {
diff --git a/apps/expo/features/pack-templates/hooks/useWritePermissionCheck.ts b/packages/app/src/pack-templates/hooks/useWritePermissionCheck.ts
similarity index 70%
rename from apps/expo/features/pack-templates/hooks/useWritePermissionCheck.ts
rename to packages/app/src/pack-templates/hooks/useWritePermissionCheck.ts
index 1e265ed56c..01620ee06d 100644
--- a/apps/expo/features/pack-templates/hooks/useWritePermissionCheck.ts
+++ b/packages/app/src/pack-templates/hooks/useWritePermissionCheck.ts
@@ -1,5 +1,5 @@
-import { useUser } from 'expo-app/features/auth/hooks/useUser';
-import { obs } from 'expo-app/lib/store';
+import { useUser } from '@packrat/app/auth/hooks/useUser';
+import { obs } from '@packrat/app/lib/store';
import { packTemplatesStore } from '../store';
export function useWritePermissionCheck(id: string) {
diff --git a/packages/app/src/pack-templates/index.ts b/packages/app/src/pack-templates/index.ts
new file mode 100644
index 0000000000..f11efe79cd
--- /dev/null
+++ b/packages/app/src/pack-templates/index.ts
@@ -0,0 +1,9 @@
+export type {
+ PackTemplate,
+ PackTemplateInput,
+ PackTemplateInStore,
+ PackTemplateItem,
+ PackTemplateItemInput,
+ WeightUnit,
+} from './types';
+export { computePackTemplateWeights } from './utils/computePacktemplateWeight';
diff --git a/apps/expo/features/pack-templates/packTemplateListAtoms.ts b/packages/app/src/pack-templates/packTemplateListAtoms.ts
similarity index 74%
rename from apps/expo/features/pack-templates/packTemplateListAtoms.ts
rename to packages/app/src/pack-templates/packTemplateListAtoms.ts
index 293d6d3ce0..f8c9e7a625 100644
--- a/apps/expo/features/pack-templates/packTemplateListAtoms.ts
+++ b/packages/app/src/pack-templates/packTemplateListAtoms.ts
@@ -1,4 +1,4 @@
-import type { PackCategory } from 'expo-app/types';
+import type { PackCategory } from '@packrat/app/types';
import { atom } from 'jotai';
export const activeTemplateFilterAtom = atom('all');
diff --git a/apps/expo/features/pack-templates/screens/CreatePackTemplateItemForm.tsx b/packages/app/src/pack-templates/screens/CreatePackTemplateItemForm.tsx
similarity index 97%
rename from apps/expo/features/pack-templates/screens/CreatePackTemplateItemForm.tsx
rename to packages/app/src/pack-templates/screens/CreatePackTemplateItemForm.tsx
index 615d0d2e98..3829574ce1 100644
--- a/apps/expo/features/pack-templates/screens/CreatePackTemplateItemForm.tsx
+++ b/packages/app/src/pack-templates/screens/CreatePackTemplateItemForm.tsx
@@ -1,15 +1,16 @@
// CreatePackTemplateItemForm.tsx
import { useActionSheet } from '@expo/react-native-action-sheet';
+import { Icon } from '@packrat/app/components/Icon';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
+import ImageCacheManager from '@packrat/app/lib/utils/ImageCacheManager';
+import type { PackTemplateItem } from '@packrat/app/pack-templates';
+import { useImagePicker } from '@packrat/app/packs/hooks/useImagePicker';
+import type { WeightUnit } from '@packrat/app/types';
import { safeIndexOf } from '@packrat/guards';
import { Form, FormItem, FormSection, SegmentedControl, TextField } from '@packrat/ui/nativewindui';
import { useForm } from '@tanstack/react-form';
-import { Icon } from 'expo-app/components/Icon';
-import { useImagePicker } from 'expo-app/features/packs/hooks/useImagePicker';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
-import ImageCacheManager from 'expo-app/lib/utils/ImageCacheManager';
-import type { WeightUnit } from 'expo-app/types';
import { useRouter } from 'expo-router';
import { useMemo, useRef, useState } from 'react';
import { Alert, Image, Pressable, Switch, Text, TouchableOpacity, View } from 'react-native';
@@ -18,7 +19,6 @@ import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { z } from 'zod';
import { useCreatePackTemplateItem } from '../hooks/useCreatePackTemplateItem';
import { useUpdatePackTemplateItem } from '../hooks/useUpdatePackTemplateItem';
-import type { PackTemplateItem } from '../types';
const itemFormSchema = z.object({
name: z.string().min(1, 'Item name is required'),
diff --git a/apps/expo/features/pack-templates/screens/CreatePackTemplateScreen.tsx b/packages/app/src/pack-templates/screens/CreatePackTemplateScreen.tsx
similarity index 100%
rename from apps/expo/features/pack-templates/screens/CreatePackTemplateScreen.tsx
rename to packages/app/src/pack-templates/screens/CreatePackTemplateScreen.tsx
diff --git a/apps/expo/features/pack-templates/screens/EditPackTemplateItemScreen.tsx b/packages/app/src/pack-templates/screens/EditPackTemplateItemScreen.tsx
similarity index 87%
rename from apps/expo/features/pack-templates/screens/EditPackTemplateItemScreen.tsx
rename to packages/app/src/pack-templates/screens/EditPackTemplateItemScreen.tsx
index acc7808389..19de382d76 100644
--- a/apps/expo/features/pack-templates/screens/EditPackTemplateItemScreen.tsx
+++ b/packages/app/src/pack-templates/screens/EditPackTemplateItemScreen.tsx
@@ -1,5 +1,5 @@
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
-import { NotFoundScreen } from 'expo-app/screens/NotFoundScreen';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
+import { NotFoundScreen } from '@packrat/app/screens/NotFoundScreen';
import { useLocalSearchParams } from 'expo-router';
import { usePackTemplateItem } from '../hooks/usePackTemplateItem';
import { CreatePackTemplateItemForm } from './CreatePackTemplateItemForm';
diff --git a/apps/expo/features/pack-templates/screens/EditPackTemplateScreen.tsx b/packages/app/src/pack-templates/screens/EditPackTemplateScreen.tsx
similarity index 83%
rename from apps/expo/features/pack-templates/screens/EditPackTemplateScreen.tsx
rename to packages/app/src/pack-templates/screens/EditPackTemplateScreen.tsx
index 4e2729f291..01edccab96 100644
--- a/apps/expo/features/pack-templates/screens/EditPackTemplateScreen.tsx
+++ b/packages/app/src/pack-templates/screens/EditPackTemplateScreen.tsx
@@ -1,5 +1,5 @@
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
-import { NotFoundScreen } from 'expo-app/screens/NotFoundScreen';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
+import { NotFoundScreen } from '@packrat/app/screens/NotFoundScreen';
import { useLocalSearchParams } from 'expo-router';
import { PackTemplateForm } from '../components/PackTemplateForm';
import { usePackTemplateDetails } from '../hooks/usePackTemplatesDetails';
diff --git a/apps/expo/features/pack-templates/screens/ItemsScanScreen.tsx b/packages/app/src/pack-templates/screens/ItemsScanScreen.tsx
similarity index 94%
rename from apps/expo/features/pack-templates/screens/ItemsScanScreen.tsx
rename to packages/app/src/pack-templates/screens/ItemsScanScreen.tsx
index 2ea880be38..cc19909544 100644
--- a/apps/expo/features/pack-templates/screens/ItemsScanScreen.tsx
+++ b/packages/app/src/pack-templates/screens/ItemsScanScreen.tsx
@@ -1,20 +1,20 @@
import { useActionSheet } from '@expo/react-native-action-sheet';
+import type { CatalogItem, CatalogItemWithPackItemFields } from '@packrat/app/catalog';
+import { ErrorState } from '@packrat/app/components/ErrorState';
+import { Icon } from '@packrat/app/components/Icon';
+import { appAlert } from '@packrat/app/lib/appAlert';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
+import { HorizontalCatalogItemCard } from '@packrat/app/packs/components/HorizontalCatalogItemCard';
+import { useImageDetection } from '@packrat/app/packs/hooks/useImageDetection';
+import { type SelectedImage, useImagePicker } from '@packrat/app/packs/hooks/useImagePicker';
import { assertNonNull } from '@packrat/guards';
import { ActivityIndicator, Button, Text } from '@packrat/ui/nativewindui';
import * as Burnt from 'burnt';
-import { appAlert } from 'expo-app/app/_layout';
-import { ErrorState } from 'expo-app/components/ErrorState';
-import { Icon } from 'expo-app/components/Icon';
-import { HorizontalCatalogItemCard } from 'expo-app/features/packs/components/HorizontalCatalogItemCard';
-import { useImageDetection } from 'expo-app/features/packs/hooks/useImageDetection';
-import { type SelectedImage, useImagePicker } from 'expo-app/features/packs/hooks/useImagePicker';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { Stack, useLocalSearchParams, useRouter } from 'expo-router';
import { useCallback, useEffect, useState } from 'react';
import { Image, ScrollView, View } from 'react-native';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
-import type { CatalogItem, CatalogItemWithPackItemFields } from '../../catalog/types';
import { useBulkAddCatalogItems } from '../hooks';
export function ItemsScanScreen() {
diff --git a/apps/expo/features/pack-templates/screens/PackTemplateDetailScreen.tsx b/packages/app/src/pack-templates/screens/PackTemplateDetailScreen.tsx
similarity index 94%
rename from apps/expo/features/pack-templates/screens/PackTemplateDetailScreen.tsx
rename to packages/app/src/pack-templates/screens/PackTemplateDetailScreen.tsx
index 4dc0b0c52f..d5a01e737b 100644
--- a/apps/expo/features/pack-templates/screens/PackTemplateDetailScreen.tsx
+++ b/packages/app/src/pack-templates/screens/PackTemplateDetailScreen.tsx
@@ -1,9 +1,10 @@
+import { useUser } from '@packrat/app/auth/hooks/useUser';
+import { Chip } from '@packrat/app/components/initial/Chip';
+import { WeightBadge } from '@packrat/app/components/initial/WeightBadge';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
+import type { PackTemplateItem } from '@packrat/app/pack-templates';
+import { NotFoundScreen } from '@packrat/app/screens/NotFoundScreen';
import { Button, Text, useSheetRef } from '@packrat/ui/nativewindui';
-import { Chip } from 'expo-app/components/initial/Chip';
-import { WeightBadge } from 'expo-app/components/initial/WeightBadge';
-import { useUser } from 'expo-app/features/auth/hooks/useUser';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
-import { NotFoundScreen } from 'expo-app/screens/NotFoundScreen';
import { useLocalSearchParams, useRouter } from 'expo-router';
import { useCallback, useState } from 'react';
import { Image, ScrollView, TouchableOpacity, View } from 'react-native';
@@ -12,7 +13,6 @@ import AddPackTemplateItemActions from '../components/AddPackTemplateItemActions
import { AppTemplateBadge } from '../components/AppTemplateBadge';
import { PackTemplateItemCard } from '../components/PackTemplateItemCard';
import { usePackTemplateDetails } from '../hooks';
-import type { PackTemplateItem } from '../types';
export function PackTemplateDetailScreen() {
const router = useRouter();
diff --git a/apps/expo/features/pack-templates/screens/PackTemplateItemDetailScreen.tsx b/packages/app/src/pack-templates/screens/PackTemplateItemDetailScreen.tsx
similarity index 92%
rename from apps/expo/features/pack-templates/screens/PackTemplateItemDetailScreen.tsx
rename to packages/app/src/pack-templates/screens/PackTemplateItemDetailScreen.tsx
index 66178a6594..0f9568af82 100644
--- a/apps/expo/features/pack-templates/screens/PackTemplateItemDetailScreen.tsx
+++ b/packages/app/src/pack-templates/screens/PackTemplateItemDetailScreen.tsx
@@ -1,10 +1,8 @@
-import { assertDefined } from '@packrat/guards';
-import { Button, Text, useColorScheme } from '@packrat/ui/nativewindui';
-import { Icon } from 'expo-app/components/Icon';
-import { Chip } from 'expo-app/components/initial/Chip';
-import { WeightBadge } from 'expo-app/components/initial/WeightBadge';
-import { isAuthed } from 'expo-app/features/auth/store';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
+import { isAuthed } from '@packrat/app/auth/store';
+import { Icon } from '@packrat/app/components/Icon';
+import { Chip } from '@packrat/app/components/initial/Chip';
+import { WeightBadge } from '@packrat/app/components/initial/WeightBadge';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
import {
calculateTotalWeight,
getNotes,
@@ -13,7 +11,9 @@ import {
isConsumable,
isWorn,
shouldShowQuantity,
-} from 'expo-app/lib/utils/itemCalculations';
+} from '@packrat/app/lib/utils/itemCalculations';
+import { assertDefined } from '@packrat/guards';
+import { Button, Text, useColorScheme } from '@packrat/ui/nativewindui';
import { router, useLocalSearchParams } from 'expo-router';
import { ScrollView, View } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
diff --git a/apps/expo/features/pack-templates/screens/PackTemplateListScreen.tsx b/packages/app/src/pack-templates/screens/PackTemplateListScreen.tsx
similarity index 93%
rename from apps/expo/features/pack-templates/screens/PackTemplateListScreen.tsx
rename to packages/app/src/pack-templates/screens/PackTemplateListScreen.tsx
index 46d55919b6..35fef9018b 100644
--- a/apps/expo/features/pack-templates/screens/PackTemplateListScreen.tsx
+++ b/packages/app/src/pack-templates/screens/PackTemplateListScreen.tsx
@@ -1,14 +1,15 @@
import type { BottomSheetModal } from '@gorhom/bottom-sheet';
+import { useAuth } from '@packrat/app/auth/hooks/useAuth';
+import { useUser } from '@packrat/app/auth/hooks/useUser';
+import { Icon } from '@packrat/app/components/Icon';
+import { LargeTitleHeaderSearchContentContainer } from '@packrat/app/components/LargeTitleHeaderSearchContentContainer';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
+import { asNonNullableRef } from '@packrat/app/lib/utils/asNonNullableRef';
+import type { PackTemplate } from '@packrat/app/pack-templates';
+import type { PackCategory } from '@packrat/app/packs';
import type { LargeTitleSearchBarMethods } from '@packrat/ui/nativewindui';
import { LargeTitleHeader, SegmentedControl } from '@packrat/ui/nativewindui';
-import { Icon } from 'expo-app/components/Icon';
-import { LargeTitleHeaderSearchContentContainer } from 'expo-app/components/LargeTitleHeaderSearchContentContainer';
-import { useAuth } from 'expo-app/features/auth/hooks/useAuth';
-import { useUser } from 'expo-app/features/auth/hooks/useUser';
-import type { PackCategory } from 'expo-app/features/packs/types';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
-import { asNonNullableRef } from 'expo-app/lib/utils/asNonNullableRef';
import { useRouter } from 'expo-router';
import { useAtom } from 'jotai';
import { useCallback, useRef, useState } from 'react';
@@ -26,7 +27,6 @@ import { PackTemplateCard } from '../components/PackTemplateCard';
import TemplateCreationOptions from '../components/TemplateCreationOptions';
import { usePackTemplates } from '../hooks';
import { activeTemplateFilterAtom, templateSearchValueAtom } from '../packTemplateListAtoms';
-import type { PackTemplate } from '../types';
type FilterOption = {
label: string;
diff --git a/apps/expo/features/pack-templates/store/index.ts b/packages/app/src/pack-templates/store/index.ts
similarity index 100%
rename from apps/expo/features/pack-templates/store/index.ts
rename to packages/app/src/pack-templates/store/index.ts
diff --git a/apps/expo/features/pack-templates/store/packTemplateItems.ts b/packages/app/src/pack-templates/store/packTemplateItems.ts
similarity index 94%
rename from apps/expo/features/pack-templates/store/packTemplateItems.ts
rename to packages/app/src/pack-templates/store/packTemplateItems.ts
index f942a5e6f4..6b11ec4507 100644
--- a/apps/expo/features/pack-templates/store/packTemplateItems.ts
+++ b/packages/app/src/pack-templates/store/packTemplateItems.ts
@@ -5,10 +5,10 @@ import {
PackTemplateItemSchema,
PackTemplateWithItemsSchema,
} from '@packrat/api/schemas/packTemplates';
-import { isAuthed } from 'expo-app/features/auth/store';
-import { apiClient } from 'expo-app/lib/api/packrat';
-import { persistPlugin } from 'expo-app/lib/persist-plugin';
-import type { PackTemplateItem } from '../types';
+import { isAuthed } from '@packrat/app/auth/store';
+import { apiClient } from '@packrat/app/lib/api/packrat';
+import { persistPlugin } from '@packrat/app/lib/persist-plugin';
+import type { PackTemplateItem } from '@packrat/app/pack-templates';
const listAllPackTemplateItems = async (): Promise => {
const { data, error } = await apiClient['pack-templates'].get();
diff --git a/apps/expo/features/pack-templates/store/packTemplates.ts b/packages/app/src/pack-templates/store/packTemplates.ts
similarity index 93%
rename from apps/expo/features/pack-templates/store/packTemplates.ts
rename to packages/app/src/pack-templates/store/packTemplates.ts
index 8e6afbf641..30bc9543aa 100644
--- a/apps/expo/features/pack-templates/store/packTemplates.ts
+++ b/packages/app/src/pack-templates/store/packTemplates.ts
@@ -5,10 +5,10 @@ import {
PackTemplateSchema,
PackTemplateWithItemsSchema,
} from '@packrat/api/schemas/packTemplates';
-import { isAuthed } from 'expo-app/features/auth/store';
-import { apiClient } from 'expo-app/lib/api/packrat';
-import { persistPlugin } from 'expo-app/lib/persist-plugin';
-import type { PackTemplate, PackTemplateInStore } from '../types';
+import { isAuthed } from '@packrat/app/auth/store';
+import { apiClient } from '@packrat/app/lib/api/packrat';
+import { persistPlugin } from '@packrat/app/lib/persist-plugin';
+import type { PackTemplate, PackTemplateInStore } from '@packrat/app/pack-templates';
const listPackTemplates = async (): Promise => {
const { data, error } = await apiClient['pack-templates'].get();
diff --git a/apps/expo/features/pack-templates/types.ts b/packages/app/src/pack-templates/types.ts
similarity index 94%
rename from apps/expo/features/pack-templates/types.ts
rename to packages/app/src/pack-templates/types.ts
index 6e7f7bf707..45934ded65 100644
--- a/apps/expo/features/pack-templates/types.ts
+++ b/packages/app/src/pack-templates/types.ts
@@ -1,4 +1,6 @@
-export type WeightUnit = 'g' | 'kg' | 'oz' | 'lb';
+import type { WeightUnit } from '@packrat/api/types';
+
+export type { WeightUnit };
export interface PackWeightHistoryEntry {
id: string;
diff --git a/apps/expo/features/pack-templates/utils/__tests__/computePacktemplateWeight.test.ts b/packages/app/src/pack-templates/utils/__tests__/computePacktemplateWeight.test.ts
similarity index 85%
rename from apps/expo/features/pack-templates/utils/__tests__/computePacktemplateWeight.test.ts
rename to packages/app/src/pack-templates/utils/__tests__/computePacktemplateWeight.test.ts
index c9f4f72480..1ed032413e 100644
--- a/apps/expo/features/pack-templates/utils/__tests__/computePacktemplateWeight.test.ts
+++ b/packages/app/src/pack-templates/utils/__tests__/computePacktemplateWeight.test.ts
@@ -1,20 +1,6 @@
-import { describe, expect, it, vi } from 'vitest';
-import type { PackTemplate, PackTemplateItem } from '../../types';
-
-// The SUT imports convertFromGrams/convertToGrams from the packs/utils barrel,
-// which also re-exports uploadImage (expo-file-system) and computeCategories
-// (expo-app/features/auth/store). Replace the barrel with the real pure
-// conversion helpers so the test stays in a Node environment.
-vi.mock('expo-app/features/packs/utils', async () => {
- const fromGrams = await import('expo-app/features/packs/utils/convertFromGrams');
- const toGrams = await import('expo-app/features/packs/utils/convertToGrams');
- return {
- convertFromGrams: fromGrams.convertFromGrams,
- convertToGrams: toGrams.convertToGrams,
- };
-});
-
-import { computePackTemplateWeights } from '../computePacktemplateWeight';
+import type { PackTemplate, PackTemplateItem } from '@packrat/app/pack-templates';
+import { computePackTemplateWeights } from '@packrat/app/pack-templates';
+import { describe, expect, it } from 'vitest';
type TemplateInput = Omit;
diff --git a/apps/expo/features/pack-templates/utils/computePacktemplateWeight.ts b/packages/app/src/pack-templates/utils/computePacktemplateWeight.ts
similarity index 92%
rename from apps/expo/features/pack-templates/utils/computePacktemplateWeight.ts
rename to packages/app/src/pack-templates/utils/computePacktemplateWeight.ts
index 4661a60e71..88e3e09b16 100644
--- a/apps/expo/features/pack-templates/utils/computePacktemplateWeight.ts
+++ b/packages/app/src/pack-templates/utils/computePacktemplateWeight.ts
@@ -1,4 +1,4 @@
-import { convertFromGrams, convertToGrams } from 'expo-app/features/packs/utils';
+import { convertFromGrams, convertToGrams } from '@packrat/app/packs';
import type { PackTemplate, WeightUnit } from '../types';
export const computePackTemplateWeights = (
diff --git a/apps/expo/features/pack-templates/utils/getPackTemplateDetailOptions.tsx b/packages/app/src/pack-templates/utils/getPackTemplateDetailOptions.tsx
similarity index 93%
rename from apps/expo/features/pack-templates/utils/getPackTemplateDetailOptions.tsx
rename to packages/app/src/pack-templates/utils/getPackTemplateDetailOptions.tsx
index 2fd3de7770..86e65b6a79 100644
--- a/apps/expo/features/pack-templates/utils/getPackTemplateDetailOptions.tsx
+++ b/packages/app/src/pack-templates/utils/getPackTemplateDetailOptions.tsx
@@ -1,7 +1,7 @@
+import { Icon } from '@packrat/app/components/Icon';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
+import { t } from '@packrat/app/lib/i18n';
import { Alert, Button, useColorScheme, useSheetRef } from '@packrat/ui/nativewindui';
-import { Icon } from 'expo-app/components/Icon';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
-import { t } from 'expo-app/lib/i18n';
import { useRouter } from 'expo-router';
import { View } from 'react-native';
import AddPackTemplateItemActions from '../components/AddPackTemplateItemActions';
diff --git a/apps/expo/features/pack-templates/utils/getPackTemplateItemDetailOptions.tsx b/packages/app/src/pack-templates/utils/getPackTemplateItemDetailOptions.tsx
similarity index 92%
rename from apps/expo/features/pack-templates/utils/getPackTemplateItemDetailOptions.tsx
rename to packages/app/src/pack-templates/utils/getPackTemplateItemDetailOptions.tsx
index 6fba2ec10c..362ad590d4 100644
--- a/apps/expo/features/pack-templates/utils/getPackTemplateItemDetailOptions.tsx
+++ b/packages/app/src/pack-templates/utils/getPackTemplateItemDetailOptions.tsx
@@ -1,8 +1,8 @@
+import { Icon } from '@packrat/app/components/Icon';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
+import { t } from '@packrat/app/lib/i18n';
import { assertDefined } from '@packrat/guards';
import { Alert, Button, useColorScheme } from '@packrat/ui/nativewindui';
-import { Icon } from 'expo-app/components/Icon';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
-import { t } from 'expo-app/lib/i18n';
import { useRouter } from 'expo-router';
import { View } from 'react-native';
import { useDeletePackTemplateItem, usePackTemplateItem } from '../hooks';
diff --git a/apps/expo/features/packs/components/ActivityPicker.tsx b/packages/app/src/packs/components/ActivityPicker.tsx
similarity index 95%
rename from apps/expo/features/packs/components/ActivityPicker.tsx
rename to packages/app/src/packs/components/ActivityPicker.tsx
index 622b2874b3..23bed4f0d3 100644
--- a/apps/expo/features/packs/components/ActivityPicker.tsx
+++ b/packages/app/src/packs/components/ActivityPicker.tsx
@@ -1,10 +1,10 @@
+import { Icon } from '@packrat/app/components/Icon';
+import { cn } from '@packrat/app/lib/cn';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import type { PackCategory } from '@packrat/app/packs';
import { Button, Text } from '@packrat/ui/nativewindui';
-import { Icon } from 'expo-app/components/Icon';
-import { cn } from 'expo-app/lib/cn';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
import { useState } from 'react';
import { Modal, Pressable, ScrollView, TouchableOpacity, View } from 'react-native';
-import type { PackCategory } from '../types';
type ActivityPickerProps = {
open: boolean;
diff --git a/apps/expo/features/packs/components/AddPackItemActions.tsx b/packages/app/src/packs/components/AddPackItemActions.tsx
similarity index 93%
rename from apps/expo/features/packs/components/AddPackItemActions.tsx
rename to packages/app/src/packs/components/AddPackItemActions.tsx
index c3039eb99d..d5f70c19d8 100644
--- a/apps/expo/features/packs/components/AddPackItemActions.tsx
+++ b/packages/app/src/packs/components/AddPackItemActions.tsx
@@ -1,15 +1,15 @@
import { useActionSheet } from '@expo/react-native-action-sheet';
import type { BottomSheetModal } from '@gorhom/bottom-sheet';
import { BottomSheetView } from '@gorhom/bottom-sheet';
+import { isAuthed } from '@packrat/app/auth/store';
+import type { CatalogItem } from '@packrat/app/catalog';
+import { CatalogBrowserModal } from '@packrat/app/catalog/components';
+import { useRecentlyUsedCatalogItems } from '@packrat/app/catalog/hooks/useRecentlyUsedCatalogItems';
+import { Icon } from '@packrat/app/components/Icon';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
+import { testIds } from '@packrat/app/lib/testIds';
import { isFunction, nullToUndefined } from '@packrat/guards';
import { Sheet, Text, useColorScheme } from '@packrat/ui/nativewindui';
-import { Icon } from 'expo-app/components/Icon';
-import { isAuthed } from 'expo-app/features/auth/store';
-import { CatalogBrowserModal } from 'expo-app/features/catalog/components';
-import { useRecentlyUsedCatalogItems } from 'expo-app/features/catalog/hooks/useRecentlyUsedCatalogItems';
-import type { CatalogItem } from 'expo-app/features/catalog/types';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
-import { testIds } from 'expo-app/lib/testIds';
import { router } from 'expo-router';
import React from 'react';
import { Alert, TouchableOpacity, View } from 'react-native';
diff --git a/apps/expo/features/packs/components/CachedImage.tsx b/packages/app/src/packs/components/CachedImage.tsx
similarity index 96%
rename from apps/expo/features/packs/components/CachedImage.tsx
rename to packages/app/src/packs/components/CachedImage.tsx
index 5d12cf739a..80396f97aa 100644
--- a/apps/expo/features/packs/components/CachedImage.tsx
+++ b/packages/app/src/packs/components/CachedImage.tsx
@@ -1,4 +1,4 @@
-import ImageCacheManager from 'expo-app/lib/utils/ImageCacheManager';
+import ImageCacheManager from '@packrat/app/lib/utils/ImageCacheManager';
import type React from 'react';
import { useEffect, useState } from 'react';
import { ActivityIndicator, Image, type ImageProps, View } from 'react-native';
diff --git a/apps/expo/features/packs/components/CurrentPackTile.tsx b/packages/app/src/packs/components/CurrentPackTile.tsx
similarity index 86%
rename from apps/expo/features/packs/components/CurrentPackTile.tsx
rename to packages/app/src/packs/components/CurrentPackTile.tsx
index 6895e65342..645ced6075 100644
--- a/apps/expo/features/packs/components/CurrentPackTile.tsx
+++ b/packages/app/src/packs/components/CurrentPackTile.tsx
@@ -1,12 +1,12 @@
+import { Icon } from '@packrat/app/components/Icon';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
import { Avatar, AvatarFallback, AvatarImage, ListItem, Text } from '@packrat/ui/nativewindui';
-import { Icon } from 'expo-app/components/Icon';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { useRouter } from 'expo-router';
import { Platform, View } from 'react-native';
import { useCurrentPack } from '../hooks';
-const _LOGO_SOURCE = require('expo-app/assets/packrat-app-icon-gradient.png');
+const _LOGO_SOURCE = require('@packrat/app/assets/packrat-app-icon-gradient.png');
export function CurrentPackTile() {
const { t } = useTranslation();
diff --git a/apps/expo/features/packs/components/GapAnalysisModal.tsx b/packages/app/src/packs/components/GapAnalysisModal.tsx
similarity index 94%
rename from apps/expo/features/packs/components/GapAnalysisModal.tsx
rename to packages/app/src/packs/components/GapAnalysisModal.tsx
index 5509b8de36..4ca04f6dac 100644
--- a/apps/expo/features/packs/components/GapAnalysisModal.tsx
+++ b/packages/app/src/packs/components/GapAnalysisModal.tsx
@@ -1,10 +1,10 @@
+import { Icon } from '@packrat/app/components/Icon';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
+import type { Pack, PackCategory } from '@packrat/app/packs';
import { ActivityIndicator, Button, cn, Text } from '@packrat/ui/nativewindui';
-import { Icon } from 'expo-app/components/Icon';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { Modal, ScrollView, TouchableOpacity, View } from 'react-native';
import type { GapAnalysisResponse } from '../hooks/usePackGapAnalysis';
-import type { Pack, PackCategory } from '../types';
import { GapSuggestion } from './GapSuggestion';
interface GapAnalysisModalProps {
diff --git a/apps/expo/features/packs/components/GapItemCatalogSuggestions.tsx b/packages/app/src/packs/components/GapItemCatalogSuggestions.tsx
similarity index 96%
rename from apps/expo/features/packs/components/GapItemCatalogSuggestions.tsx
rename to packages/app/src/packs/components/GapItemCatalogSuggestions.tsx
index 8f629b8b28..4544987dc8 100644
--- a/apps/expo/features/packs/components/GapItemCatalogSuggestions.tsx
+++ b/packages/app/src/packs/components/GapItemCatalogSuggestions.tsx
@@ -1,8 +1,8 @@
+import type { CatalogItem } from '@packrat/app/catalog';
+import { Icon } from '@packrat/app/components/Icon';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
import { assertDefined } from '@packrat/guards';
import { ActivityIndicator, Button, cn, Text } from '@packrat/ui/nativewindui';
-import { Icon } from 'expo-app/components/Icon';
-import type { CatalogItem } from 'expo-app/features/catalog/types';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
import { useState } from 'react';
import { Modal, ScrollView, TouchableOpacity, View } from 'react-native';
import type { GapAnalysisItem } from '../hooks/usePackGapAnalysis';
diff --git a/apps/expo/features/packs/components/GapSuggestion.tsx b/packages/app/src/packs/components/GapSuggestion.tsx
similarity index 94%
rename from apps/expo/features/packs/components/GapSuggestion.tsx
rename to packages/app/src/packs/components/GapSuggestion.tsx
index 21dd623988..7b5a363e46 100644
--- a/apps/expo/features/packs/components/GapSuggestion.tsx
+++ b/packages/app/src/packs/components/GapSuggestion.tsx
@@ -1,8 +1,8 @@
+import type { CatalogItem } from '@packrat/app/catalog';
+import { useVectorSearch } from '@packrat/app/catalog/hooks/useVectorSearch';
+import { Icon } from '@packrat/app/components/Icon';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
import { Button, Text } from '@packrat/ui/nativewindui';
-import { Icon } from 'expo-app/components/Icon';
-import { useVectorSearch } from 'expo-app/features/catalog/hooks/useVectorSearch';
-import type { CatalogItem } from 'expo-app/features/catalog/types';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
import { useState } from 'react';
import { View } from 'react-native';
import type { GapAnalysisItem } from '../hooks';
diff --git a/apps/expo/features/packs/components/GearInventoryTile.tsx b/packages/app/src/packs/components/GearInventoryTile.tsx
similarity index 90%
rename from apps/expo/features/packs/components/GearInventoryTile.tsx
rename to packages/app/src/packs/components/GearInventoryTile.tsx
index 2efede9639..debdf585a1 100644
--- a/apps/expo/features/packs/components/GearInventoryTile.tsx
+++ b/packages/app/src/packs/components/GearInventoryTile.tsx
@@ -1,8 +1,8 @@
+import { Icon } from '@packrat/app/components/Icon';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
import type { AlertMethods } from '@packrat/ui/nativewindui';
import { Alert, ListItem, Text } from '@packrat/ui/nativewindui';
-import { Icon } from 'expo-app/components/Icon';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { useRouter } from 'expo-router';
import { useRef } from 'react';
import { Platform, View } from 'react-native';
diff --git a/apps/expo/features/packs/components/HorizontalCatalogItemCard.tsx b/packages/app/src/packs/components/HorizontalCatalogItemCard.tsx
similarity index 92%
rename from apps/expo/features/packs/components/HorizontalCatalogItemCard.tsx
rename to packages/app/src/packs/components/HorizontalCatalogItemCard.tsx
index 247f26e154..c4047b246f 100644
--- a/apps/expo/features/packs/components/HorizontalCatalogItemCard.tsx
+++ b/packages/app/src/packs/components/HorizontalCatalogItemCard.tsx
@@ -1,8 +1,8 @@
+import type { CatalogItem } from '@packrat/app/catalog';
+import { CatalogItemImage } from '@packrat/app/catalog/components/CatalogItemImage';
+import { Icon } from '@packrat/app/components/Icon';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
import { Text } from '@packrat/ui/nativewindui';
-import { Icon } from 'expo-app/components/Icon';
-import { CatalogItemImage } from 'expo-app/features/catalog/components/CatalogItemImage';
-import type { CatalogItem } from 'expo-app/features/catalog/types';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
import { TouchableWithoutFeedback, View } from 'react-native';
type HorizontalCatalogItemCardProps = {
diff --git a/apps/expo/features/packs/components/PackCard.tsx b/packages/app/src/packs/components/PackCard.tsx
similarity index 96%
rename from apps/expo/features/packs/components/PackCard.tsx
rename to packages/app/src/packs/components/PackCard.tsx
index 449b1954e7..aaf46237af 100644
--- a/apps/expo/features/packs/components/PackCard.tsx
+++ b/packages/app/src/packs/components/PackCard.tsx
@@ -1,15 +1,15 @@
import { useActionSheet } from '@expo/react-native-action-sheet';
+import { Icon } from '@packrat/app/components/Icon';
+import { WeightBadge } from '@packrat/app/components/initial/WeightBadge';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import type { Pack, PackInStore } from '@packrat/app/packs';
import { isArray } from '@packrat/guards';
import { Button, Text } from '@packrat/ui/nativewindui';
-import { Icon } from 'expo-app/components/Icon';
-import { WeightBadge } from 'expo-app/components/initial/WeightBadge';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
import { router } from 'expo-router';
import { ActivityIndicator, Alert, Image, Pressable, View } from 'react-native';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { useDeletePack, useDuplicatePack, usePackDetailsFromStore } from '../hooks';
import { usePackOwnershipCheck } from '../hooks/usePackOwnershipCheck';
-import type { Pack, PackInStore } from '../types';
type PackCardProps = {
pack: Pack | PackInStore;
diff --git a/apps/expo/features/packs/components/PackCategoriesTile.tsx b/packages/app/src/packs/components/PackCategoriesTile.tsx
similarity index 91%
rename from apps/expo/features/packs/components/PackCategoriesTile.tsx
rename to packages/app/src/packs/components/PackCategoriesTile.tsx
index e91096206c..42435ed1f1 100644
--- a/apps/expo/features/packs/components/PackCategoriesTile.tsx
+++ b/packages/app/src/packs/components/PackCategoriesTile.tsx
@@ -1,8 +1,8 @@
+import { Icon } from '@packrat/app/components/Icon';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
import type { AlertMethods } from '@packrat/ui/nativewindui';
import { Alert, ListItem, Text } from '@packrat/ui/nativewindui';
-import { Icon } from 'expo-app/components/Icon';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { useRouter } from 'expo-router';
import { useRef } from 'react';
import { Platform, View } from 'react-native';
diff --git a/apps/expo/features/packs/components/PackForm.tsx b/packages/app/src/packs/components/PackForm.tsx
similarity index 94%
rename from apps/expo/features/packs/components/PackForm.tsx
rename to packages/app/src/packs/components/PackForm.tsx
index baea33172e..c298aaaa25 100644
--- a/apps/expo/features/packs/components/PackForm.tsx
+++ b/packages/app/src/packs/components/PackForm.tsx
@@ -1,4 +1,12 @@
import { PackCategorySchema } from '@packrat/api/types';
+import { Icon } from '@packrat/app/components/Icon';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
+import { testIds } from '@packrat/app/lib/testIds';
+import { useCreatePackFromTemplate } from '@packrat/app/pack-templates/hooks/useCreatePackFromTemplate';
+import { getTemplateItems, packTemplatesStore } from '@packrat/app/pack-templates/store';
+import type { Pack } from '@packrat/app/packs';
+import { TemplateItemsSection } from '@packrat/app/packs/components/TemplateItemsSection';
import { fromZod } from '@packrat/guards';
import {
Button,
@@ -11,13 +19,6 @@ import {
} from '@packrat/ui/nativewindui';
import { useForm } from '@tanstack/react-form';
import * as Burnt from 'burnt';
-import { Icon } from 'expo-app/components/Icon';
-import { useCreatePackFromTemplate } from 'expo-app/features/pack-templates';
-import { getTemplateItems, packTemplatesStore } from 'expo-app/features/pack-templates/store';
-import { TemplateItemsSection } from 'expo-app/features/packs/components/TemplateItemsSection';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
-import { testIds } from 'expo-app/lib/testIds';
import { Stack, useLocalSearchParams, useRouter } from 'expo-router';
import { useEffect, useRef, useState } from 'react';
import {
@@ -31,7 +32,6 @@ import {
} from 'react-native';
import { z } from 'zod';
import { useCreatePack, useUpdatePack } from '../hooks';
-import type { Pack } from '../types';
// Define Zod schema
const packFormSchema = z.object({
diff --git a/apps/expo/features/packs/components/PackItemCard.tsx b/packages/app/src/packs/components/PackItemCard.tsx
similarity index 95%
rename from apps/expo/features/packs/components/PackItemCard.tsx
rename to packages/app/src/packs/components/PackItemCard.tsx
index 529e45e1bd..574e773cd5 100644
--- a/apps/expo/features/packs/components/PackItemCard.tsx
+++ b/packages/app/src/packs/components/PackItemCard.tsx
@@ -1,10 +1,11 @@
import { useActionSheet } from '@expo/react-native-action-sheet';
+import { Icon } from '@packrat/app/components/Icon';
+import { cn } from '@packrat/app/lib/cn';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { testIds } from '@packrat/app/lib/testIds';
+import type { PackItem } from '@packrat/app/packs';
import { assertDefined } from '@packrat/guards';
import { Text } from '@packrat/ui/nativewindui';
-import { Icon } from 'expo-app/components/Icon';
-import { cn } from 'expo-app/lib/cn';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { testIds } from 'expo-app/lib/testIds';
import { useRouter } from 'expo-router';
import { Alert, Pressable, TouchableWithoutFeedback, View } from 'react-native';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
@@ -13,7 +14,6 @@ import {
usePackItemDetailsFromStore,
usePackItemOwnershipCheck,
} from '../hooks';
-import type { PackItem } from '../types';
import { PackItemImage } from './PackItemImage';
type Base = {
diff --git a/apps/expo/features/packs/components/PackItemImage.tsx b/packages/app/src/packs/components/PackItemImage.tsx
similarity index 82%
rename from apps/expo/features/packs/components/PackItemImage.tsx
rename to packages/app/src/packs/components/PackItemImage.tsx
index 4396f09783..dd480acee5 100644
--- a/apps/expo/features/packs/components/PackItemImage.tsx
+++ b/packages/app/src/packs/components/PackItemImage.tsx
@@ -1,11 +1,11 @@
+import { CatalogItemImage } from '@packrat/app/catalog/components/CatalogItemImage';
+import { Icon } from '@packrat/app/components/Icon';
+import { buildImageUrl } from '@packrat/app/lib/utils/buildImageUrl';
+import type { PackItem } from '@packrat/app/packs';
import { isRemoteUrl } from '@packrat/guards';
import { useColorScheme } from '@packrat/ui/nativewindui';
-import { Icon } from 'expo-app/components/Icon';
-import { CatalogItemImage } from 'expo-app/features/catalog/components/CatalogItemImage';
-import { buildImageUrl } from 'expo-app/lib/utils/buildImageUrl';
import { Image, type ImageProps, View } from 'react-native';
import { usePackItemOwnershipCheck } from '../hooks';
-import type { PackItem } from '../types';
import { CachedImage } from './CachedImage';
interface PackItemImageProps extends Omit {
diff --git a/apps/expo/features/packs/components/PackStatsTile.tsx b/packages/app/src/packs/components/PackStatsTile.tsx
similarity index 90%
rename from apps/expo/features/packs/components/PackStatsTile.tsx
rename to packages/app/src/packs/components/PackStatsTile.tsx
index 72259eeebe..79ca1068f5 100644
--- a/apps/expo/features/packs/components/PackStatsTile.tsx
+++ b/packages/app/src/packs/components/PackStatsTile.tsx
@@ -1,8 +1,8 @@
+import { Icon } from '@packrat/app/components/Icon';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
import type { AlertMethods } from '@packrat/ui/nativewindui';
import { Alert, ListItem } from '@packrat/ui/nativewindui';
-import { Icon } from 'expo-app/components/Icon';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { type Href, useRouter } from 'expo-router';
import { useRef } from 'react';
import { Platform, View } from 'react-native';
diff --git a/apps/expo/features/packs/components/RecentPacksTile.tsx b/packages/app/src/packs/components/RecentPacksTile.tsx
similarity index 89%
rename from apps/expo/features/packs/components/RecentPacksTile.tsx
rename to packages/app/src/packs/components/RecentPacksTile.tsx
index 8958356406..cd585218de 100644
--- a/apps/expo/features/packs/components/RecentPacksTile.tsx
+++ b/packages/app/src/packs/components/RecentPacksTile.tsx
@@ -1,8 +1,8 @@
+import { Icon } from '@packrat/app/components/Icon';
+import { cn } from '@packrat/app/lib/cn';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
import { Avatar, AvatarFallback, AvatarImage, ListItem, Text } from '@packrat/ui/nativewindui';
-import { Icon } from 'expo-app/components/Icon';
-import { cn } from 'expo-app/lib/cn';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { useRouter } from 'expo-router';
import { Platform, View } from 'react-native';
import { useRecentPacks } from '../hooks/useRecentPacks';
diff --git a/apps/expo/features/packs/components/SearchResults.tsx b/packages/app/src/packs/components/SearchResults.tsx
similarity index 93%
rename from apps/expo/features/packs/components/SearchResults.tsx
rename to packages/app/src/packs/components/SearchResults.tsx
index 7ce0f63039..45f51efa69 100644
--- a/apps/expo/features/packs/components/SearchResults.tsx
+++ b/packages/app/src/packs/components/SearchResults.tsx
@@ -1,6 +1,6 @@
-import { Icon } from 'expo-app/components/Icon';
+import { Icon } from '@packrat/app/components/Icon';
+import type { Pack } from '@packrat/app/packs';
import { FlatList, Pressable, Text, View } from 'react-native';
-import type { Pack } from '../types';
type SearchResultsProps = {
results: Omit[];
diff --git a/apps/expo/features/packs/components/SeasonSuggestionsTile.tsx b/packages/app/src/packs/components/SeasonSuggestionsTile.tsx
similarity index 89%
rename from apps/expo/features/packs/components/SeasonSuggestionsTile.tsx
rename to packages/app/src/packs/components/SeasonSuggestionsTile.tsx
index 832503a11a..df7075e078 100644
--- a/apps/expo/features/packs/components/SeasonSuggestionsTile.tsx
+++ b/packages/app/src/packs/components/SeasonSuggestionsTile.tsx
@@ -1,7 +1,7 @@
+import { Icon } from '@packrat/app/components/Icon';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
import { ListItem, Text } from '@packrat/ui/nativewindui';
-import { Icon } from 'expo-app/components/Icon';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { useRouter } from 'expo-router';
import { Platform, View } from 'react-native';
import { useHasMinimumInventory } from '../hooks/useHasMinimumInventory';
diff --git a/apps/expo/features/packs/components/SharedPacksTile.tsx b/packages/app/src/packs/components/SharedPacksTile.tsx
similarity index 86%
rename from apps/expo/features/packs/components/SharedPacksTile.tsx
rename to packages/app/src/packs/components/SharedPacksTile.tsx
index 4151c10e36..8b8cd933b1 100644
--- a/apps/expo/features/packs/components/SharedPacksTile.tsx
+++ b/packages/app/src/packs/components/SharedPacksTile.tsx
@@ -1,7 +1,7 @@
+import { Icon } from '@packrat/app/components/Icon';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
import { ListItem } from '@packrat/ui/nativewindui';
-import { Icon } from 'expo-app/components/Icon';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { useRouter } from 'expo-router';
import { Platform, View } from 'react-native';
diff --git a/apps/expo/features/packs/components/ShoppingListTile.tsx b/packages/app/src/packs/components/ShoppingListTile.tsx
similarity index 87%
rename from apps/expo/features/packs/components/ShoppingListTile.tsx
rename to packages/app/src/packs/components/ShoppingListTile.tsx
index c875b2059e..d77f3208cb 100644
--- a/apps/expo/features/packs/components/ShoppingListTile.tsx
+++ b/packages/app/src/packs/components/ShoppingListTile.tsx
@@ -1,7 +1,7 @@
+import { Icon } from '@packrat/app/components/Icon';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
import { ListItem, Text } from '@packrat/ui/nativewindui';
-import { Icon } from 'expo-app/components/Icon';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { useRouter } from 'expo-router';
import { Platform, View } from 'react-native';
diff --git a/apps/expo/features/packs/components/SimilarItemsForPackItem.tsx b/packages/app/src/packs/components/SimilarItemsForPackItem.tsx
similarity index 93%
rename from apps/expo/features/packs/components/SimilarItemsForPackItem.tsx
rename to packages/app/src/packs/components/SimilarItemsForPackItem.tsx
index 0d4d25aa93..fc6eeadd6a 100644
--- a/apps/expo/features/packs/components/SimilarItemsForPackItem.tsx
+++ b/packages/app/src/packs/components/SimilarItemsForPackItem.tsx
@@ -1,7 +1,7 @@
+import { CatalogItemImage } from '@packrat/app/catalog/components/CatalogItemImage';
+import { type SimilarItem, useSimilarPackItems } from '@packrat/app/catalog/hooks';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
import { Text } from '@packrat/ui/nativewindui';
-import { CatalogItemImage } from 'expo-app/features/catalog/components/CatalogItemImage';
-import { type SimilarItem, useSimilarPackItems } from 'expo-app/features/catalog/hooks';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { useRouter } from 'expo-router';
import type React from 'react';
import { FlatList, Pressable, View } from 'react-native';
diff --git a/apps/expo/features/packs/components/SyncBanner.tsx b/packages/app/src/packs/components/SyncBanner.tsx
similarity index 90%
rename from apps/expo/features/packs/components/SyncBanner.tsx
rename to packages/app/src/packs/components/SyncBanner.tsx
index ed779257cf..fd9ad244c1 100644
--- a/apps/expo/features/packs/components/SyncBanner.tsx
+++ b/packages/app/src/packs/components/SyncBanner.tsx
@@ -1,5 +1,5 @@
-import { Icon } from 'expo-app/components/Icon';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
+import { Icon } from '@packrat/app/components/Icon';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
import { usePathname, useRouter } from 'expo-router';
import { Pressable, Text, View } from 'react-native';
diff --git a/apps/expo/features/packs/components/TemplateItemsSection.tsx b/packages/app/src/packs/components/TemplateItemsSection.tsx
similarity index 91%
rename from apps/expo/features/packs/components/TemplateItemsSection.tsx
rename to packages/app/src/packs/components/TemplateItemsSection.tsx
index 41823d6e60..319b518e4b 100644
--- a/apps/expo/features/packs/components/TemplateItemsSection.tsx
+++ b/packages/app/src/packs/components/TemplateItemsSection.tsx
@@ -1,9 +1,9 @@
-import { Icon } from 'expo-app/components/Icon';
-import { cn } from 'expo-app/lib/cn';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
-import { buildPackTemplateItemImageUrl } from 'expo-app/lib/utils/buildPackTemplateItemImageUrl';
-import type { WeightUnit } from 'expo-app/types';
+import { Icon } from '@packrat/app/components/Icon';
+import { cn } from '@packrat/app/lib/cn';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
+import { buildPackTemplateItemImageUrl } from '@packrat/app/lib/utils/buildPackTemplateItemImageUrl';
+import type { WeightUnit } from '@packrat/app/types';
import { Image, ScrollView, Text, View } from 'react-native';
export interface PackTemplateItem {
diff --git a/apps/expo/features/packs/components/WeightAnalysisTile.tsx b/packages/app/src/packs/components/WeightAnalysisTile.tsx
similarity index 91%
rename from apps/expo/features/packs/components/WeightAnalysisTile.tsx
rename to packages/app/src/packs/components/WeightAnalysisTile.tsx
index d1060dee14..fc2bc94ac3 100644
--- a/apps/expo/features/packs/components/WeightAnalysisTile.tsx
+++ b/packages/app/src/packs/components/WeightAnalysisTile.tsx
@@ -1,8 +1,8 @@
+import { Icon } from '@packrat/app/components/Icon';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
import type { AlertMethods } from '@packrat/ui/nativewindui';
import { Alert, ListItem, Text } from '@packrat/ui/nativewindui';
-import { Icon } from 'expo-app/components/Icon';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { type Href, useRouter } from 'expo-router';
import { useRef } from 'react';
import { Platform, View } from 'react-native';
diff --git a/apps/expo/features/packs/hooks/index.ts b/packages/app/src/packs/hooks/index.ts
similarity index 100%
rename from apps/expo/features/packs/hooks/index.ts
rename to packages/app/src/packs/hooks/index.ts
diff --git a/apps/expo/features/packs/hooks/useAddCatalogItem.ts b/packages/app/src/packs/hooks/useAddCatalogItem.ts
similarity index 87%
rename from apps/expo/features/packs/hooks/useAddCatalogItem.ts
rename to packages/app/src/packs/hooks/useAddCatalogItem.ts
index d5a27dfbf4..677fd813a6 100644
--- a/apps/expo/features/packs/hooks/useAddCatalogItem.ts
+++ b/packages/app/src/packs/hooks/useAddCatalogItem.ts
@@ -1,10 +1,10 @@
import { WeightUnitSchema } from '@packrat/api/types';
+import type { CatalogItem } from '@packrat/app/catalog';
+import { cacheCatalogItemImage } from '@packrat/app/catalog/lib/cacheCatalogItemImage';
+import type { PackItem } from '@packrat/app/packs';
import { fromZod } from '@packrat/guards';
import * as Burnt from 'burnt';
-import { cacheCatalogItemImage } from 'expo-app/features/catalog/lib/cacheCatalogItemImage';
-import type { CatalogItem } from 'expo-app/features/catalog/types';
import { useState } from 'react';
-import type { PackItem } from '../types';
import { useCreatePackItem } from './useCreatePackItem';
export function useAddCatalogItem() {
diff --git a/apps/expo/features/packs/hooks/useAllPacks.ts b/packages/app/src/packs/hooks/useAllPacks.ts
similarity index 79%
rename from apps/expo/features/packs/hooks/useAllPacks.ts
rename to packages/app/src/packs/hooks/useAllPacks.ts
index d3431230a5..5be1d532c2 100644
--- a/apps/expo/features/packs/hooks/useAllPacks.ts
+++ b/packages/app/src/packs/hooks/useAllPacks.ts
@@ -1,6 +1,6 @@
+import { apiClient } from '@packrat/app/lib/api/packrat';
+import { useAuthenticatedQueryToolkit } from '@packrat/app/lib/hooks/useAuthenticatedQueryToolkit';
import { useQuery } from '@tanstack/react-query';
-import { apiClient } from 'expo-app/lib/api/packrat';
-import { useAuthenticatedQueryToolkit } from 'expo-app/lib/hooks/useAuthenticatedQueryToolkit';
export const fetchAllPacks = async () => {
const { data, error } = await apiClient.packs.get({ query: { includePublic: 0 } });
diff --git a/apps/expo/features/packs/hooks/useBulkAddCatalogItems.ts b/packages/app/src/packs/hooks/useBulkAddCatalogItems.ts
similarity index 95%
rename from apps/expo/features/packs/hooks/useBulkAddCatalogItems.ts
rename to packages/app/src/packs/hooks/useBulkAddCatalogItems.ts
index 24a36cd18d..bb57357810 100644
--- a/apps/expo/features/packs/hooks/useBulkAddCatalogItems.ts
+++ b/packages/app/src/packs/hooks/useBulkAddCatalogItems.ts
@@ -1,8 +1,8 @@
import { WeightUnitSchema } from '@packrat/api/types';
+import type { CatalogItemWithPackItemFields } from '@packrat/app/catalog';
import { fromZod } from '@packrat/guards';
import { useState } from 'react';
import { cacheCatalogItemImage } from '../../catalog/lib/cacheCatalogItemImage';
-import type { CatalogItemWithPackItemFields } from '../../catalog/types';
import { useCreatePackItem } from './useCreatePackItem';
export function useBulkAddCatalogItems() {
diff --git a/apps/expo/features/packs/hooks/useCategoriesCount.ts b/packages/app/src/packs/hooks/useCategoriesCount.ts
similarity index 100%
rename from apps/expo/features/packs/hooks/useCategoriesCount.ts
rename to packages/app/src/packs/hooks/useCategoriesCount.ts
diff --git a/apps/expo/features/packs/hooks/useCreatePack.ts b/packages/app/src/packs/hooks/useCreatePack.ts
similarity index 75%
rename from apps/expo/features/packs/hooks/useCreatePack.ts
rename to packages/app/src/packs/hooks/useCreatePack.ts
index 38c4a7d918..79524abf39 100644
--- a/apps/expo/features/packs/hooks/useCreatePack.ts
+++ b/packages/app/src/packs/hooks/useCreatePack.ts
@@ -1,8 +1,8 @@
-import { packsStore } from 'expo-app/features/packs/store';
-import { obs } from 'expo-app/lib/store';
+import { obs } from '@packrat/app/lib/store';
+import type { PackInput, PackInStore } from '@packrat/app/packs';
+import { packsStore } from '@packrat/app/packs/store';
import { nanoid } from 'nanoid';
import { useCallback } from 'react';
-import type { PackInput, PackInStore } from '../types';
// Hook to create a pack
export function useCreatePack() {
diff --git a/apps/expo/features/packs/hooks/useCreatePackFromPack.ts b/packages/app/src/packs/hooks/useCreatePackFromPack.ts
similarity index 87%
rename from apps/expo/features/packs/hooks/useCreatePackFromPack.ts
rename to packages/app/src/packs/hooks/useCreatePackFromPack.ts
index 287c8dee5b..27a501c275 100644
--- a/apps/expo/features/packs/hooks/useCreatePackFromPack.ts
+++ b/packages/app/src/packs/hooks/useCreatePackFromPack.ts
@@ -1,6 +1,6 @@
-import { packItemsStore, packsStore } from 'expo-app/features/packs/store';
-import { recordPackWeight } from 'expo-app/features/packs/store/packWeightHistory';
-import type { Pack, PackInput, PackInStore } from 'expo-app/features/packs/types';
+import type { Pack, PackInput, PackInStore } from '@packrat/app/packs';
+import { packItemsStore, packsStore } from '@packrat/app/packs/store';
+import { recordPackWeight } from '@packrat/app/packs/store/packWeightHistory';
import { nanoid } from 'nanoid';
import { useCallback } from 'react';
diff --git a/apps/expo/features/packs/hooks/useCreatePackItem.ts b/packages/app/src/packs/hooks/useCreatePackItem.ts
similarity index 85%
rename from apps/expo/features/packs/hooks/useCreatePackItem.ts
rename to packages/app/src/packs/hooks/useCreatePackItem.ts
index a9d677971b..1b3e375592 100644
--- a/apps/expo/features/packs/hooks/useCreatePackItem.ts
+++ b/packages/app/src/packs/hooks/useCreatePackItem.ts
@@ -1,9 +1,9 @@
-import { packItemsStore, packsStore } from 'expo-app/features/packs/store';
-import { obs } from 'expo-app/lib/store';
+import { obs } from '@packrat/app/lib/store';
+import type { PackItem, PackItemInput } from '@packrat/app/packs';
+import { packItemsStore, packsStore } from '@packrat/app/packs/store';
import { nanoid } from 'nanoid';
import { useCallback } from 'react';
import { recordPackWeight } from '../store/packWeightHistory';
-import type { PackItem, PackItemInput } from '../types';
export function useCreatePackItem() {
const createPackItem = useCallback(
diff --git a/apps/expo/features/packs/hooks/useCreatePackWithItems.ts b/packages/app/src/packs/hooks/useCreatePackWithItems.ts
similarity index 95%
rename from apps/expo/features/packs/hooks/useCreatePackWithItems.ts
rename to packages/app/src/packs/hooks/useCreatePackWithItems.ts
index 6ddcc44167..f2fef37805 100644
--- a/apps/expo/features/packs/hooks/useCreatePackWithItems.ts
+++ b/packages/app/src/packs/hooks/useCreatePackWithItems.ts
@@ -1,5 +1,5 @@
+import type { PackInput, PackItemInput } from '@packrat/app/packs';
import { useCallback } from 'react';
-import type { PackInput, PackItemInput } from '../types';
import { useCreatePack } from './useCreatePack';
import { useCreatePackItem } from './useCreatePackItem';
diff --git a/apps/expo/features/packs/hooks/useCurrentPack.ts b/packages/app/src/packs/hooks/useCurrentPack.ts
similarity index 100%
rename from apps/expo/features/packs/hooks/useCurrentPack.ts
rename to packages/app/src/packs/hooks/useCurrentPack.ts
diff --git a/apps/expo/features/packs/hooks/useDeletePack.ts b/packages/app/src/packs/hooks/useDeletePack.ts
similarity index 72%
rename from apps/expo/features/packs/hooks/useDeletePack.ts
rename to packages/app/src/packs/hooks/useDeletePack.ts
index 9c69d93d26..7ebeafcad0 100644
--- a/apps/expo/features/packs/hooks/useDeletePack.ts
+++ b/packages/app/src/packs/hooks/useDeletePack.ts
@@ -1,5 +1,5 @@
-import { getPackItems, packItemsStore, packsStore } from 'expo-app/features/packs/store';
-import { obs } from 'expo-app/lib/store';
+import { obs } from '@packrat/app/lib/store';
+import { getPackItems, packItemsStore, packsStore } from '@packrat/app/packs/store';
import { useCallback } from 'react';
export function useDeletePack() {
diff --git a/apps/expo/features/packs/hooks/useDeletePackItem.ts b/packages/app/src/packs/hooks/useDeletePackItem.ts
similarity index 73%
rename from apps/expo/features/packs/hooks/useDeletePackItem.ts
rename to packages/app/src/packs/hooks/useDeletePackItem.ts
index 9e8a35c557..e9de2b6f0a 100644
--- a/apps/expo/features/packs/hooks/useDeletePackItem.ts
+++ b/packages/app/src/packs/hooks/useDeletePackItem.ts
@@ -1,5 +1,5 @@
-import { packItemsStore } from 'expo-app/features/packs/store';
-import { obs } from 'expo-app/lib/store';
+import { obs } from '@packrat/app/lib/store';
+import { packItemsStore } from '@packrat/app/packs/store';
import { useCallback } from 'react';
export function useDeletePackItem() {
diff --git a/apps/expo/features/packs/hooks/useDetailedPacks.ts b/packages/app/src/packs/hooks/useDetailedPacks.ts
similarity index 82%
rename from apps/expo/features/packs/hooks/useDetailedPacks.ts
rename to packages/app/src/packs/hooks/useDetailedPacks.ts
index f8ad24344d..cb3e9e2f70 100644
--- a/apps/expo/features/packs/hooks/useDetailedPacks.ts
+++ b/packages/app/src/packs/hooks/useDetailedPacks.ts
@@ -1,6 +1,6 @@
import { use$ } from '@legendapp/state/react';
-import { getPackItems, packsStore } from 'expo-app/features/packs/store';
-import type { Pack } from '../types';
+import type { Pack } from '@packrat/app/packs';
+import { getPackItems, packsStore } from '@packrat/app/packs/store';
import { computePackWeights } from '../utils';
export function useDetailedPacks(): Pack[] {
diff --git a/apps/expo/features/packs/hooks/useDuplicatePack.ts b/packages/app/src/packs/hooks/useDuplicatePack.ts
similarity index 92%
rename from apps/expo/features/packs/hooks/useDuplicatePack.ts
rename to packages/app/src/packs/hooks/useDuplicatePack.ts
index 7570426363..0747916b27 100644
--- a/apps/expo/features/packs/hooks/useDuplicatePack.ts
+++ b/packages/app/src/packs/hooks/useDuplicatePack.ts
@@ -1,7 +1,7 @@
+import { apiClient } from '@packrat/app/lib/api/packrat';
+import type { Pack, PackInput } from '@packrat/app/packs';
import { useMutation, useQueryClient } from '@tanstack/react-query';
-import { apiClient } from 'expo-app/lib/api/packrat';
import { useCallback } from 'react';
-import type { Pack, PackInput } from '../types';
import { useCreatePackFromPack } from './useCreatePackFromPack';
export function useDuplicatePack() {
diff --git a/apps/expo/features/packs/hooks/useHasMinimumInventory.ts b/packages/app/src/packs/hooks/useHasMinimumInventory.ts
similarity index 100%
rename from apps/expo/features/packs/hooks/useHasMinimumInventory.ts
rename to packages/app/src/packs/hooks/useHasMinimumInventory.ts
diff --git a/apps/expo/features/packs/hooks/useImageDetection.ts b/packages/app/src/packs/hooks/useImageDetection.ts
similarity index 91%
rename from apps/expo/features/packs/hooks/useImageDetection.ts
rename to packages/app/src/packs/hooks/useImageDetection.ts
index 89b0b48ec7..71e2ccd6aa 100644
--- a/apps/expo/features/packs/hooks/useImageDetection.ts
+++ b/packages/app/src/packs/hooks/useImageDetection.ts
@@ -1,6 +1,6 @@
+import type { CatalogItem } from '@packrat/app/catalog';
+import { apiClient } from '@packrat/app/lib/api/packrat';
import { useMutation } from '@tanstack/react-query';
-import type { CatalogItem } from 'expo-app/features/catalog/types';
-import { apiClient } from 'expo-app/lib/api/packrat';
import { uploadImage } from '../utils';
import type { SelectedImage } from './useImagePicker';
diff --git a/apps/expo/features/packs/hooks/useImagePicker.ts b/packages/app/src/packs/hooks/useImagePicker.ts
similarity index 97%
rename from apps/expo/features/packs/hooks/useImagePicker.ts
rename to packages/app/src/packs/hooks/useImagePicker.ts
index bde40fae25..f52ca0ce93 100644
--- a/apps/expo/features/packs/hooks/useImagePicker.ts
+++ b/packages/app/src/packs/hooks/useImagePicker.ts
@@ -1,5 +1,5 @@
+import ImageCacheManager from '@packrat/app/lib/utils/ImageCacheManager';
import { assertDefined } from '@packrat/guards';
-import ImageCacheManager from 'expo-app/lib/utils/ImageCacheManager';
import * as ImagePicker from 'expo-image-picker';
import { nanoid } from 'nanoid';
import { useState } from 'react';
diff --git a/apps/expo/features/packs/hooks/usePackDetailsFromApi.ts b/packages/app/src/packs/hooks/usePackDetailsFromApi.ts
similarity index 82%
rename from apps/expo/features/packs/hooks/usePackDetailsFromApi.ts
rename to packages/app/src/packs/hooks/usePackDetailsFromApi.ts
index 67714cec3f..cf71ddf1f2 100644
--- a/apps/expo/features/packs/hooks/usePackDetailsFromApi.ts
+++ b/packages/app/src/packs/hooks/usePackDetailsFromApi.ts
@@ -1,7 +1,7 @@
import { PackWithWeightsSchema } from '@packrat/api/schemas/packs';
+import { apiClient } from '@packrat/app/lib/api/packrat';
+import { useAuthenticatedQueryToolkit } from '@packrat/app/lib/hooks/useAuthenticatedQueryToolkit';
import { useQuery } from '@tanstack/react-query';
-import { apiClient } from 'expo-app/lib/api/packrat';
-import { useAuthenticatedQueryToolkit } from 'expo-app/lib/hooks/useAuthenticatedQueryToolkit';
const fetchPackById = async (id: string) => {
const { data, error } = await apiClient.packs({ packId: id }).get();
diff --git a/apps/expo/features/packs/hooks/usePackDetailsFromStore.ts b/packages/app/src/packs/hooks/usePackDetailsFromStore.ts
similarity index 77%
rename from apps/expo/features/packs/hooks/usePackDetailsFromStore.ts
rename to packages/app/src/packs/hooks/usePackDetailsFromStore.ts
index 9bdb7bc106..215bd7a54b 100644
--- a/apps/expo/features/packs/hooks/usePackDetailsFromStore.ts
+++ b/packages/app/src/packs/hooks/usePackDetailsFromStore.ts
@@ -1,8 +1,7 @@
import { use$ } from '@legendapp/state/react';
-
-import { getPackItems, packsStore } from 'expo-app/features/packs/store';
-import { obs } from 'expo-app/lib/store';
-import { computePackWeights } from '../utils/computePackWeights';
+import { obs } from '@packrat/app/lib/store';
+import { computePackWeights } from '@packrat/app/packs';
+import { getPackItems, packsStore } from '@packrat/app/packs/store';
/**
* Retrieves pack from store.
diff --git a/apps/expo/features/packs/hooks/usePackGapAnalysis.ts b/packages/app/src/packs/hooks/usePackGapAnalysis.ts
similarity index 95%
rename from apps/expo/features/packs/hooks/usePackGapAnalysis.ts
rename to packages/app/src/packs/hooks/usePackGapAnalysis.ts
index 3387d24278..fed0951bd9 100644
--- a/apps/expo/features/packs/hooks/usePackGapAnalysis.ts
+++ b/packages/app/src/packs/hooks/usePackGapAnalysis.ts
@@ -1,5 +1,5 @@
+import { apiClient } from '@packrat/app/lib/api/packrat';
import { useMutation } from '@tanstack/react-query';
-import { apiClient } from 'expo-app/lib/api/packrat';
export interface GapAnalysisRequest {
destination?: string;
diff --git a/apps/expo/features/packs/hooks/usePackItemDetailsFromApi.ts b/packages/app/src/packs/hooks/usePackItemDetailsFromApi.ts
similarity index 80%
rename from apps/expo/features/packs/hooks/usePackItemDetailsFromApi.ts
rename to packages/app/src/packs/hooks/usePackItemDetailsFromApi.ts
index 8229888c23..61fc917cb4 100644
--- a/apps/expo/features/packs/hooks/usePackItemDetailsFromApi.ts
+++ b/packages/app/src/packs/hooks/usePackItemDetailsFromApi.ts
@@ -1,8 +1,8 @@
import { PackItemSchema } from '@packrat/api/schemas/packs';
+import { apiClient } from '@packrat/app/lib/api/packrat';
+import { useAuthenticatedQueryToolkit } from '@packrat/app/lib/hooks/useAuthenticatedQueryToolkit';
+import type { PackItem } from '@packrat/app/packs';
import { useQuery } from '@tanstack/react-query';
-import { apiClient } from 'expo-app/lib/api/packrat';
-import { useAuthenticatedQueryToolkit } from 'expo-app/lib/hooks/useAuthenticatedQueryToolkit';
-import type { PackItem } from '../types';
export async function fetchPackItemById(id: string) {
const { data, error } = await apiClient.packs.items({ itemId: id }).get();
diff --git a/apps/expo/features/packs/hooks/usePackItemDetailsFromStore.ts b/packages/app/src/packs/hooks/usePackItemDetailsFromStore.ts
similarity index 86%
rename from apps/expo/features/packs/hooks/usePackItemDetailsFromStore.ts
rename to packages/app/src/packs/hooks/usePackItemDetailsFromStore.ts
index a5055558e9..bf8cc2f08e 100644
--- a/apps/expo/features/packs/hooks/usePackItemDetailsFromStore.ts
+++ b/packages/app/src/packs/hooks/usePackItemDetailsFromStore.ts
@@ -1,5 +1,5 @@
import { use$ } from '@legendapp/state/react';
-import { packItemsStore } from 'expo-app/features/packs/store';
+import { packItemsStore } from '@packrat/app/packs/store';
/**
* Retrieves item from store.
diff --git a/apps/expo/features/packs/hooks/usePackItemOwnershipCheck.ts b/packages/app/src/packs/hooks/usePackItemOwnershipCheck.ts
similarity index 79%
rename from apps/expo/features/packs/hooks/usePackItemOwnershipCheck.ts
rename to packages/app/src/packs/hooks/usePackItemOwnershipCheck.ts
index 563e42f3ce..58c9c3d1c1 100644
--- a/apps/expo/features/packs/hooks/usePackItemOwnershipCheck.ts
+++ b/packages/app/src/packs/hooks/usePackItemOwnershipCheck.ts
@@ -1,4 +1,4 @@
-import { obs } from 'expo-app/lib/store';
+import { obs } from '@packrat/app/lib/store';
import { packItemsStore } from '../store';
export function usePackItemOwnershipCheck(id: string) {
diff --git a/apps/expo/features/packs/hooks/usePackOwnershipCheck.ts b/packages/app/src/packs/hooks/usePackOwnershipCheck.ts
similarity index 77%
rename from apps/expo/features/packs/hooks/usePackOwnershipCheck.ts
rename to packages/app/src/packs/hooks/usePackOwnershipCheck.ts
index a681ce4edf..73ffce6fb8 100644
--- a/apps/expo/features/packs/hooks/usePackOwnershipCheck.ts
+++ b/packages/app/src/packs/hooks/usePackOwnershipCheck.ts
@@ -1,4 +1,4 @@
-import { obs } from 'expo-app/lib/store';
+import { obs } from '@packrat/app/lib/store';
import { packsStore } from '../store';
export function usePackOwnershipCheck(id: string) {
diff --git a/apps/expo/features/packs/hooks/usePackWeightAnalysis.ts b/packages/app/src/packs/hooks/usePackWeightAnalysis.ts
similarity index 95%
rename from apps/expo/features/packs/hooks/usePackWeightAnalysis.ts
rename to packages/app/src/packs/hooks/usePackWeightAnalysis.ts
index a9559d0f49..fca8510adf 100644
--- a/apps/expo/features/packs/hooks/usePackWeightAnalysis.ts
+++ b/packages/app/src/packs/hooks/usePackWeightAnalysis.ts
@@ -1,4 +1,4 @@
-import { userStore } from 'expo-app/features/auth/store';
+import { userStore } from '@packrat/app/auth/store';
import { computeCategorySummaries, convertFromGrams, convertToGrams } from '../utils';
import { usePackDetailsFromStore } from './usePackDetailsFromStore';
diff --git a/apps/expo/features/packs/hooks/usePackWeightHistory.ts b/packages/app/src/packs/hooks/usePackWeightHistory.ts
similarity index 97%
rename from apps/expo/features/packs/hooks/usePackWeightHistory.ts
rename to packages/app/src/packs/hooks/usePackWeightHistory.ts
index cf73028208..cd0af67b74 100644
--- a/apps/expo/features/packs/hooks/usePackWeightHistory.ts
+++ b/packages/app/src/packs/hooks/usePackWeightHistory.ts
@@ -1,7 +1,7 @@
import { use$ } from '@legendapp/state/react';
+import type { PackWeightHistoryEntry } from '@packrat/app/packs';
import { assertDefined } from '@packrat/guards';
import { packWeigthHistoryStore } from '../store/packWeightHistory';
-import type { PackWeightHistoryEntry } from '../types';
export type PackMonthlyAverage = {
month: string;
diff --git a/apps/expo/features/packs/hooks/usePacks.ts b/packages/app/src/packs/hooks/usePacks.ts
similarity index 83%
rename from apps/expo/features/packs/hooks/usePacks.ts
rename to packages/app/src/packs/hooks/usePacks.ts
index f241d5c9f5..55f9116281 100644
--- a/apps/expo/features/packs/hooks/usePacks.ts
+++ b/packages/app/src/packs/hooks/usePacks.ts
@@ -1,6 +1,6 @@
import { use$ } from '@legendapp/state/react';
-import { packsStore } from 'expo-app/features/packs/store';
+import { packsStore } from '@packrat/app/packs/store';
export function usePacks() {
const packs = use$(() => {
diff --git a/apps/expo/features/packs/hooks/useRecentPacks.ts b/packages/app/src/packs/hooks/useRecentPacks.ts
similarity index 93%
rename from apps/expo/features/packs/hooks/useRecentPacks.ts
rename to packages/app/src/packs/hooks/useRecentPacks.ts
index c6e3818f34..8ff6393078 100644
--- a/apps/expo/features/packs/hooks/useRecentPacks.ts
+++ b/packages/app/src/packs/hooks/useRecentPacks.ts
@@ -1,6 +1,6 @@
import { use$ } from '@legendapp/state/react';
+import { computePackWeights } from '@packrat/app/packs';
import { getPackItems } from '../store';
-import { computePackWeights } from '../utils/computePackWeights';
import { usePacks } from './usePacks';
export function useRecentPacks() {
diff --git a/apps/expo/features/packs/hooks/useSeasonSuggestions.ts b/packages/app/src/packs/hooks/useSeasonSuggestions.ts
similarity index 88%
rename from apps/expo/features/packs/hooks/useSeasonSuggestions.ts
rename to packages/app/src/packs/hooks/useSeasonSuggestions.ts
index 4aa43cbf17..49089c8ef0 100644
--- a/apps/expo/features/packs/hooks/useSeasonSuggestions.ts
+++ b/packages/app/src/packs/hooks/useSeasonSuggestions.ts
@@ -1,6 +1,6 @@
+import { apiClient } from '@packrat/app/lib/api/packrat';
+import type { PackInput, PackItemInput } from '@packrat/app/packs';
import { useMutation } from '@tanstack/react-query';
-import { apiClient } from 'expo-app/lib/api/packrat';
-import type { PackInput, PackItemInput } from '../types';
export interface SeasonSuggestionsRequest {
location: string;
diff --git a/apps/expo/features/packs/hooks/useUpdatePack.ts b/packages/app/src/packs/hooks/useUpdatePack.ts
similarity index 63%
rename from apps/expo/features/packs/hooks/useUpdatePack.ts
rename to packages/app/src/packs/hooks/useUpdatePack.ts
index 638504bc70..cd41e0ede8 100644
--- a/apps/expo/features/packs/hooks/useUpdatePack.ts
+++ b/packages/app/src/packs/hooks/useUpdatePack.ts
@@ -1,7 +1,7 @@
-import { packsStore } from 'expo-app/features/packs/store';
-import { obs } from 'expo-app/lib/store';
+import { obs } from '@packrat/app/lib/store';
+import type { Pack } from '@packrat/app/packs';
+import { packsStore } from '@packrat/app/packs/store';
import { useCallback } from 'react';
-import type { Pack } from '../types';
export function useUpdatePack() {
const updatePack = useCallback((pack: Pack) => {
diff --git a/apps/expo/features/packs/hooks/useUpdatePackItem.ts b/packages/app/src/packs/hooks/useUpdatePackItem.ts
similarity index 82%
rename from apps/expo/features/packs/hooks/useUpdatePackItem.ts
rename to packages/app/src/packs/hooks/useUpdatePackItem.ts
index e5ba312cc3..f9672cf2f8 100644
--- a/apps/expo/features/packs/hooks/useUpdatePackItem.ts
+++ b/packages/app/src/packs/hooks/useUpdatePackItem.ts
@@ -1,7 +1,7 @@
-import { packItemsStore, packsStore } from 'expo-app/features/packs/store';
+import type { PackItem } from '@packrat/app/packs';
+import { packItemsStore, packsStore } from '@packrat/app/packs/store';
import { useCallback } from 'react';
import { recordPackWeight } from '../store/packWeightHistory';
-import type { PackItem } from '../types';
export function useUpdatePackItem() {
const updatePackItem = useCallback((item: PackItem) => {
diff --git a/apps/expo/features/packs/hooks/useUserPackItems.ts b/packages/app/src/packs/hooks/useUserPackItems.ts
similarity index 100%
rename from apps/expo/features/packs/hooks/useUserPackItems.ts
rename to packages/app/src/packs/hooks/useUserPackItems.ts
diff --git a/packages/app/src/packs/index.ts b/packages/app/src/packs/index.ts
new file mode 100644
index 0000000000..38c9c34e27
--- /dev/null
+++ b/packages/app/src/packs/index.ts
@@ -0,0 +1,15 @@
+export type {
+ Pack,
+ PackCategory,
+ PackInput,
+ PackInStore,
+ PackItem,
+ PackItemCategory,
+ PackItemInput,
+ PackWeightHistoryEntry,
+ Weight,
+ WeightUnit,
+} from './types';
+export { computePackWeights } from './utils/computePackWeights';
+export { convertFromGrams } from './utils/convertFromGrams';
+export { convertToGrams } from './utils/convertToGrams';
diff --git a/apps/expo/features/packs/input.ts b/packages/app/src/packs/input.ts
similarity index 83%
rename from apps/expo/features/packs/input.ts
rename to packages/app/src/packs/input.ts
index 8d9a4895e0..ac6329784f 100644
--- a/apps/expo/features/packs/input.ts
+++ b/packages/app/src/packs/input.ts
@@ -1,4 +1,4 @@
-import type { WeightUnit } from 'expo-app/types';
+import type { WeightUnit } from '@packrat/api/types';
export interface PackItemInput {
name: string;
diff --git a/apps/expo/features/packs/packListAtoms.ts b/packages/app/src/packs/packListAtoms.ts
similarity index 72%
rename from apps/expo/features/packs/packListAtoms.ts
rename to packages/app/src/packs/packListAtoms.ts
index daf48bb44b..52973f5ad7 100644
--- a/apps/expo/features/packs/packListAtoms.ts
+++ b/packages/app/src/packs/packListAtoms.ts
@@ -1,4 +1,4 @@
-import type { PackCategory } from 'expo-app/types';
+import type { PackCategory } from '@packrat/app/types';
import { atom } from 'jotai';
export const activeFilterAtom = atom('all');
diff --git a/apps/expo/features/packs/screens/CreatePackItemForm.tsx b/packages/app/src/packs/screens/CreatePackItemForm.tsx
similarity index 97%
rename from apps/expo/features/packs/screens/CreatePackItemForm.tsx
rename to packages/app/src/packs/screens/CreatePackItemForm.tsx
index 9f0dff53d7..ee9819e90a 100644
--- a/apps/expo/features/packs/screens/CreatePackItemForm.tsx
+++ b/packages/app/src/packs/screens/CreatePackItemForm.tsx
@@ -1,13 +1,14 @@
import { useActionSheet } from '@expo/react-native-action-sheet';
+import { Icon } from '@packrat/app/components/Icon';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
+import { testIds } from '@packrat/app/lib/testIds';
+import ImageCacheManager from '@packrat/app/lib/utils/ImageCacheManager';
+import type { PackItem } from '@packrat/app/packs';
+import type { WeightUnit } from '@packrat/app/types';
import { safeIndexOf } from '@packrat/guards';
import { Form, FormItem, FormSection, SegmentedControl, TextField } from '@packrat/ui/nativewindui';
import { useForm } from '@tanstack/react-form';
-import { Icon } from 'expo-app/components/Icon';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
-import { testIds } from 'expo-app/lib/testIds';
-import ImageCacheManager from 'expo-app/lib/utils/ImageCacheManager';
-import type { WeightUnit } from 'expo-app/types';
import { useRouter } from 'expo-router';
import { useEffect, useMemo, useRef, useState } from 'react';
import { Alert, Image, Pressable, Switch, Text, TouchableOpacity, View } from 'react-native';
@@ -16,7 +17,6 @@ import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { z } from 'zod';
import { useCreatePackItem, useUpdatePackItem } from '../hooks';
import { useImagePicker } from '../hooks/useImagePicker';
-import type { PackItem } from '../types';
// Define Zod schema
const itemFormSchema = z.object({
diff --git a/apps/expo/features/packs/screens/CreatePackScreen.tsx b/packages/app/src/packs/screens/CreatePackScreen.tsx
similarity index 100%
rename from apps/expo/features/packs/screens/CreatePackScreen.tsx
rename to packages/app/src/packs/screens/CreatePackScreen.tsx
diff --git a/apps/expo/features/packs/screens/EditPackItemScreen.tsx b/packages/app/src/packs/screens/EditPackItemScreen.tsx
similarity index 76%
rename from apps/expo/features/packs/screens/EditPackItemScreen.tsx
rename to packages/app/src/packs/screens/EditPackItemScreen.tsx
index a1035ae45e..dc54ae4b84 100644
--- a/apps/expo/features/packs/screens/EditPackItemScreen.tsx
+++ b/packages/app/src/packs/screens/EditPackItemScreen.tsx
@@ -1,6 +1,6 @@
-import { CreatePackItemForm } from 'expo-app/features/packs/screens/CreatePackItemForm';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
-import { NotFoundScreen } from 'expo-app/screens/NotFoundScreen';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
+import { CreatePackItemForm } from '@packrat/app/packs/screens/CreatePackItemForm';
+import { NotFoundScreen } from '@packrat/app/screens/NotFoundScreen';
import { useLocalSearchParams } from 'expo-router';
import { usePackItemDetailsFromStore } from '../hooks';
diff --git a/apps/expo/features/packs/screens/EditPackScreen.tsx b/packages/app/src/packs/screens/EditPackScreen.tsx
similarity index 70%
rename from apps/expo/features/packs/screens/EditPackScreen.tsx
rename to packages/app/src/packs/screens/EditPackScreen.tsx
index 544374b503..895c052698 100644
--- a/apps/expo/features/packs/screens/EditPackScreen.tsx
+++ b/packages/app/src/packs/screens/EditPackScreen.tsx
@@ -1,6 +1,6 @@
+import { PackForm } from '@packrat/app/packs/components/PackForm';
+import { usePackDetailsFromStore } from '@packrat/app/packs/hooks/usePackDetailsFromStore';
import { assertDefined } from '@packrat/guards';
-import { usePackDetailsFromStore } from 'expo-app/features/packs';
-import { PackForm } from 'expo-app/features/packs/components/PackForm';
import { useLocalSearchParams } from 'expo-router';
export function EditPackScreen() {
diff --git a/apps/expo/features/packs/screens/ItemsScanScreen.tsx b/packages/app/src/packs/screens/ItemsScanScreen.tsx
similarity index 95%
rename from apps/expo/features/packs/screens/ItemsScanScreen.tsx
rename to packages/app/src/packs/screens/ItemsScanScreen.tsx
index 4f6093118f..9f705be0dd 100644
--- a/apps/expo/features/packs/screens/ItemsScanScreen.tsx
+++ b/packages/app/src/packs/screens/ItemsScanScreen.tsx
@@ -1,18 +1,18 @@
import { useActionSheet } from '@expo/react-native-action-sheet';
+import type { CatalogItem, CatalogItemWithPackItemFields } from '@packrat/app/catalog';
+import { ErrorState } from '@packrat/app/components/ErrorState';
+import { Icon } from '@packrat/app/components/Icon';
+import { appAlert } from '@packrat/app/lib/appAlert';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
+import { type SelectedImage, useImagePicker } from '@packrat/app/packs/hooks/useImagePicker';
import { assertNonNull } from '@packrat/guards';
import { ActivityIndicator, Button, Text } from '@packrat/ui/nativewindui';
import * as Burnt from 'burnt';
-import { appAlert } from 'expo-app/app/_layout';
-import { ErrorState } from 'expo-app/components/ErrorState';
-import { Icon } from 'expo-app/components/Icon';
-import { type SelectedImage, useImagePicker } from 'expo-app/features/packs/hooks/useImagePicker';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { Stack, useLocalSearchParams, useRouter } from 'expo-router';
import { useCallback, useEffect, useState } from 'react';
import { Image, ScrollView, View } from 'react-native';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
-import type { CatalogItem, CatalogItemWithPackItemFields } from '../../catalog/types';
import { HorizontalCatalogItemCard } from '../components/HorizontalCatalogItemCard';
import { useBulkAddCatalogItems } from '../hooks';
import { useImageDetection } from '../hooks/useImageDetection';
diff --git a/apps/expo/features/packs/screens/PackDetailScreen.tsx b/packages/app/src/packs/screens/PackDetailScreen.tsx
similarity index 95%
rename from apps/expo/features/packs/screens/PackDetailScreen.tsx
rename to packages/app/src/packs/screens/PackDetailScreen.tsx
index 78d45b6e1d..ace2e33b5a 100644
--- a/apps/expo/features/packs/screens/PackDetailScreen.tsx
+++ b/packages/app/src/packs/screens/PackDetailScreen.tsx
@@ -1,23 +1,24 @@
import { BottomSheetView } from '@gorhom/bottom-sheet';
+import { isAuthed } from '@packrat/app/auth/store';
+import { Icon } from '@packrat/app/components/Icon';
+import { Chip } from '@packrat/app/components/initial/Chip';
+import { WeightBadge } from '@packrat/app/components/initial/WeightBadge';
+import { appAlert } from '@packrat/app/lib/appAlert';
+import { cn } from '@packrat/app/lib/cn';
+import { useBottomSheetAction } from '@packrat/app/lib/hooks/useBottomSheetAction';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
+import { obs } from '@packrat/app/lib/store';
+import { testIds } from '@packrat/app/lib/testIds';
+import type { Pack, PackCategory, PackItem } from '@packrat/app/packs';
+import { ActivityPicker } from '@packrat/app/packs/components/ActivityPicker';
+import { GapAnalysisModal } from '@packrat/app/packs/components/GapAnalysisModal';
+import { PackItemCard } from '@packrat/app/packs/components/PackItemCard';
+import { LocationPicker } from '@packrat/app/weather/components';
+import type { WeatherLocation } from '@packrat/app/weather/types';
import { isDefined } from '@packrat/guards';
import { ActivityIndicator, Button, Sheet, Text, useSheetRef } from '@packrat/ui/nativewindui';
import * as Burnt from 'burnt';
-import { appAlert } from 'expo-app/app/_layout';
-import { Icon } from 'expo-app/components/Icon';
-import { Chip } from 'expo-app/components/initial/Chip';
-import { WeightBadge } from 'expo-app/components/initial/WeightBadge';
-import { isAuthed } from 'expo-app/features/auth/store';
-import { ActivityPicker } from 'expo-app/features/packs/components/ActivityPicker';
-import { GapAnalysisModal } from 'expo-app/features/packs/components/GapAnalysisModal';
-import { PackItemCard } from 'expo-app/features/packs/components/PackItemCard';
-import { LocationPicker } from 'expo-app/features/weather/components';
-import type { WeatherLocation } from 'expo-app/features/weather/types';
-import { cn } from 'expo-app/lib/cn';
-import { useBottomSheetAction } from 'expo-app/lib/hooks/useBottomSheetAction';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
-import { obs } from 'expo-app/lib/store';
-import { testIds } from 'expo-app/lib/testIds';
import { useLocalSearchParams, useRouter } from 'expo-router';
import { useMemo, useState } from 'react';
import { Image, ScrollView, Share, TouchableOpacity, View } from 'react-native';
@@ -26,7 +27,6 @@ import AddPackItemActions from '../components/AddPackItemActions';
import { usePackDetailsFromApi, usePackDetailsFromStore, usePackGapAnalysis } from '../hooks';
import { usePackOwnershipCheck } from '../hooks/usePackOwnershipCheck';
import { packingModeStore } from '../store/packingMode';
-import type { Pack, PackCategory, PackItem } from '../types';
export function PackDetailScreen() {
const { t } = useTranslation();
diff --git a/apps/expo/features/packs/screens/PackItemDetailScreen.tsx b/packages/app/src/packs/screens/PackItemDetailScreen.tsx
similarity index 95%
rename from apps/expo/features/packs/screens/PackItemDetailScreen.tsx
rename to packages/app/src/packs/screens/PackItemDetailScreen.tsx
index c1b67368a9..20f28e11f4 100644
--- a/apps/expo/features/packs/screens/PackItemDetailScreen.tsx
+++ b/packages/app/src/packs/screens/PackItemDetailScreen.tsx
@@ -1,10 +1,8 @@
-import { isDefined } from '@packrat/guards';
-import { ActivityIndicator, Button, Text, useColorScheme } from '@packrat/ui/nativewindui';
-import { Icon } from 'expo-app/components/Icon';
-import { Chip } from 'expo-app/components/initial/Chip';
-import { WeightBadge } from 'expo-app/components/initial/WeightBadge';
-import { isAuthed } from 'expo-app/features/auth/store';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
+import { isAuthed } from '@packrat/app/auth/store';
+import { Icon } from '@packrat/app/components/Icon';
+import { Chip } from '@packrat/app/components/initial/Chip';
+import { WeightBadge } from '@packrat/app/components/initial/WeightBadge';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
import {
calculateTotalWeight,
getNotes,
@@ -13,7 +11,9 @@ import {
isConsumable,
isWorn,
shouldShowQuantity,
-} from 'expo-app/lib/utils/itemCalculations';
+} from '@packrat/app/lib/utils/itemCalculations';
+import { isDefined } from '@packrat/guards';
+import { ActivityIndicator, Button, Text, useColorScheme } from '@packrat/ui/nativewindui';
import { router, useLocalSearchParams } from 'expo-router';
import { ScrollView, View } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
diff --git a/apps/expo/features/packs/screens/PackListScreen.tsx b/packages/app/src/packs/screens/PackListScreen.tsx
similarity index 91%
rename from apps/expo/features/packs/screens/PackListScreen.tsx
rename to packages/app/src/packs/screens/PackListScreen.tsx
index 9c31d519a4..803bd4cdf7 100644
--- a/apps/expo/features/packs/screens/PackListScreen.tsx
+++ b/packages/app/src/packs/screens/PackListScreen.tsx
@@ -1,3 +1,17 @@
+import { useAuth } from '@packrat/app/auth/hooks/useAuth';
+import { AndroidTabBarInsetFix } from '@packrat/app/components/AndroidTabBarInsetFix';
+import { Icon } from '@packrat/app/components/Icon';
+import { LargeTitleHeaderOverlapFixIOS } from '@packrat/app/components/LargeTitleHeaderOverlapFixIOS';
+import { LargeTitleHeaderSearchContentContainer } from '@packrat/app/components/LargeTitleHeaderSearchContentContainer';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
+import { testIds } from '@packrat/app/lib/testIds';
+import { asNonNullableRef } from '@packrat/app/lib/utils/asNonNullableRef';
+import type { Pack, PackCategory, PackInStore } from '@packrat/app/packs';
+import { PackCard } from '@packrat/app/packs/components/PackCard';
+import { SearchResults } from '@packrat/app/packs/components/SearchResults';
+import SyncBanner from '@packrat/app/packs/components/SyncBanner';
+import { activeFilterAtom, searchValueAtom } from '@packrat/app/packs/packListAtoms';
import type { LargeTitleSearchBarMethods } from '@packrat/ui/nativewindui';
import {
ActivityIndicator,
@@ -5,19 +19,6 @@ import {
LargeTitleHeader,
SegmentedControl,
} from '@packrat/ui/nativewindui';
-import { AndroidTabBarInsetFix } from 'expo-app/components/AndroidTabBarInsetFix';
-import { Icon } from 'expo-app/components/Icon';
-import { LargeTitleHeaderOverlapFixIOS } from 'expo-app/components/LargeTitleHeaderOverlapFixIOS';
-import { LargeTitleHeaderSearchContentContainer } from 'expo-app/components/LargeTitleHeaderSearchContentContainer';
-import { useAuth } from 'expo-app/features/auth/hooks/useAuth';
-import { PackCard } from 'expo-app/features/packs/components/PackCard';
-import { SearchResults } from 'expo-app/features/packs/components/SearchResults';
-import SyncBanner from 'expo-app/features/packs/components/SyncBanner';
-import { activeFilterAtom, searchValueAtom } from 'expo-app/features/packs/packListAtoms';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
-import { testIds } from 'expo-app/lib/testIds';
-import { asNonNullableRef } from 'expo-app/lib/utils/asNonNullableRef';
import { Link, useFocusEffect, useLocalSearchParams, useRouter } from 'expo-router';
import { useAtom } from 'jotai';
import { useCallback, useRef, useState } from 'react';
@@ -33,7 +34,6 @@ import {
import { SafeAreaView } from 'react-native-safe-area-context';
import { useAllPacks } from '../hooks/useAllPacks';
import { usePacks } from '../hooks/usePacks';
-import type { Pack, PackCategory, PackInStore } from '../types';
type FilterOption = {
label: string;
diff --git a/apps/expo/features/packs/store/index.ts b/packages/app/src/packs/store/index.ts
similarity index 100%
rename from apps/expo/features/packs/store/index.ts
rename to packages/app/src/packs/store/index.ts
diff --git a/apps/expo/features/packs/store/packItems.ts b/packages/app/src/packs/store/packItems.ts
similarity index 91%
rename from apps/expo/features/packs/store/packItems.ts
rename to packages/app/src/packs/store/packItems.ts
index e8339a567f..7508216363 100644
--- a/apps/expo/features/packs/store/packItems.ts
+++ b/packages/app/src/packs/store/packItems.ts
@@ -2,12 +2,12 @@ import { observable, syncState } from '@legendapp/state';
import { syncObservable } from '@legendapp/state/sync';
import { syncedCrud } from '@legendapp/state/sync-plugins/crud';
import { PackItemSchema, PackWithWeightsSchema } from '@packrat/api/schemas/packs';
+import { isAuthed } from '@packrat/app/auth/store';
+import { apiClient } from '@packrat/app/lib/api/packrat';
+import { persistPlugin } from '@packrat/app/lib/persist-plugin';
+import ImageCacheManager from '@packrat/app/lib/utils/ImageCacheManager';
+import type { PackItem } from '@packrat/app/packs';
import { isRemoteUrl } from '@packrat/guards';
-import { isAuthed } from 'expo-app/features/auth/store';
-import { apiClient } from 'expo-app/lib/api/packrat';
-import { persistPlugin } from 'expo-app/lib/persist-plugin';
-import ImageCacheManager from 'expo-app/lib/utils/ImageCacheManager';
-import type { PackItem } from '../types';
import { uploadImage } from '../utils';
const listAllPackItems = async (): Promise => {
diff --git a/apps/expo/features/packs/store/packWeightHistory.ts b/packages/app/src/packs/store/packWeightHistory.ts
similarity index 90%
rename from apps/expo/features/packs/store/packWeightHistory.ts
rename to packages/app/src/packs/store/packWeightHistory.ts
index 75dc2f32a1..1c47c91661 100644
--- a/apps/expo/features/packs/store/packWeightHistory.ts
+++ b/packages/app/src/packs/store/packWeightHistory.ts
@@ -2,12 +2,12 @@ import { observable, syncState } from '@legendapp/state';
import { syncObservable } from '@legendapp/state/sync';
import { syncedCrud } from '@legendapp/state/sync-plugins/crud';
import { PackWeightHistoryResponseSchema } from '@packrat/api/schemas/packs';
-import { isAuthed } from 'expo-app/features/auth/store';
-import { apiClient } from 'expo-app/lib/api/packrat';
-import { persistPlugin } from 'expo-app/lib/persist-plugin';
-import { obs } from 'expo-app/lib/store';
+import { isAuthed } from '@packrat/app/auth/store';
+import { apiClient } from '@packrat/app/lib/api/packrat';
+import { persistPlugin } from '@packrat/app/lib/persist-plugin';
+import { obs } from '@packrat/app/lib/store';
+import type { PackWeightHistoryEntry } from '@packrat/app/packs';
import { nanoid } from 'nanoid';
-import type { PackWeightHistoryEntry } from '../types';
import { computePackWeights } from '../utils';
import { packItemsStore } from './packItems';
import { packsStore } from './packs';
diff --git a/apps/expo/features/packs/store/packingMode.ts b/packages/app/src/packs/store/packingMode.ts
similarity index 85%
rename from apps/expo/features/packs/store/packingMode.ts
rename to packages/app/src/packs/store/packingMode.ts
index 18ca1cb146..ac8bfbe69b 100644
--- a/apps/expo/features/packs/store/packingMode.ts
+++ b/packages/app/src/packs/store/packingMode.ts
@@ -1,6 +1,6 @@
import { observable } from '@legendapp/state';
import { syncObservable } from '@legendapp/state/sync';
-import { persistPlugin } from 'expo-app/lib/persist-plugin';
+import { persistPlugin } from '@packrat/app/lib/persist-plugin';
export const packingModeStore = observable>>({});
diff --git a/apps/expo/features/packs/store/packs.ts b/packages/app/src/packs/store/packs.ts
similarity index 94%
rename from apps/expo/features/packs/store/packs.ts
rename to packages/app/src/packs/store/packs.ts
index 4f1917a0e3..36733e68a6 100644
--- a/apps/expo/features/packs/store/packs.ts
+++ b/packages/app/src/packs/store/packs.ts
@@ -2,10 +2,10 @@ import { observable, syncState } from '@legendapp/state';
import { syncObservable } from '@legendapp/state/sync';
import { syncedCrud } from '@legendapp/state/sync-plugins/crud';
import { PackWithWeightsSchema } from '@packrat/api/schemas/packs';
-import { isAuthed } from 'expo-app/features/auth/store';
-import { apiClient } from 'expo-app/lib/api/packrat';
-import { persistPlugin } from 'expo-app/lib/persist-plugin';
-import type { PackInStore } from '../types';
+import { isAuthed } from '@packrat/app/auth/store';
+import { apiClient } from '@packrat/app/lib/api/packrat';
+import { persistPlugin } from '@packrat/app/lib/persist-plugin';
+import type { PackInStore } from '@packrat/app/packs';
let _refreshPacksList: (() => void) | undefined;
export const refreshPacksList = () => _refreshPacksList?.();
diff --git a/apps/expo/features/packs/types.ts b/packages/app/src/packs/types.ts
similarity index 89%
rename from apps/expo/features/packs/types.ts
rename to packages/app/src/packs/types.ts
index 9b6f433895..93e659317a 100644
--- a/apps/expo/features/packs/types.ts
+++ b/packages/app/src/packs/types.ts
@@ -1,6 +1,6 @@
-import type { CatalogItem } from 'expo-app/features/catalog/types';
-import type { PackTemplateItem } from 'expo-app/features/pack-templates/types';
-import type { PackCategory, WeightUnit } from 'expo-app/types';
+import type { PackCategory, WeightUnit } from '@packrat/api/types';
+import type { CatalogItem } from '../catalog/types';
+import type { PackTemplateItem } from '../pack-templates/types';
export type { PackCategory, WeightUnit };
diff --git a/apps/expo/features/packs/utils/__tests__/computePackWeights.test.ts b/packages/app/src/packs/utils/__tests__/computePackWeights.test.ts
similarity index 99%
rename from apps/expo/features/packs/utils/__tests__/computePackWeights.test.ts
rename to packages/app/src/packs/utils/__tests__/computePackWeights.test.ts
index 80b1608278..e3a6b4f199 100644
--- a/apps/expo/features/packs/utils/__tests__/computePackWeights.test.ts
+++ b/packages/app/src/packs/utils/__tests__/computePackWeights.test.ts
@@ -1,6 +1,6 @@
+import type { Pack, PackItem } from '@packrat/app/packs';
+import { computePackWeights } from '@packrat/app/packs';
import { describe, expect, it } from 'vitest';
-import type { Pack, PackItem } from '../../types';
-import { computePackWeights } from '../computePackWeights';
describe('computePackWeights', () => {
describe('basic weight calculations', () => {
diff --git a/apps/expo/features/packs/utils/__tests__/convertFromGrams.test.ts b/packages/app/src/packs/utils/__tests__/convertFromGrams.test.ts
similarity index 99%
rename from apps/expo/features/packs/utils/__tests__/convertFromGrams.test.ts
rename to packages/app/src/packs/utils/__tests__/convertFromGrams.test.ts
index 1af77352eb..fb9f608643 100644
--- a/apps/expo/features/packs/utils/__tests__/convertFromGrams.test.ts
+++ b/packages/app/src/packs/utils/__tests__/convertFromGrams.test.ts
@@ -1,5 +1,5 @@
+import { convertFromGrams } from '@packrat/app/packs';
import { describe, expect, it } from 'vitest';
-import { convertFromGrams } from '../convertFromGrams';
describe('convertFromGrams', () => {
// -------------------------------------------------------------------------
diff --git a/apps/expo/features/packs/utils/__tests__/convertToGrams.test.ts b/packages/app/src/packs/utils/__tests__/convertToGrams.test.ts
similarity index 98%
rename from apps/expo/features/packs/utils/__tests__/convertToGrams.test.ts
rename to packages/app/src/packs/utils/__tests__/convertToGrams.test.ts
index e8903526b6..8eb01a5ff0 100644
--- a/apps/expo/features/packs/utils/__tests__/convertToGrams.test.ts
+++ b/packages/app/src/packs/utils/__tests__/convertToGrams.test.ts
@@ -1,5 +1,5 @@
+import { convertToGrams } from '@packrat/app/packs';
import { describe, expect, it } from 'vitest';
-import { convertToGrams } from '../convertToGrams';
describe('convertToGrams', () => {
// -------------------------------------------------------------------------
diff --git a/apps/expo/features/packs/utils/computeCategories.ts b/packages/app/src/packs/utils/computeCategories.ts
similarity index 87%
rename from apps/expo/features/packs/utils/computeCategories.ts
rename to packages/app/src/packs/utils/computeCategories.ts
index 9646f31335..03429da212 100644
--- a/apps/expo/features/packs/utils/computeCategories.ts
+++ b/packages/app/src/packs/utils/computeCategories.ts
@@ -1,8 +1,7 @@
+import { userStore } from '@packrat/app/auth/store';
+import type { Pack } from '@packrat/app/packs';
+import { convertFromGrams, convertToGrams } from '@packrat/app/packs';
import { assertDefined } from '@packrat/guards';
-import { userStore } from 'expo-app/features/auth/store';
-import type { Pack } from '../types';
-import { convertFromGrams } from './convertFromGrams';
-import { convertToGrams } from './convertToGrams';
export type CategorySummary = {
name: string;
diff --git a/apps/expo/features/packs/utils/computePackWeights.ts b/packages/app/src/packs/utils/computePackWeights.ts
similarity index 100%
rename from apps/expo/features/packs/utils/computePackWeights.ts
rename to packages/app/src/packs/utils/computePackWeights.ts
diff --git a/apps/expo/features/packs/utils/convertFromGrams.ts b/packages/app/src/packs/utils/convertFromGrams.ts
similarity index 100%
rename from apps/expo/features/packs/utils/convertFromGrams.ts
rename to packages/app/src/packs/utils/convertFromGrams.ts
diff --git a/apps/expo/features/packs/utils/convertToGrams.ts b/packages/app/src/packs/utils/convertToGrams.ts
similarity index 100%
rename from apps/expo/features/packs/utils/convertToGrams.ts
rename to packages/app/src/packs/utils/convertToGrams.ts
diff --git a/apps/expo/features/packs/utils/getPackDetailOptions.tsx b/packages/app/src/packs/utils/getPackDetailOptions.tsx
similarity index 93%
rename from apps/expo/features/packs/utils/getPackDetailOptions.tsx
rename to packages/app/src/packs/utils/getPackDetailOptions.tsx
index ccf3497987..5b0de95531 100644
--- a/apps/expo/features/packs/utils/getPackDetailOptions.tsx
+++ b/packages/app/src/packs/utils/getPackDetailOptions.tsx
@@ -1,7 +1,7 @@
+import { Icon } from '@packrat/app/components/Icon';
+import { t } from '@packrat/app/lib/i18n';
+import { testIds } from '@packrat/app/lib/testIds';
import { Alert, Button, useColorScheme, useSheetRef } from '@packrat/ui/nativewindui';
-import { Icon } from 'expo-app/components/Icon';
-import { t } from 'expo-app/lib/i18n';
-import { testIds } from 'expo-app/lib/testIds';
import { useRouter } from 'expo-router';
import { View } from 'react-native';
import AddPackItemActions from '../components/AddPackItemActions';
diff --git a/apps/expo/features/packs/utils/getPackItemDetailOptions.tsx b/packages/app/src/packs/utils/getPackItemDetailOptions.tsx
similarity index 95%
rename from apps/expo/features/packs/utils/getPackItemDetailOptions.tsx
rename to packages/app/src/packs/utils/getPackItemDetailOptions.tsx
index 6f166f2862..eac8a8d32a 100644
--- a/apps/expo/features/packs/utils/getPackItemDetailOptions.tsx
+++ b/packages/app/src/packs/utils/getPackItemDetailOptions.tsx
@@ -1,7 +1,7 @@
+import { Icon } from '@packrat/app/components/Icon';
+import { t } from '@packrat/app/lib/i18n';
import { assertDefined } from '@packrat/guards';
import { Alert, Button, useColorScheme } from '@packrat/ui/nativewindui';
-import { Icon } from 'expo-app/components/Icon';
-import { t } from 'expo-app/lib/i18n';
import { useRouter } from 'expo-router';
import { View } from 'react-native';
import {
diff --git a/packages/app/src/packs/utils/index.ts b/packages/app/src/packs/utils/index.ts
new file mode 100644
index 0000000000..5d90e234e2
--- /dev/null
+++ b/packages/app/src/packs/utils/index.ts
@@ -0,0 +1,5 @@
+export * from '@packrat/app/packs';
+export * from '@packrat/app/packs';
+export * from '@packrat/app/packs';
+export * from './computeCategories';
+export * from './uploadImage';
diff --git a/apps/expo/features/packs/utils/uploadImage.ts b/packages/app/src/packs/utils/uploadImage.ts
similarity index 92%
rename from apps/expo/features/packs/utils/uploadImage.ts
rename to packages/app/src/packs/utils/uploadImage.ts
index 384c2bc8a1..5efd93e154 100644
--- a/apps/expo/features/packs/utils/uploadImage.ts
+++ b/packages/app/src/packs/utils/uploadImage.ts
@@ -1,5 +1,5 @@
-import { userStore } from 'expo-app/features/auth/store';
-import { apiClient } from 'expo-app/lib/api/packrat';
+import { userStore } from '@packrat/app/auth/store';
+import { apiClient } from '@packrat/app/lib/api/packrat';
import * as FileSystem from 'expo-file-system/legacy';
export const uploadImage = async (fileName: string, uri: string): Promise => {
diff --git a/apps/expo/features/profile/components/ProfileAuthWall.tsx b/packages/app/src/profile/components/ProfileAuthWall.tsx
similarity index 95%
rename from apps/expo/features/profile/components/ProfileAuthWall.tsx
rename to packages/app/src/profile/components/ProfileAuthWall.tsx
index fee0cdc17f..82aedfe204 100644
--- a/apps/expo/features/profile/components/ProfileAuthWall.tsx
+++ b/packages/app/src/profile/components/ProfileAuthWall.tsx
@@ -1,6 +1,6 @@
+import { Icon, type MaterialIconName } from '@packrat/app/components/Icon';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
import { Button, Text } from '@packrat/ui/nativewindui';
-import { Icon, type MaterialIconName } from 'expo-app/components/Icon';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { Stack, usePathname, useRouter } from 'expo-router';
import { View } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
diff --git a/apps/expo/features/profile/components/index.ts b/packages/app/src/profile/components/index.ts
similarity index 100%
rename from apps/expo/features/profile/components/index.ts
rename to packages/app/src/profile/components/index.ts
diff --git a/apps/expo/features/profile/hooks/useUpdateProfile.ts b/packages/app/src/profile/hooks/useUpdateProfile.ts
similarity index 90%
rename from apps/expo/features/profile/hooks/useUpdateProfile.ts
rename to packages/app/src/profile/hooks/useUpdateProfile.ts
index 189a5fddc9..637a9cbe94 100644
--- a/apps/expo/features/profile/hooks/useUpdateProfile.ts
+++ b/packages/app/src/profile/hooks/useUpdateProfile.ts
@@ -1,5 +1,5 @@
-import { userStore } from 'expo-app/features/auth/store';
-import { apiClient } from 'expo-app/lib/api/packrat';
+import { userStore } from '@packrat/app/auth/store';
+import { apiClient } from '@packrat/app/lib/api/packrat';
import { useState } from 'react';
export type UpdateProfilePayload = {
diff --git a/packages/app/src/profile/index.ts b/packages/app/src/profile/index.ts
new file mode 100644
index 0000000000..1524bfb596
--- /dev/null
+++ b/packages/app/src/profile/index.ts
@@ -0,0 +1 @@
+export type { User } from './types';
diff --git a/apps/expo/features/profile/types.ts b/packages/app/src/profile/types.ts
similarity index 100%
rename from apps/expo/features/profile/types.ts
rename to packages/app/src/profile/types.ts
diff --git a/apps/expo/providers/JotaiProvider.tsx b/packages/app/src/providers/JotaiProvider.tsx
similarity index 77%
rename from apps/expo/providers/JotaiProvider.tsx
rename to packages/app/src/providers/JotaiProvider.tsx
index 7434e0876b..c18b84de5f 100644
--- a/apps/expo/providers/JotaiProvider.tsx
+++ b/packages/app/src/providers/JotaiProvider.tsx
@@ -1,4 +1,4 @@
-import { store } from 'expo-app/atoms/store';
+import { store } from '@packrat/app/atoms/store';
import { Provider } from 'jotai';
export function JotaiProvider({ children }: { children: React.ReactNode }) {
diff --git a/apps/expo/providers/TanstackProvider.tsx b/packages/app/src/providers/TanstackProvider.tsx
similarity index 100%
rename from apps/expo/providers/TanstackProvider.tsx
rename to packages/app/src/providers/TanstackProvider.tsx
diff --git a/apps/expo/providers/index.tsx b/packages/app/src/providers/index.tsx
similarity index 90%
rename from apps/expo/providers/index.tsx
rename to packages/app/src/providers/index.tsx
index 8bb092b9a4..fa92681d81 100644
--- a/apps/expo/providers/index.tsx
+++ b/packages/app/src/providers/index.tsx
@@ -1,8 +1,8 @@
import { ActionSheetProvider } from '@expo/react-native-action-sheet';
import { BottomSheetModalProvider } from '@gorhom/bottom-sheet';
+import { ErrorBoundary } from '@packrat/app/components/initial/ErrorBoundary';
import { PortalHost } from '@rn-primitives/portal';
-import { ErrorBoundary } from 'expo-app/components/initial/ErrorBoundary';
-import 'expo-app/utils/polyfills';
+import '@packrat/app/utils/polyfills';
import { GestureHandlerRootView } from 'react-native-gesture-handler';
import { KeyboardProvider } from 'react-native-keyboard-controller';
import { SafeAreaProvider } from 'react-native-safe-area-context';
diff --git a/apps/expo/screens/ConsentWelcomeScreen.tsx b/packages/app/src/screens/ConsentWelcomeScreen.tsx
similarity index 93%
rename from apps/expo/screens/ConsentWelcomeScreen.tsx
rename to packages/app/src/screens/ConsentWelcomeScreen.tsx
index d9cd215900..7ff67ee7dc 100644
--- a/apps/expo/screens/ConsentWelcomeScreen.tsx
+++ b/packages/app/src/screens/ConsentWelcomeScreen.tsx
@@ -1,7 +1,7 @@
+import { Icon } from '@packrat/app/components/Icon';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
import { Button, Text } from '@packrat/ui/nativewindui';
-import { Icon } from 'expo-app/components/Icon';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { Link } from 'expo-router';
import { Platform, View, type ViewStyle } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
diff --git a/apps/expo/screens/ErrorScreen.tsx b/packages/app/src/screens/ErrorScreen.tsx
similarity index 90%
rename from apps/expo/screens/ErrorScreen.tsx
rename to packages/app/src/screens/ErrorScreen.tsx
index 4d6d617796..56aea422c8 100644
--- a/apps/expo/screens/ErrorScreen.tsx
+++ b/packages/app/src/screens/ErrorScreen.tsx
@@ -1,9 +1,9 @@
'use client';
+import { Icon, type MaterialIconName } from '@packrat/app/components/Icon';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
import { Button } from '@packrat/ui/nativewindui';
-import { Icon, type MaterialIconName } from 'expo-app/components/Icon';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { useRouter } from 'expo-router';
import { Text, View } from 'react-native';
diff --git a/apps/expo/screens/LoadingSpinnerScreen.tsx b/packages/app/src/screens/LoadingSpinnerScreen.tsx
similarity index 100%
rename from apps/expo/screens/LoadingSpinnerScreen.tsx
rename to packages/app/src/screens/LoadingSpinnerScreen.tsx
diff --git a/apps/expo/screens/NotFoundScreen.tsx b/packages/app/src/screens/NotFoundScreen.tsx
similarity index 92%
rename from apps/expo/screens/NotFoundScreen.tsx
rename to packages/app/src/screens/NotFoundScreen.tsx
index 5165b48cd3..21d73b3cf2 100644
--- a/apps/expo/screens/NotFoundScreen.tsx
+++ b/packages/app/src/screens/NotFoundScreen.tsx
@@ -1,7 +1,7 @@
'use client';
+import { Icon } from '@packrat/app/components/Icon';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
import { Button } from '@packrat/ui/nativewindui';
-import { Icon } from 'expo-app/components/Icon';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
import { useRouter } from 'expo-router';
import { Text, View } from 'react-native';
diff --git a/apps/expo/screens/ProfileScreen.tsx b/packages/app/src/screens/ProfileScreen.tsx
similarity index 90%
rename from apps/expo/screens/ProfileScreen.tsx
rename to packages/app/src/screens/ProfileScreen.tsx
index 98222b82a0..52509155c3 100644
--- a/apps/expo/screens/ProfileScreen.tsx
+++ b/packages/app/src/screens/ProfileScreen.tsx
@@ -1,10 +1,10 @@
-import { Icon } from 'expo-app/components/Icon';
-import { UserAvatar } from 'expo-app/components/initial/UserAvatar';
-import type { Pack } from 'expo-app/features/packs';
-import { PackCard } from 'expo-app/features/packs/components/PackCard';
-import { usePacks } from 'expo-app/features/packs/hooks/usePacks';
-import { getPackItems } from 'expo-app/features/packs/store';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
+import { Icon } from '@packrat/app/components/Icon';
+import { UserAvatar } from '@packrat/app/components/initial/UserAvatar';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
+import type { Pack } from '@packrat/app/packs';
+import { PackCard } from '@packrat/app/packs/components/PackCard';
+import { usePacks } from '@packrat/app/packs/hooks/usePacks';
+import { getPackItems } from '@packrat/app/packs/store';
import { ScrollView, Text, TouchableOpacity, View } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { currentUser } from '../data/mockData';
diff --git a/apps/expo/theme/colors.ts b/packages/app/src/theme/colors.ts
similarity index 100%
rename from apps/expo/theme/colors.ts
rename to packages/app/src/theme/colors.ts
diff --git a/apps/expo/theme/index.ts b/packages/app/src/theme/index.ts
similarity index 100%
rename from apps/expo/theme/index.ts
rename to packages/app/src/theme/index.ts
diff --git a/apps/expo/features/trail-conditions/components/ConditionBadge.tsx b/packages/app/src/trail-conditions/components/ConditionBadge.tsx
similarity index 88%
rename from apps/expo/features/trail-conditions/components/ConditionBadge.tsx
rename to packages/app/src/trail-conditions/components/ConditionBadge.tsx
index 63a2621cb7..fe60fe7eb8 100644
--- a/apps/expo/features/trail-conditions/components/ConditionBadge.tsx
+++ b/packages/app/src/trail-conditions/components/ConditionBadge.tsx
@@ -1,7 +1,7 @@
+import { cn } from '@packrat/app/lib/cn';
+import type { OverallCondition } from '@packrat/app/trail-conditions';
import { Text } from '@packrat/ui/nativewindui';
-import { cn } from 'expo-app/lib/cn';
import { View } from 'react-native';
-import type { OverallCondition } from '../types';
interface ConditionBadgeProps {
condition: OverallCondition | string;
diff --git a/apps/expo/features/trail-conditions/components/SubmitConditionReportForm.tsx b/packages/app/src/trail-conditions/components/SubmitConditionReportForm.tsx
similarity index 97%
rename from apps/expo/features/trail-conditions/components/SubmitConditionReportForm.tsx
rename to packages/app/src/trail-conditions/components/SubmitConditionReportForm.tsx
index 6ee4f06fe7..2c061219ea 100644
--- a/apps/expo/features/trail-conditions/components/SubmitConditionReportForm.tsx
+++ b/packages/app/src/trail-conditions/components/SubmitConditionReportForm.tsx
@@ -1,10 +1,14 @@
+import { TextInput } from '@packrat/app/components/TextInput';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
+import type {
+ OverallCondition,
+ TrailSurface,
+ WaterCrossingDifficulty,
+} from '@packrat/app/trail-conditions';
import { Text } from '@packrat/ui/nativewindui';
-import { TextInput } from 'expo-app/components/TextInput';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { useState } from 'react';
import { Alert, KeyboardAvoidingView, Platform, Pressable, ScrollView, View } from 'react-native';
import { useSubmitTrailConditionReport } from '../hooks/useSubmitTrailConditionReport';
-import type { OverallCondition, TrailSurface, WaterCrossingDifficulty } from '../types';
const SURFACE_OPTIONS: TrailSurface[] = ['paved', 'gravel', 'dirt', 'rocky', 'snow', 'mud'];
const CONDITION_OPTIONS: OverallCondition[] = ['excellent', 'good', 'fair', 'poor'];
diff --git a/apps/expo/features/trail-conditions/components/TrailConditionReportCard.tsx b/packages/app/src/trail-conditions/components/TrailConditionReportCard.tsx
similarity index 95%
rename from apps/expo/features/trail-conditions/components/TrailConditionReportCard.tsx
rename to packages/app/src/trail-conditions/components/TrailConditionReportCard.tsx
index 0bb13c7d60..5a0c1c0e3b 100644
--- a/apps/expo/features/trail-conditions/components/TrailConditionReportCard.tsx
+++ b/packages/app/src/trail-conditions/components/TrailConditionReportCard.tsx
@@ -1,7 +1,11 @@
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
+import type {
+ TrailConditionReport,
+ TrailSurface,
+ WaterCrossingDifficulty,
+} from '@packrat/app/trail-conditions';
import { Text } from '@packrat/ui/nativewindui';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { View } from 'react-native';
-import type { TrailConditionReport, TrailSurface, WaterCrossingDifficulty } from '../types';
import { ConditionBadge } from './ConditionBadge';
interface TrailConditionReportCardProps {
diff --git a/apps/expo/features/trail-conditions/components/index.ts b/packages/app/src/trail-conditions/components/index.ts
similarity index 100%
rename from apps/expo/features/trail-conditions/components/index.ts
rename to packages/app/src/trail-conditions/components/index.ts
diff --git a/apps/expo/features/trail-conditions/hooks/index.ts b/packages/app/src/trail-conditions/hooks/index.ts
similarity index 100%
rename from apps/expo/features/trail-conditions/hooks/index.ts
rename to packages/app/src/trail-conditions/hooks/index.ts
diff --git a/apps/expo/features/trail-conditions/hooks/useSubmitTrailConditionReport.ts b/packages/app/src/trail-conditions/hooks/useSubmitTrailConditionReport.ts
similarity index 97%
rename from apps/expo/features/trail-conditions/hooks/useSubmitTrailConditionReport.ts
rename to packages/app/src/trail-conditions/hooks/useSubmitTrailConditionReport.ts
index bcb16fa563..48430b0a4b 100644
--- a/apps/expo/features/trail-conditions/hooks/useSubmitTrailConditionReport.ts
+++ b/packages/app/src/trail-conditions/hooks/useSubmitTrailConditionReport.ts
@@ -1,10 +1,13 @@
+import type {
+ TrailConditionReportInput,
+ TrailConditionReportInStore,
+} from '@packrat/app/trail-conditions';
import { nanoid } from 'nanoid';
import { useCallback, useEffect, useRef, useState } from 'react';
import {
trailConditionReportsStore,
trailConditionReportsSyncState,
} from '../store/trailConditionReports';
-import type { TrailConditionReportInput, TrailConditionReportInStore } from '../types';
interface SubmitOptions {
onSuccess?: (id: string) => void;
diff --git a/apps/expo/features/trail-conditions/hooks/useTrailConditionReports.ts b/packages/app/src/trail-conditions/hooks/useTrailConditionReports.ts
similarity index 93%
rename from apps/expo/features/trail-conditions/hooks/useTrailConditionReports.ts
rename to packages/app/src/trail-conditions/hooks/useTrailConditionReports.ts
index f0887a9dad..5a2420371e 100644
--- a/apps/expo/features/trail-conditions/hooks/useTrailConditionReports.ts
+++ b/packages/app/src/trail-conditions/hooks/useTrailConditionReports.ts
@@ -1,12 +1,12 @@
import { useSelector } from '@legendapp/state/react';
+import { userStore } from '@packrat/app/auth/store/user';
+import { apiClient } from '@packrat/app/lib/api/packrat';
+import { useAuthenticatedQueryToolkit } from '@packrat/app/lib/hooks/useAuthenticatedQueryToolkit';
+import type { TrailConditionReport } from '@packrat/app/trail-conditions';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { useQuery } from '@tanstack/react-query';
-import { userStore } from 'expo-app/features/auth/store/user';
-import { apiClient } from 'expo-app/lib/api/packrat';
-import { useAuthenticatedQueryToolkit } from 'expo-app/lib/hooks/useAuthenticatedQueryToolkit';
import { useEffect, useRef, useState } from 'react';
import { trailConditionReportsStore } from '../store/trailConditionReports';
-import type { TrailConditionReport } from '../types';
const CACHE_KEY_PREFIX = 'trail_condition_reports_cache';
diff --git a/packages/app/src/trail-conditions/index.ts b/packages/app/src/trail-conditions/index.ts
new file mode 100644
index 0000000000..9717724410
--- /dev/null
+++ b/packages/app/src/trail-conditions/index.ts
@@ -0,0 +1,8 @@
+export type {
+ OverallCondition,
+ TrailConditionReport,
+ TrailConditionReportInput,
+ TrailConditionReportInStore,
+ TrailSurface,
+ WaterCrossingDifficulty,
+} from './types';
diff --git a/apps/expo/features/trail-conditions/store/trailConditionReports.ts b/packages/app/src/trail-conditions/store/trailConditionReports.ts
similarity index 93%
rename from apps/expo/features/trail-conditions/store/trailConditionReports.ts
rename to packages/app/src/trail-conditions/store/trailConditionReports.ts
index 0ddc8d5115..7cb4cb5099 100644
--- a/apps/expo/features/trail-conditions/store/trailConditionReports.ts
+++ b/packages/app/src/trail-conditions/store/trailConditionReports.ts
@@ -2,10 +2,10 @@ import { observable, syncState } from '@legendapp/state';
import { syncObservable } from '@legendapp/state/sync';
import { syncedCrud } from '@legendapp/state/sync-plugins/crud';
import { TrailConditionReportSchema } from '@packrat/api/schemas/trailConditions';
-import { isAuthed } from 'expo-app/features/auth/store';
-import { apiClient } from 'expo-app/lib/api/packrat';
-import { persistPlugin } from 'expo-app/lib/persist-plugin';
-import type { TrailConditionReportInStore } from '../types';
+import { isAuthed } from '@packrat/app/auth/store';
+import { apiClient } from '@packrat/app/lib/api/packrat';
+import { persistPlugin } from '@packrat/app/lib/persist-plugin';
+import type { TrailConditionReportInStore } from '@packrat/app/trail-conditions';
const listMyReports = async (_params: unknown, { lastSync }: { lastSync?: number } = {}) => {
const { data, error } = await apiClient['trail-conditions'].mine.get({
diff --git a/apps/expo/features/trail-conditions/types.ts b/packages/app/src/trail-conditions/types.ts
similarity index 100%
rename from apps/expo/features/trail-conditions/types.ts
rename to packages/app/src/trail-conditions/types.ts
diff --git a/apps/expo/features/trips/components/TrailConditionsTile.tsx b/packages/app/src/trips/components/TrailConditionsTile.tsx
similarity index 87%
rename from apps/expo/features/trips/components/TrailConditionsTile.tsx
rename to packages/app/src/trips/components/TrailConditionsTile.tsx
index ca57e1c64f..307b416f9e 100644
--- a/apps/expo/features/trips/components/TrailConditionsTile.tsx
+++ b/packages/app/src/trips/components/TrailConditionsTile.tsx
@@ -1,9 +1,9 @@
+import { Icon } from '@packrat/app/components/Icon';
+import { featureFlags } from '@packrat/app/config';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
import type { AlertMethods } from '@packrat/ui/nativewindui';
import { Alert, ListItem } from '@packrat/ui/nativewindui';
-import { Icon } from 'expo-app/components/Icon';
-import { featureFlags } from 'expo-app/config';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { useRouter } from 'expo-router';
import { useRef } from 'react';
import { View } from 'react-native';
diff --git a/apps/expo/features/trips/components/TripCard.tsx b/packages/app/src/trips/components/TripCard.tsx
similarity index 95%
rename from apps/expo/features/trips/components/TripCard.tsx
rename to packages/app/src/trips/components/TripCard.tsx
index 7a7d6225df..91fcf60de0 100644
--- a/apps/expo/features/trips/components/TripCard.tsx
+++ b/packages/app/src/trips/components/TripCard.tsx
@@ -1,15 +1,15 @@
import { useActionSheet } from '@expo/react-native-action-sheet';
+import { Icon } from '@packrat/app/components/Icon';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
+import { formatLocalDate } from '@packrat/app/lib/utils/dateUtils';
+import type { Trip } from '@packrat/app/trips';
import { Alert, type AlertMethods, Button } from '@packrat/ui/nativewindui';
-import { Icon } from 'expo-app/components/Icon';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
-import { formatLocalDate } from 'expo-app/lib/utils/dateUtils';
import { useRouter } from 'expo-router';
import { useRef } from 'react';
import { Pressable, Text, View } from 'react-native';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { useDeleteTrip } from '../hooks/useDeleteTrip';
-import type { Trip } from '../types';
interface TripCardProps {
trip: Trip;
diff --git a/apps/expo/features/trips/components/TripForm.tsx b/packages/app/src/trips/components/TripForm.tsx
similarity index 97%
rename from apps/expo/features/trips/components/TripForm.tsx
rename to packages/app/src/trips/components/TripForm.tsx
index cd71757cd3..b5cc938826 100644
--- a/apps/expo/features/trips/components/TripForm.tsx
+++ b/packages/app/src/trips/components/TripForm.tsx
@@ -1,14 +1,15 @@
+import { Icon } from '@packrat/app/components/Icon';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
+import { testIds } from '@packrat/app/lib/testIds';
+import { usePacks } from '@packrat/app/packs/hooks/usePacks';
+import type { Trip } from '@packrat/app/trips';
import { assertDefined, isString } from '@packrat/guards';
import { Form, FormItem, FormSection, TextField } from '@packrat/ui/nativewindui';
import DateTimePicker from '@react-native-community/datetimepicker';
import { Picker } from '@react-native-picker/picker';
import { useForm } from '@tanstack/react-form';
import * as Burnt from 'burnt';
-import { Icon } from 'expo-app/components/Icon';
-import { usePacks } from 'expo-app/features/packs/hooks/usePacks';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
-import { testIds } from 'expo-app/lib/testIds';
import { Stack, useRouter } from 'expo-router';
import { useEffect, useMemo, useState } from 'react';
import { Keyboard, Modal, Platform, Pressable, Text, View } from 'react-native';
@@ -17,7 +18,6 @@ import { SafeAreaView, useSafeAreaInsets } from 'react-native-safe-area-context'
import { z } from 'zod';
import { useCreateTrip, useUpdateTrip } from '../hooks';
import { tripLocationStore, useTripLocation } from '../store/tripLocationStore';
-import type { Trip } from '../types';
const tripFormSchema = z
.object({
diff --git a/apps/expo/features/trips/components/UpcomingTripsTile.tsx b/packages/app/src/trips/components/UpcomingTripsTile.tsx
similarity index 91%
rename from apps/expo/features/trips/components/UpcomingTripsTile.tsx
rename to packages/app/src/trips/components/UpcomingTripsTile.tsx
index 00de15c86e..5b77d2dc0a 100644
--- a/apps/expo/features/trips/components/UpcomingTripsTile.tsx
+++ b/packages/app/src/trips/components/UpcomingTripsTile.tsx
@@ -1,10 +1,10 @@
+import { Icon } from '@packrat/app/components/Icon';
+import { featureFlags } from '@packrat/app/config';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
+import { parseLocalDate } from '@packrat/app/lib/utils/dateUtils';
+import { useTrips } from '@packrat/app/trips/hooks';
import type { AlertMethods } from '@packrat/ui/nativewindui';
import { Alert, ListItem, Text, useColorScheme } from '@packrat/ui/nativewindui';
-import { Icon } from 'expo-app/components/Icon';
-import { featureFlags } from 'expo-app/config';
-import { useTrips } from 'expo-app/features/trips/hooks';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
-import { parseLocalDate } from 'expo-app/lib/utils/dateUtils';
import { useRouter } from 'expo-router';
import { useMemo, useRef, useState } from 'react';
import { View } from 'react-native';
diff --git a/apps/expo/features/trips/hooks/index.ts b/packages/app/src/trips/hooks/index.ts
similarity index 100%
rename from apps/expo/features/trips/hooks/index.ts
rename to packages/app/src/trips/hooks/index.ts
diff --git a/apps/expo/features/trips/hooks/useAllTrips.ts b/packages/app/src/trips/hooks/useAllTrips.ts
similarity index 78%
rename from apps/expo/features/trips/hooks/useAllTrips.ts
rename to packages/app/src/trips/hooks/useAllTrips.ts
index 46a24d1561..52d6f19260 100644
--- a/apps/expo/features/trips/hooks/useAllTrips.ts
+++ b/packages/app/src/trips/hooks/useAllTrips.ts
@@ -1,6 +1,6 @@
+import { apiClient } from '@packrat/app/lib/api/packrat';
+import { useAuthenticatedQueryToolkit } from '@packrat/app/lib/hooks/useAuthenticatedQueryToolkit';
import { useQuery } from '@tanstack/react-query';
-import { apiClient } from 'expo-app/lib/api/packrat';
-import { useAuthenticatedQueryToolkit } from 'expo-app/lib/hooks/useAuthenticatedQueryToolkit';
export const fetchAllTrips = async () => {
const { data, error } = await apiClient.trips.get();
if (error) throw new Error(`Failed to fetch trips: ${error.value}`);
diff --git a/apps/expo/features/trips/hooks/useCreateTrip.ts b/packages/app/src/trips/hooks/useCreateTrip.ts
similarity index 75%
rename from apps/expo/features/trips/hooks/useCreateTrip.ts
rename to packages/app/src/trips/hooks/useCreateTrip.ts
index 30cf45edbd..e729d31f72 100644
--- a/apps/expo/features/trips/hooks/useCreateTrip.ts
+++ b/packages/app/src/trips/hooks/useCreateTrip.ts
@@ -1,8 +1,8 @@
-import { tripsStore } from 'expo-app/features/trips/store/trips';
-import { obs } from 'expo-app/lib/store';
+import { obs } from '@packrat/app/lib/store';
+import type { TripInput, TripInStore } from '@packrat/app/trips';
+import { tripsStore } from '@packrat/app/trips/store/trips';
import { nanoid } from 'nanoid';
import { useCallback } from 'react';
-import type { TripInput, TripInStore } from '../types';
// Hook to create a trip
export function useCreateTrip() {
diff --git a/apps/expo/features/trips/hooks/useDeleteTrip.ts b/packages/app/src/trips/hooks/useDeleteTrip.ts
similarity index 73%
rename from apps/expo/features/trips/hooks/useDeleteTrip.ts
rename to packages/app/src/trips/hooks/useDeleteTrip.ts
index b85b73c0af..896d6675ec 100644
--- a/apps/expo/features/trips/hooks/useDeleteTrip.ts
+++ b/packages/app/src/trips/hooks/useDeleteTrip.ts
@@ -1,5 +1,5 @@
-import { tripsStore } from 'expo-app/features/trips/store/trips';
-import { obs } from 'expo-app/lib/store';
+import { obs } from '@packrat/app/lib/store';
+import { tripsStore } from '@packrat/app/trips/store/trips';
import { useCallback } from 'react';
export function useDeleteTrip() {
diff --git a/apps/expo/features/trips/hooks/useTripDetailsFromStore.ts b/packages/app/src/trips/hooks/useTripDetailsFromStore.ts
similarity index 80%
rename from apps/expo/features/trips/hooks/useTripDetailsFromStore.ts
rename to packages/app/src/trips/hooks/useTripDetailsFromStore.ts
index 8c3909b4b7..c79631d347 100644
--- a/apps/expo/features/trips/hooks/useTripDetailsFromStore.ts
+++ b/packages/app/src/trips/hooks/useTripDetailsFromStore.ts
@@ -1,6 +1,6 @@
import { use$ } from '@legendapp/state/react';
-import { tripsStore } from 'expo-app/features/trips/store/trips';
-import { obs } from 'expo-app/lib/store';
+import { obs } from '@packrat/app/lib/store';
+import { tripsStore } from '@packrat/app/trips/store/trips';
/**
* Retrieves a trip from the store.
diff --git a/apps/expo/features/trips/hooks/useTrips.ts b/packages/app/src/trips/hooks/useTrips.ts
similarity index 100%
rename from apps/expo/features/trips/hooks/useTrips.ts
rename to packages/app/src/trips/hooks/useTrips.ts
diff --git a/apps/expo/features/trips/hooks/useUpdateTrip.ts b/packages/app/src/trips/hooks/useUpdateTrip.ts
similarity index 62%
rename from apps/expo/features/trips/hooks/useUpdateTrip.ts
rename to packages/app/src/trips/hooks/useUpdateTrip.ts
index 47c8d8b3e8..98b48a4fc0 100644
--- a/apps/expo/features/trips/hooks/useUpdateTrip.ts
+++ b/packages/app/src/trips/hooks/useUpdateTrip.ts
@@ -1,7 +1,7 @@
-import { tripsStore } from 'expo-app/features/trips/store/trips';
-import { obs } from 'expo-app/lib/store';
+import { obs } from '@packrat/app/lib/store';
+import type { Trip } from '@packrat/app/trips';
+import { tripsStore } from '@packrat/app/trips/store/trips';
import { useCallback } from 'react';
-import type { Trip } from '../types';
export function useUpdateTrip() {
const updateTrip = useCallback((trip: Trip) => {
diff --git a/packages/app/src/trips/index.ts b/packages/app/src/trips/index.ts
new file mode 100644
index 0000000000..03473f3154
--- /dev/null
+++ b/packages/app/src/trips/index.ts
@@ -0,0 +1 @@
+export type { Trip, TripInput, TripInStore, TripStatus } from './types';
diff --git a/apps/expo/features/trips/screens/CreateTripScreen.tsx b/packages/app/src/trips/screens/CreateTripScreen.tsx
similarity index 100%
rename from apps/expo/features/trips/screens/CreateTripScreen.tsx
rename to packages/app/src/trips/screens/CreateTripScreen.tsx
diff --git a/apps/expo/features/trips/screens/EditTripScreen.tsx b/packages/app/src/trips/screens/EditTripScreen.tsx
similarity index 89%
rename from apps/expo/features/trips/screens/EditTripScreen.tsx
rename to packages/app/src/trips/screens/EditTripScreen.tsx
index bc53d83aa6..f17c381d6d 100644
--- a/apps/expo/features/trips/screens/EditTripScreen.tsx
+++ b/packages/app/src/trips/screens/EditTripScreen.tsx
@@ -1,7 +1,7 @@
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
+import { useTripDetailsFromStore } from '@packrat/app/trips/hooks/useTripDetailsFromStore';
import { assertDefined } from '@packrat/guards';
import { ActivityIndicator } from '@packrat/ui/nativewindui';
-import { useTripDetailsFromStore } from 'expo-app/features/trips/hooks/useTripDetailsFromStore';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { useLocalSearchParams, useRouter } from 'expo-router';
import { useEffect, useState } from 'react';
import { Pressable, SafeAreaView, Text } from 'react-native';
diff --git a/apps/expo/features/trips/screens/TripDetailScreen.tsx b/packages/app/src/trips/screens/TripDetailScreen.tsx
similarity index 95%
rename from apps/expo/features/trips/screens/TripDetailScreen.tsx
rename to packages/app/src/trips/screens/TripDetailScreen.tsx
index 8bc6a0f39b..32cbbb5349 100644
--- a/apps/expo/features/trips/screens/TripDetailScreen.tsx
+++ b/packages/app/src/trips/screens/TripDetailScreen.tsx
@@ -1,11 +1,12 @@
+import { Icon } from '@packrat/app/components/Icon';
+import { featureFlags } from '@packrat/app/config';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
+import { testIds } from '@packrat/app/lib/testIds';
+import { SubmitConditionReportForm } from '@packrat/app/trail-conditions/components/SubmitConditionReportForm';
+import type { Trip } from '@packrat/app/trips';
import { assertDefined } from '@packrat/guards';
import { ActivityIndicator, Button, Card, Text } from '@packrat/ui/nativewindui';
-import { Icon } from 'expo-app/components/Icon';
-import { featureFlags } from 'expo-app/config';
-import { SubmitConditionReportForm } from 'expo-app/features/trail-conditions/components/SubmitConditionReportForm';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
-import { testIds } from 'expo-app/lib/testIds';
import { useLocalSearchParams, useRouter } from 'expo-router';
import { useMemo, useState } from 'react';
import { Modal, ScrollView, Share, View } from 'react-native';
@@ -13,7 +14,6 @@ import MapView, { Marker } from 'react-native-maps';
import { SafeAreaView } from 'react-native-safe-area-context';
import { useDetailedPacks } from '../../packs/hooks/useDetailedPacks';
import { useTripDetailsFromStore } from '../hooks/useTripDetailsFromStore';
-import type { Trip } from '../types';
export function TripDetailScreen() {
const router = useRouter();
diff --git a/apps/expo/features/trips/screens/TripListScreen.tsx b/packages/app/src/trips/screens/TripListScreen.tsx
similarity index 91%
rename from apps/expo/features/trips/screens/TripListScreen.tsx
rename to packages/app/src/trips/screens/TripListScreen.tsx
index ea2efee01d..18a190e5f8 100644
--- a/apps/expo/features/trips/screens/TripListScreen.tsx
+++ b/packages/app/src/trips/screens/TripListScreen.tsx
@@ -1,18 +1,18 @@
+import { AndroidTabBarInsetFix } from '@packrat/app/components/AndroidTabBarInsetFix';
+import { Icon } from '@packrat/app/components/Icon';
+import { LargeTitleHeaderSearchContentContainer } from '@packrat/app/components/LargeTitleHeaderSearchContentContainer';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
+import { testIds } from '@packrat/app/lib/testIds';
+import { asNonNullableRef } from '@packrat/app/lib/utils/asNonNullableRef';
+import type { Trip } from '@packrat/app/trips';
import { LargeTitleHeader, type LargeTitleSearchBarMethods } from '@packrat/ui/nativewindui';
-import { AndroidTabBarInsetFix } from 'expo-app/components/AndroidTabBarInsetFix';
-import { Icon } from 'expo-app/components/Icon';
-import { LargeTitleHeaderSearchContentContainer } from 'expo-app/components/LargeTitleHeaderSearchContentContainer';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
-import { testIds } from 'expo-app/lib/testIds';
-import { asNonNullableRef } from 'expo-app/lib/utils/asNonNullableRef';
import { Link, useRouter } from 'expo-router';
import { useCallback, useMemo, useRef, useState } from 'react';
import { FlatList, Pressable, ScrollView, Text, TouchableOpacity, View } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { TripCard } from '../components/TripCard';
import { useTrips } from '../hooks';
-import type { Trip } from '../types';
function TrailConditionsBanner() {
const { colors } = useColorScheme();
diff --git a/apps/expo/features/trips/screens/TripWeatherDetailsScreen.tsx b/packages/app/src/trips/screens/TripWeatherDetailsScreen.tsx
similarity index 96%
rename from apps/expo/features/trips/screens/TripWeatherDetailsScreen.tsx
rename to packages/app/src/trips/screens/TripWeatherDetailsScreen.tsx
index bddef8c413..81bae81919 100644
--- a/apps/expo/features/trips/screens/TripWeatherDetailsScreen.tsx
+++ b/packages/app/src/trips/screens/TripWeatherDetailsScreen.tsx
@@ -1,11 +1,11 @@
import type { WeatherAPIForecastResponse } from '@packrat/api/schemas/weather';
-import { Icon } from 'expo-app/components/Icon';
-import { WeatherForecast } from 'expo-app/features/weather/components/WeatherForecast';
+import { Icon } from '@packrat/app/components/Icon';
+import { WeatherForecast } from '@packrat/app/weather/components/WeatherForecast';
import {
getWeatherBackgroundColors,
getWeatherData,
searchLocationsByCoordinates,
-} from 'expo-app/features/weather/lib/weatherService';
+} from '@packrat/app/weather/lib/weatherService';
import { LinearGradient } from 'expo-linear-gradient';
import { router, Stack, useLocalSearchParams } from 'expo-router';
import { useEffect, useState } from 'react';
diff --git a/apps/expo/features/trips/store/tripLocationStore.ts b/packages/app/src/trips/store/tripLocationStore.ts
similarity index 100%
rename from apps/expo/features/trips/store/tripLocationStore.ts
rename to packages/app/src/trips/store/tripLocationStore.ts
diff --git a/apps/expo/features/trips/store/trips.ts b/packages/app/src/trips/store/trips.ts
similarity index 94%
rename from apps/expo/features/trips/store/trips.ts
rename to packages/app/src/trips/store/trips.ts
index 17262b42b6..6a2fb4d33b 100644
--- a/apps/expo/features/trips/store/trips.ts
+++ b/packages/app/src/trips/store/trips.ts
@@ -2,10 +2,10 @@ import { observable, syncState } from '@legendapp/state';
import { syncObservable } from '@legendapp/state/sync';
import { syncedCrud } from '@legendapp/state/sync-plugins/crud';
import { TripSchema } from '@packrat/api/schemas/trips';
-import { isAuthed } from 'expo-app/features/auth/store';
-import { apiClient } from 'expo-app/lib/api/packrat';
-import { persistPlugin } from 'expo-app/lib/persist-plugin';
-import type { TripInStore } from '../types';
+import { isAuthed } from '@packrat/app/auth/store';
+import { apiClient } from '@packrat/app/lib/api/packrat';
+import { persistPlugin } from '@packrat/app/lib/persist-plugin';
+import type { TripInStore } from '@packrat/app/trips';
let _refreshTripsList: (() => void) | undefined;
export const refreshTripsList = () => _refreshTripsList?.();
diff --git a/apps/expo/features/trips/tripListAtoms.tsx b/packages/app/src/trips/tripListAtoms.tsx
similarity index 100%
rename from apps/expo/features/trips/tripListAtoms.tsx
rename to packages/app/src/trips/tripListAtoms.tsx
diff --git a/apps/expo/features/trips/types.ts b/packages/app/src/trips/types.ts
similarity index 92%
rename from apps/expo/features/trips/types.ts
rename to packages/app/src/trips/types.ts
index 2d59bb5b50..31256b37df 100644
--- a/apps/expo/features/trips/types.ts
+++ b/packages/app/src/trips/types.ts
@@ -1,4 +1,4 @@
-import type { Pack } from 'expo-app/features/packs/types';
+import type { Pack } from '../packs/types';
export type TripStatus = 'planned' | 'ongoing' | 'completed' | 'cancelled';
diff --git a/apps/expo/features/trips/utils/getTripDetailOptions.tsx b/packages/app/src/trips/utils/getTripDetailOptions.tsx
similarity index 91%
rename from apps/expo/features/trips/utils/getTripDetailOptions.tsx
rename to packages/app/src/trips/utils/getTripDetailOptions.tsx
index b971c8327b..bb84157bda 100644
--- a/apps/expo/features/trips/utils/getTripDetailOptions.tsx
+++ b/packages/app/src/trips/utils/getTripDetailOptions.tsx
@@ -1,7 +1,7 @@
+import { Icon } from '@packrat/app/components/Icon';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
+import { t } from '@packrat/app/lib/i18n';
import { Alert, Button, useColorScheme } from '@packrat/ui/nativewindui';
-import { Icon } from 'expo-app/components/Icon';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
-import { t } from 'expo-app/lib/i18n';
import { useRouter } from 'expo-router';
import { View } from 'react-native';
import { useDeleteTrip } from '../hooks';
diff --git a/apps/expo/types/global.d.ts b/packages/app/src/types/global.d.ts
similarity index 100%
rename from apps/expo/types/global.d.ts
rename to packages/app/src/types/global.d.ts
diff --git a/apps/expo/types/index.ts b/packages/app/src/types/index.ts
similarity index 100%
rename from apps/expo/types/index.ts
rename to packages/app/src/types/index.ts
diff --git a/apps/expo/utils/__tests__/chatContextHelpers.test.ts b/packages/app/src/utils/__tests__/chatContextHelpers.test.ts
similarity index 100%
rename from apps/expo/utils/__tests__/chatContextHelpers.test.ts
rename to packages/app/src/utils/__tests__/chatContextHelpers.test.ts
diff --git a/apps/expo/utils/__tests__/format-ai-response.test.ts b/packages/app/src/utils/__tests__/format-ai-response.test.ts
similarity index 100%
rename from apps/expo/utils/__tests__/format-ai-response.test.ts
rename to packages/app/src/utils/__tests__/format-ai-response.test.ts
diff --git a/apps/expo/utils/__tests__/storage.test.ts b/packages/app/src/utils/__tests__/storage.test.ts
similarity index 99%
rename from apps/expo/utils/__tests__/storage.test.ts
rename to packages/app/src/utils/__tests__/storage.test.ts
index 517d4ca838..014928d39c 100644
--- a/apps/expo/utils/__tests__/storage.test.ts
+++ b/packages/app/src/utils/__tests__/storage.test.ts
@@ -14,8 +14,8 @@ vi.mock('jotai/utils', () => ({
createJSONStorage: vi.fn((storageFunction) => storageFunction()),
}));
+import type { WeatherLocation } from '@packrat/app/weather/types';
import AsyncStorage from '@react-native-async-storage/async-storage';
-import type { WeatherLocation } from 'expo-app/features/weather/types';
import { asyncStorage } from '../storage';
describe('storage', () => {
diff --git a/apps/expo/utils/__tests__/weight.test.ts b/packages/app/src/utils/__tests__/weight.test.ts
similarity index 98%
rename from apps/expo/utils/__tests__/weight.test.ts
rename to packages/app/src/utils/__tests__/weight.test.ts
index d53da50a98..a01d878b8a 100644
--- a/apps/expo/utils/__tests__/weight.test.ts
+++ b/packages/app/src/utils/__tests__/weight.test.ts
@@ -1,4 +1,4 @@
-import type { PackItem } from 'expo-app/types';
+import type { PackItem } from '@packrat/app/types';
import { describe, expect, it } from 'vitest';
import { calculateBaseWeight, calculateTotalWeight, convertWeight, formatWeight } from '../weight';
diff --git a/apps/expo/utils/chatContextHelpers.ts b/packages/app/src/utils/chatContextHelpers.ts
similarity index 100%
rename from apps/expo/utils/chatContextHelpers.ts
rename to packages/app/src/utils/chatContextHelpers.ts
diff --git a/apps/expo/utils/format-ai-response.ts b/packages/app/src/utils/format-ai-response.ts
similarity index 100%
rename from apps/expo/utils/format-ai-response.ts
rename to packages/app/src/utils/format-ai-response.ts
diff --git a/apps/expo/utils/polyfills.ts b/packages/app/src/utils/polyfills.ts
similarity index 100%
rename from apps/expo/utils/polyfills.ts
rename to packages/app/src/utils/polyfills.ts
diff --git a/apps/expo/utils/storage.ts b/packages/app/src/utils/storage.ts
similarity index 89%
rename from apps/expo/utils/storage.ts
rename to packages/app/src/utils/storage.ts
index abbab9a7da..f7b52ffbc3 100644
--- a/apps/expo/utils/storage.ts
+++ b/packages/app/src/utils/storage.ts
@@ -1,5 +1,5 @@
+import type { WeatherLocation } from '@packrat/app/weather/types';
import AsyncStorage from '@react-native-async-storage/async-storage';
-import type { WeatherLocation } from 'expo-app/features/weather/types';
import { createJSONStorage } from 'jotai/utils';
// Create a storage adapter for Jotai that uses AsyncStorage
diff --git a/apps/expo/utils/weight.ts b/packages/app/src/utils/weight.ts
similarity index 95%
rename from apps/expo/utils/weight.ts
rename to packages/app/src/utils/weight.ts
index 771bbba3a3..851833d870 100644
--- a/apps/expo/utils/weight.ts
+++ b/packages/app/src/utils/weight.ts
@@ -1,4 +1,4 @@
-import type { PackItem, WeightUnit } from 'expo-app/types';
+import type { PackItem, WeightUnit } from '@packrat/app/types';
// Convert weight between units
export const convertWeight = (weight: number, from: WeightUnit, to: WeightUnit): number => {
diff --git a/apps/expo/features/weather/atoms/locationsAtoms.ts b/packages/app/src/weather/atoms/locationsAtoms.ts
similarity index 95%
rename from apps/expo/features/weather/atoms/locationsAtoms.ts
rename to packages/app/src/weather/atoms/locationsAtoms.ts
index 52231d8c15..06d1372317 100644
--- a/apps/expo/features/weather/atoms/locationsAtoms.ts
+++ b/packages/app/src/weather/atoms/locationsAtoms.ts
@@ -1,4 +1,4 @@
-import { asyncStorage } from 'expo-app/utils/storage';
+import { asyncStorage } from '@packrat/app/utils/storage';
import { atom } from 'jotai';
import { atomWithStorage, loadable } from 'jotai/utils';
import type { WeatherLocation } from '../types';
diff --git a/apps/expo/features/weather/components/LocationCard.tsx b/packages/app/src/weather/components/LocationCard.tsx
similarity index 96%
rename from apps/expo/features/weather/components/LocationCard.tsx
rename to packages/app/src/weather/components/LocationCard.tsx
index 3a83c9eb6e..3a3d78b8ba 100644
--- a/apps/expo/features/weather/components/LocationCard.tsx
+++ b/packages/app/src/weather/components/LocationCard.tsx
@@ -1,8 +1,8 @@
import { useActionSheet } from '@expo/react-native-action-sheet';
+import { cn } from '@packrat/app/lib/cn';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
import { Text } from '@packrat/ui/nativewindui';
-import { cn } from 'expo-app/lib/cn';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { Pressable, View } from 'react-native';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import type { WeatherLocation } from '../types';
diff --git a/apps/expo/features/weather/components/LocationPicker.tsx b/packages/app/src/weather/components/LocationPicker.tsx
similarity index 96%
rename from apps/expo/features/weather/components/LocationPicker.tsx
rename to packages/app/src/weather/components/LocationPicker.tsx
index b0f250245d..810857d307 100644
--- a/apps/expo/features/weather/components/LocationPicker.tsx
+++ b/packages/app/src/weather/components/LocationPicker.tsx
@@ -1,9 +1,9 @@
+import { Icon } from '@packrat/app/components/Icon';
+import { cn } from '@packrat/app/lib/cn';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
import { assertNonNull } from '@packrat/guards';
import { Button, Text } from '@packrat/ui/nativewindui';
-import { Icon } from 'expo-app/components/Icon';
-import { cn } from 'expo-app/lib/cn';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { useRouter } from 'expo-router';
import { useState } from 'react';
import { Modal, Pressable, ScrollView, TouchableOpacity, View } from 'react-native';
diff --git a/apps/expo/features/weather/components/WeatherAlertsTile.tsx b/packages/app/src/weather/components/WeatherAlertsTile.tsx
similarity index 91%
rename from apps/expo/features/weather/components/WeatherAlertsTile.tsx
rename to packages/app/src/weather/components/WeatherAlertsTile.tsx
index c3f42bfd1f..372af15cfe 100644
--- a/apps/expo/features/weather/components/WeatherAlertsTile.tsx
+++ b/packages/app/src/weather/components/WeatherAlertsTile.tsx
@@ -1,8 +1,8 @@
+import { Icon } from '@packrat/app/components/Icon';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
import type { AlertMethods } from '@packrat/ui/nativewindui';
import { Alert, ListItem, Text } from '@packrat/ui/nativewindui';
-import { Icon } from 'expo-app/components/Icon';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { useRouter } from 'expo-router';
import { useRef } from 'react';
import { Platform, View } from 'react-native';
diff --git a/apps/expo/features/weather/components/WeatherAuthWall.tsx b/packages/app/src/weather/components/WeatherAuthWall.tsx
similarity index 91%
rename from apps/expo/features/weather/components/WeatherAuthWall.tsx
rename to packages/app/src/weather/components/WeatherAuthWall.tsx
index 5df04b2bd5..f33f3ebb51 100644
--- a/apps/expo/features/weather/components/WeatherAuthWall.tsx
+++ b/packages/app/src/weather/components/WeatherAuthWall.tsx
@@ -1,11 +1,11 @@
+import { Icon, type MaterialIconName } from '@packrat/app/components/Icon';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
import { Button, Text } from '@packrat/ui/nativewindui';
-import { Icon, type MaterialIconName } from 'expo-app/components/Icon';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { Stack, usePathname, useRouter } from 'expo-router';
import { Image, View } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
-const LOGO_SOURCE = require('expo-app/assets/packrat-app-icon-gradient.png');
+const LOGO_SOURCE = require('@packrat/app/assets/packrat-app-icon-gradient.png');
export function WeatherAuthWall() {
const router = useRouter();
diff --git a/apps/expo/features/weather/components/WeatherForecast.tsx b/packages/app/src/weather/components/WeatherForecast.tsx
similarity index 96%
rename from apps/expo/features/weather/components/WeatherForecast.tsx
rename to packages/app/src/weather/components/WeatherForecast.tsx
index 54337d0225..25b44e6f08 100644
--- a/apps/expo/features/weather/components/WeatherForecast.tsx
+++ b/packages/app/src/weather/components/WeatherForecast.tsx
@@ -1,7 +1,7 @@
+import { Icon } from '@packrat/app/components/Icon';
+import { cn } from '@packrat/app/lib/cn';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
import { Text } from '@packrat/ui/nativewindui';
-import { Icon } from 'expo-app/components/Icon';
-import { cn } from 'expo-app/lib/cn';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { ScrollView, View } from 'react-native';
import { WeatherIcon } from './WeatherIcon';
diff --git a/apps/expo/features/weather/components/WeatherIcon.tsx b/packages/app/src/weather/components/WeatherIcon.tsx
similarity index 92%
rename from apps/expo/features/weather/components/WeatherIcon.tsx
rename to packages/app/src/weather/components/WeatherIcon.tsx
index 7cdf6ec53a..94c4abd2f7 100644
--- a/apps/expo/features/weather/components/WeatherIcon.tsx
+++ b/packages/app/src/weather/components/WeatherIcon.tsx
@@ -1,4 +1,4 @@
-import { Icon, type MaterialIconName } from 'expo-app/components/Icon';
+import { Icon, type MaterialIconName } from '@packrat/app/components/Icon';
import { View } from 'react-native';
import { getWeatherIconByCondition, getWeatherIconName } from '../lib/weatherIcons';
diff --git a/apps/expo/features/weather/components/WeatherTile.tsx b/packages/app/src/weather/components/WeatherTile.tsx
similarity index 90%
rename from apps/expo/features/weather/components/WeatherTile.tsx
rename to packages/app/src/weather/components/WeatherTile.tsx
index 510736bdd5..9a5e5fb6b5 100644
--- a/apps/expo/features/weather/components/WeatherTile.tsx
+++ b/packages/app/src/weather/components/WeatherTile.tsx
@@ -1,7 +1,7 @@
+import { Icon } from '@packrat/app/components/Icon';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
import { ListItem, Text } from '@packrat/ui/nativewindui';
-import { Icon } from 'expo-app/components/Icon';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { router } from 'expo-router';
import { Platform, View } from 'react-native';
import { useActiveLocation } from '../hooks';
diff --git a/apps/expo/features/weather/components/index.ts b/packages/app/src/weather/components/index.ts
similarity index 100%
rename from apps/expo/features/weather/components/index.ts
rename to packages/app/src/weather/components/index.ts
diff --git a/apps/expo/features/weather/hooks/index.ts b/packages/app/src/weather/hooks/index.ts
similarity index 100%
rename from apps/expo/features/weather/hooks/index.ts
rename to packages/app/src/weather/hooks/index.ts
diff --git a/apps/expo/features/weather/hooks/useActiveLocation.ts b/packages/app/src/weather/hooks/useActiveLocation.ts
similarity index 100%
rename from apps/expo/features/weather/hooks/useActiveLocation.ts
rename to packages/app/src/weather/hooks/useActiveLocation.ts
diff --git a/apps/expo/features/weather/hooks/useLocationRefresh.ts b/packages/app/src/weather/hooks/useLocationRefresh.ts
similarity index 95%
rename from apps/expo/features/weather/hooks/useLocationRefresh.ts
rename to packages/app/src/weather/hooks/useLocationRefresh.ts
index c9ad3a8e92..24c7887420 100644
--- a/apps/expo/features/weather/hooks/useLocationRefresh.ts
+++ b/packages/app/src/weather/hooks/useLocationRefresh.ts
@@ -1,4 +1,4 @@
-import { formatWeatherData, getWeatherData } from 'expo-app/features/weather/lib/weatherService';
+import { formatWeatherData, getWeatherData } from '@packrat/app/weather/lib/weatherService';
import { useState } from 'react';
import type { WeatherLocation } from '../types';
import { useLocations } from './useLocations';
diff --git a/apps/expo/features/weather/hooks/useLocationSearch.ts b/packages/app/src/weather/hooks/useLocationSearch.ts
similarity index 97%
rename from apps/expo/features/weather/hooks/useLocationSearch.ts
rename to packages/app/src/weather/hooks/useLocationSearch.ts
index 36574bf651..059fd52855 100644
--- a/apps/expo/features/weather/hooks/useLocationSearch.ts
+++ b/packages/app/src/weather/hooks/useLocationSearch.ts
@@ -3,7 +3,7 @@ import {
getWeatherData,
searchLocations,
searchLocationsByCoordinates,
-} from 'expo-app/features/weather/lib/weatherService';
+} from '@packrat/app/weather/lib/weatherService';
import { useState } from 'react';
import type { LocationSearchResult, WeatherLocation } from '../types';
import { useLocations } from './useLocations';
diff --git a/apps/expo/features/weather/hooks/useLocations.ts b/packages/app/src/weather/hooks/useLocations.ts
similarity index 100%
rename from apps/expo/features/weather/hooks/useLocations.ts
rename to packages/app/src/weather/hooks/useLocations.ts
diff --git a/apps/expo/features/weather/hooks/useWeatherAlert.ts b/packages/app/src/weather/hooks/useWeatherAlert.ts
similarity index 98%
rename from apps/expo/features/weather/hooks/useWeatherAlert.ts
rename to packages/app/src/weather/hooks/useWeatherAlert.ts
index 1a462f2ad2..ec7913366f 100644
--- a/apps/expo/features/weather/hooks/useWeatherAlert.ts
+++ b/packages/app/src/weather/hooks/useWeatherAlert.ts
@@ -1,4 +1,4 @@
-import { getWeatherData } from 'expo-app/features/weather/lib/weatherService';
+import { getWeatherData } from '@packrat/app/weather/lib/weatherService';
import { useAtomValue } from 'jotai';
import { useEffect, useState } from 'react';
import { activeLocationAtom } from '../atoms/locationsAtoms';
diff --git a/apps/expo/features/weather/lib/weatherIcons.ts b/packages/app/src/weather/lib/weatherIcons.ts
similarity index 98%
rename from apps/expo/features/weather/lib/weatherIcons.ts
rename to packages/app/src/weather/lib/weatherIcons.ts
index 29f986d0a7..c5aea4e48e 100644
--- a/apps/expo/features/weather/lib/weatherIcons.ts
+++ b/packages/app/src/weather/lib/weatherIcons.ts
@@ -1,4 +1,4 @@
-import type { MaterialIconName } from 'expo-app/components/Icon';
+import type { MaterialIconName } from '@packrat/app/components/Icon';
/**
* Maps weather condition codes to available icons
diff --git a/apps/expo/features/weather/lib/weatherService.ts b/packages/app/src/weather/lib/weatherService.ts
similarity index 99%
rename from apps/expo/features/weather/lib/weatherService.ts
rename to packages/app/src/weather/lib/weatherService.ts
index 836e07f111..c4e192b43d 100644
--- a/apps/expo/features/weather/lib/weatherService.ts
+++ b/packages/app/src/weather/lib/weatherService.ts
@@ -3,8 +3,8 @@ import {
type WeatherAPIForecastResponse,
WeatherAPIForecastResponseSchema,
} from '@packrat/api/schemas/weather';
+import { apiClient } from '@packrat/app/lib/api/packrat';
import { assertDefined } from '@packrat/guards';
-import { apiClient } from 'expo-app/lib/api/packrat';
import { getWeatherIconName as getIconNameFromCode } from './weatherIcons';
/**
diff --git a/apps/expo/features/weather/screens/LocationDetailScreen.tsx b/packages/app/src/weather/screens/LocationDetailScreen.tsx
similarity index 97%
rename from apps/expo/features/weather/screens/LocationDetailScreen.tsx
rename to packages/app/src/weather/screens/LocationDetailScreen.tsx
index 7fd6bf7319..96d6e07277 100644
--- a/apps/expo/features/weather/screens/LocationDetailScreen.tsx
+++ b/packages/app/src/weather/screens/LocationDetailScreen.tsx
@@ -1,9 +1,9 @@
import { useActionSheet } from '@expo/react-native-action-sheet';
+import { Icon } from '@packrat/app/components/Icon';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
+import { getWeatherBackgroundColors } from '@packrat/app/weather/lib/weatherService';
import { Text } from '@packrat/ui/nativewindui';
-import { Icon } from 'expo-app/components/Icon';
-import { getWeatherBackgroundColors } from 'expo-app/features/weather/lib/weatherService';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { LinearGradient } from 'expo-linear-gradient';
import { router, Stack, useLocalSearchParams } from 'expo-router';
import { useEffect, useState } from 'react';
diff --git a/apps/expo/features/weather/screens/LocationPreviewScreen.tsx b/packages/app/src/weather/screens/LocationPreviewScreen.tsx
similarity index 97%
rename from apps/expo/features/weather/screens/LocationPreviewScreen.tsx
rename to packages/app/src/weather/screens/LocationPreviewScreen.tsx
index b3ea514a48..4a3714edba 100644
--- a/apps/expo/features/weather/screens/LocationPreviewScreen.tsx
+++ b/packages/app/src/weather/screens/LocationPreviewScreen.tsx
@@ -1,13 +1,13 @@
-import { Text } from '@packrat/ui/nativewindui';
-import { Icon } from 'expo-app/components/Icon';
+import { Icon } from '@packrat/app/components/Icon';
+import { cn } from '@packrat/app/lib/cn';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
import {
formatWeatherData,
getWeatherBackgroundColors,
getWeatherData,
-} from 'expo-app/features/weather/lib/weatherService';
-import { cn } from 'expo-app/lib/cn';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
-// import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
+} from '@packrat/app/weather/lib/weatherService';
+import { Text } from '@packrat/ui/nativewindui';
+// import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
import { LinearGradient } from 'expo-linear-gradient';
import { router, Stack, useLocalSearchParams } from 'expo-router';
import { useEffect, useState } from 'react';
diff --git a/apps/expo/features/weather/screens/LocationSearchScreen.tsx b/packages/app/src/weather/screens/LocationSearchScreen.tsx
similarity index 97%
rename from apps/expo/features/weather/screens/LocationSearchScreen.tsx
rename to packages/app/src/weather/screens/LocationSearchScreen.tsx
index 43822e5695..1674b01df4 100644
--- a/apps/expo/features/weather/screens/LocationSearchScreen.tsx
+++ b/packages/app/src/weather/screens/LocationSearchScreen.tsx
@@ -1,10 +1,10 @@
+import { Icon } from '@packrat/app/components/Icon';
+import { SearchInput } from '@packrat/app/components/SearchInput';
+import { cn } from '@packrat/app/lib/cn';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
import { Text } from '@packrat/ui/nativewindui';
import AsyncStorage from '@react-native-async-storage/async-storage';
-import { Icon } from 'expo-app/components/Icon';
-import { SearchInput } from 'expo-app/components/SearchInput';
-import { cn } from 'expo-app/lib/cn';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import * as Location from 'expo-location';
import { router } from 'expo-router';
import { useEffect, useRef, useState } from 'react';
diff --git a/apps/expo/features/weather/screens/LocationsScreen.tsx b/packages/app/src/weather/screens/LocationsScreen.tsx
similarity index 95%
rename from apps/expo/features/weather/screens/LocationsScreen.tsx
rename to packages/app/src/weather/screens/LocationsScreen.tsx
index 1c48d033b2..5819c9984d 100644
--- a/apps/expo/features/weather/screens/LocationsScreen.tsx
+++ b/packages/app/src/weather/screens/LocationsScreen.tsx
@@ -1,10 +1,10 @@
+import { withAuthWall } from '@packrat/app/auth/hocs';
+import { Icon } from '@packrat/app/components/Icon';
+import { LargeTitleHeaderOverlapFixIOS } from '@packrat/app/components/LargeTitleHeaderOverlapFixIOS';
+import { SearchInput } from '@packrat/app/components/SearchInput';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
import { Button, LargeTitleHeader, Text } from '@packrat/ui/nativewindui';
-import { Icon } from 'expo-app/components/Icon';
-import { LargeTitleHeaderOverlapFixIOS } from 'expo-app/components/LargeTitleHeaderOverlapFixIOS';
-import { SearchInput } from 'expo-app/components/SearchInput';
-import { withAuthWall } from 'expo-app/features/auth/hocs';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { router, useNavigation } from 'expo-router';
import { useAtom } from 'jotai';
import { useCallback, useEffect, useRef, useState } from 'react';
diff --git a/apps/expo/features/weather/screens/index.ts b/packages/app/src/weather/screens/index.ts
similarity index 100%
rename from apps/expo/features/weather/screens/index.ts
rename to packages/app/src/weather/screens/index.ts
diff --git a/apps/expo/features/weather/types.ts b/packages/app/src/weather/types.ts
similarity index 98%
rename from apps/expo/features/weather/types.ts
rename to packages/app/src/weather/types.ts
index 42ba85b31c..2e57849f8a 100644
--- a/apps/expo/features/weather/types.ts
+++ b/packages/app/src/weather/types.ts
@@ -1,4 +1,4 @@
-import type { MaterialIconName } from 'expo-app/components/Icon';
+import type { MaterialIconName } from '@packrat/app/components/Icon';
export interface WeatherApiForecastResponse {
location: Location;
diff --git a/apps/expo/features/wildlife/atoms/wildlifeAtoms.ts b/packages/app/src/wildlife/atoms/wildlifeAtoms.ts
similarity index 88%
rename from apps/expo/features/wildlife/atoms/wildlifeAtoms.ts
rename to packages/app/src/wildlife/atoms/wildlifeAtoms.ts
index 7c53db4725..4d5421a91c 100644
--- a/apps/expo/features/wildlife/atoms/wildlifeAtoms.ts
+++ b/packages/app/src/wildlife/atoms/wildlifeAtoms.ts
@@ -1,6 +1,6 @@
+import type { WildlifeIdentification } from '@packrat/app/wildlife';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { atomWithStorage, createJSONStorage, loadable } from 'jotai/utils';
-import type { WildlifeIdentification } from '../types';
// createJSONStorage handles JSON.parse/stringify internally; pass AsyncStorage directly.
const wildlifeStorage = createJSONStorage(() => AsyncStorage);
diff --git a/apps/expo/features/wildlife/components/SpeciesCard.tsx b/packages/app/src/wildlife/components/SpeciesCard.tsx
similarity index 94%
rename from apps/expo/features/wildlife/components/SpeciesCard.tsx
rename to packages/app/src/wildlife/components/SpeciesCard.tsx
index f186438533..e6631f7621 100644
--- a/apps/expo/features/wildlife/components/SpeciesCard.tsx
+++ b/packages/app/src/wildlife/components/SpeciesCard.tsx
@@ -1,7 +1,7 @@
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
+import type { IdentificationResult } from '@packrat/app/wildlife';
import { Text } from '@packrat/ui/nativewindui';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { Pressable, View } from 'react-native';
-import type { IdentificationResult } from '../types';
interface SpeciesCardProps {
result: IdentificationResult;
diff --git a/apps/expo/features/wildlife/components/WildlifeTile.tsx b/packages/app/src/wildlife/components/WildlifeTile.tsx
similarity index 85%
rename from apps/expo/features/wildlife/components/WildlifeTile.tsx
rename to packages/app/src/wildlife/components/WildlifeTile.tsx
index c155d610de..74f903b9eb 100644
--- a/apps/expo/features/wildlife/components/WildlifeTile.tsx
+++ b/packages/app/src/wildlife/components/WildlifeTile.tsx
@@ -1,7 +1,7 @@
+import { Icon } from '@packrat/app/components/Icon';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
import { ListItem, Text } from '@packrat/ui/nativewindui';
-import { Icon } from 'expo-app/components/Icon';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { useRouter } from 'expo-router';
import { View } from 'react-native';
diff --git a/apps/expo/features/wildlife/components/index.ts b/packages/app/src/wildlife/components/index.ts
similarity index 100%
rename from apps/expo/features/wildlife/components/index.ts
rename to packages/app/src/wildlife/components/index.ts
diff --git a/apps/expo/features/wildlife/data/speciesDatabase.ts b/packages/app/src/wildlife/data/speciesDatabase.ts
similarity index 100%
rename from apps/expo/features/wildlife/data/speciesDatabase.ts
rename to packages/app/src/wildlife/data/speciesDatabase.ts
diff --git a/apps/expo/features/wildlife/hooks/index.ts b/packages/app/src/wildlife/hooks/index.ts
similarity index 100%
rename from apps/expo/features/wildlife/hooks/index.ts
rename to packages/app/src/wildlife/hooks/index.ts
diff --git a/apps/expo/features/wildlife/hooks/useWildlifeHistory.ts b/packages/app/src/wildlife/hooks/useWildlifeHistory.ts
similarity index 95%
rename from apps/expo/features/wildlife/hooks/useWildlifeHistory.ts
rename to packages/app/src/wildlife/hooks/useWildlifeHistory.ts
index 5f076e8a3a..42c49ad7e8 100644
--- a/apps/expo/features/wildlife/hooks/useWildlifeHistory.ts
+++ b/packages/app/src/wildlife/hooks/useWildlifeHistory.ts
@@ -1,9 +1,9 @@
-import ImageCacheManager from 'expo-app/lib/utils/ImageCacheManager';
+import ImageCacheManager from '@packrat/app/lib/utils/ImageCacheManager';
+import type { IdentificationResult, WildlifeIdentification } from '@packrat/app/wildlife';
import { useAtom } from 'jotai';
import { nanoid } from 'nanoid';
import { useCallback } from 'react';
import { baseWildlifeHistoryAtom, wildlifeHistoryAtom } from '../atoms/wildlifeAtoms';
-import type { IdentificationResult, WildlifeIdentification } from '../types';
export function useWildlifeHistory() {
const [historyState] = useAtom(wildlifeHistoryAtom);
diff --git a/apps/expo/features/wildlife/hooks/useWildlifeIdentification.ts b/packages/app/src/wildlife/hooks/useWildlifeIdentification.ts
similarity index 89%
rename from apps/expo/features/wildlife/hooks/useWildlifeIdentification.ts
rename to packages/app/src/wildlife/hooks/useWildlifeIdentification.ts
index 0dc6794346..7529a66cf6 100644
--- a/apps/expo/features/wildlife/hooks/useWildlifeIdentification.ts
+++ b/packages/app/src/wildlife/hooks/useWildlifeIdentification.ts
@@ -1,11 +1,11 @@
+import { apiClient } from '@packrat/app/lib/api/packrat';
+import type { SelectedImage } from '@packrat/app/packs/hooks/useImagePicker';
+import { uploadImage } from '@packrat/app/packs/utils';
+import type { IdentificationResult } from '@packrat/app/wildlife';
+import { identifyFromDescription } from '@packrat/app/wildlife';
import { isObject, zodGuard } from '@packrat/guards';
import { useMutation } from '@tanstack/react-query';
-import type { SelectedImage } from 'expo-app/features/packs/hooks/useImagePicker';
-import { uploadImage } from 'expo-app/features/packs/utils';
-import { apiClient } from 'expo-app/lib/api/packrat';
import { z } from 'zod';
-import { identifyFromDescription } from '../lib/offlineIdentifier';
-import type { IdentificationResult } from '../types';
const isIdentifyResponse = zodGuard(z.object({ results: z.array(z.unknown()) }));
diff --git a/packages/app/src/wildlife/index.ts b/packages/app/src/wildlife/index.ts
new file mode 100644
index 0000000000..0f29f40333
--- /dev/null
+++ b/packages/app/src/wildlife/index.ts
@@ -0,0 +1,18 @@
+export {
+ getSpeciesByCategory,
+ getSpeciesById,
+ SPECIES_DATABASE,
+ searchSpecies,
+} from './data/speciesDatabase';
+export {
+ getAllSpeciesResults,
+ getCandidatesByCategory,
+ getDangerousSpecies,
+ identifyFromDescription,
+} from './lib/offlineIdentifier';
+export type {
+ IdentificationResult,
+ SpeciesCategory,
+ SpeciesEntry,
+ WildlifeIdentification,
+} from './types';
diff --git a/apps/expo/features/wildlife/lib/offlineIdentifier.ts b/packages/app/src/wildlife/lib/offlineIdentifier.ts
similarity index 100%
rename from apps/expo/features/wildlife/lib/offlineIdentifier.ts
rename to packages/app/src/wildlife/lib/offlineIdentifier.ts
diff --git a/apps/expo/features/wildlife/screens/IdentificationScreen.tsx b/packages/app/src/wildlife/screens/IdentificationScreen.tsx
similarity index 94%
rename from apps/expo/features/wildlife/screens/IdentificationScreen.tsx
rename to packages/app/src/wildlife/screens/IdentificationScreen.tsx
index d7359b7229..c9cfcb7faa 100644
--- a/apps/expo/features/wildlife/screens/IdentificationScreen.tsx
+++ b/packages/app/src/wildlife/screens/IdentificationScreen.tsx
@@ -1,18 +1,18 @@
import { useActionSheet } from '@expo/react-native-action-sheet';
+import { Icon } from '@packrat/app/components/Icon';
+import { TextInput } from '@packrat/app/components/TextInput';
+import { appAlert } from '@packrat/app/lib/appAlert';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
+import { useImagePicker } from '@packrat/app/packs/hooks/useImagePicker';
+import type { IdentificationResult } from '@packrat/app/wildlife';
import { ActivityIndicator, Button, Text } from '@packrat/ui/nativewindui';
-import { appAlert } from 'expo-app/app/_layout';
-import { Icon } from 'expo-app/components/Icon';
-import { TextInput } from 'expo-app/components/TextInput';
-import { useImagePicker } from 'expo-app/features/packs/hooks/useImagePicker';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { Stack, useRouter } from 'expo-router';
import { useEffect, useRef, useState } from 'react';
import { Image, ScrollView, View } from 'react-native';
import { SpeciesCard } from '../components/SpeciesCard';
import { useWildlifeHistory } from '../hooks/useWildlifeHistory';
import { useWildlifeIdentification } from '../hooks/useWildlifeIdentification';
-import type { IdentificationResult } from '../types';
export function IdentificationScreen() {
const { t } = useTranslation();
diff --git a/apps/expo/features/wildlife/screens/SpeciesDetailScreen.tsx b/packages/app/src/wildlife/screens/SpeciesDetailScreen.tsx
similarity index 98%
rename from apps/expo/features/wildlife/screens/SpeciesDetailScreen.tsx
rename to packages/app/src/wildlife/screens/SpeciesDetailScreen.tsx
index ae48b9b740..e545d7ec53 100644
--- a/apps/expo/features/wildlife/screens/SpeciesDetailScreen.tsx
+++ b/packages/app/src/wildlife/screens/SpeciesDetailScreen.tsx
@@ -1,10 +1,10 @@
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
+import { getSpeciesById } from '@packrat/app/wildlife';
import { Text } from '@packrat/ui/nativewindui';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { Stack, useLocalSearchParams } from 'expo-router';
import { useAtom } from 'jotai';
import { ActivityIndicator, ScrollView, View } from 'react-native';
import { wildlifeHistoryAtom } from '../atoms/wildlifeAtoms';
-import { getSpeciesById } from '../data/speciesDatabase';
const DANGER_BADGE = {
safe: 'bg-green-100 dark:bg-green-900/30',
diff --git a/apps/expo/features/wildlife/screens/WildlifeScreen.tsx b/packages/app/src/wildlife/screens/WildlifeScreen.tsx
similarity index 94%
rename from apps/expo/features/wildlife/screens/WildlifeScreen.tsx
rename to packages/app/src/wildlife/screens/WildlifeScreen.tsx
index c48d234e17..222b23a8d9 100644
--- a/apps/expo/features/wildlife/screens/WildlifeScreen.tsx
+++ b/packages/app/src/wildlife/screens/WildlifeScreen.tsx
@@ -1,11 +1,11 @@
+import { Icon } from '@packrat/app/components/Icon';
+import { useColorScheme } from '@packrat/app/lib/hooks/useColorScheme';
+import { useTranslation } from '@packrat/app/lib/hooks/useTranslation';
+import type { WildlifeIdentification } from '@packrat/app/wildlife';
import { LargeTitleHeader, Text } from '@packrat/ui/nativewindui';
-import { Icon } from 'expo-app/components/Icon';
-import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme';
-import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { useRouter } from 'expo-router';
import { ActivityIndicator, FlatList, Pressable, SafeAreaView, View } from 'react-native';
import { useWildlifeHistory } from '../hooks/useWildlifeHistory';
-import type { WildlifeIdentification } from '../types';
function HistoryItem({ item, onPress }: { item: WildlifeIdentification; onPress: () => void }) {
const { colors } = useColorScheme();
diff --git a/apps/expo/features/wildlife/screens/index.ts b/packages/app/src/wildlife/screens/index.ts
similarity index 100%
rename from apps/expo/features/wildlife/screens/index.ts
rename to packages/app/src/wildlife/screens/index.ts
diff --git a/apps/expo/features/wildlife/types.ts b/packages/app/src/wildlife/types.ts
similarity index 100%
rename from apps/expo/features/wildlife/types.ts
rename to packages/app/src/wildlife/types.ts
diff --git a/packages/app/tsconfig.json b/packages/app/tsconfig.json
new file mode 100644
index 0000000000..c7a9b4dbd4
--- /dev/null
+++ b/packages/app/tsconfig.json
@@ -0,0 +1,14 @@
+{
+ "extends": "../../tsconfig.json",
+ "compilerOptions": {
+ "verbatimModuleSyntax": true,
+ "skipLibCheck": true,
+ "noEmit": true,
+ "paths": {
+ "@packrat/app": ["./src/index.ts"],
+ "@packrat/app/*": ["./src/*"]
+ }
+ },
+ "include": ["src/**/*.ts", "src/**/*.tsx"],
+ "exclude": ["node_modules", "dist"]
+}
diff --git a/todos/001-pending-p1-toggle-api-mismatch.md b/todos/001-pending-p1-toggle-api-mismatch.md
new file mode 100644
index 0000000000..7f748bfefd
--- /dev/null
+++ b/todos/001-pending-p1-toggle-api-mismatch.md
@@ -0,0 +1,59 @@
+---
+status: pending
+priority: p1
+issue_id: "001"
+tags: [code-review, architecture, runtime-bug, web-shims]
+dependencies: []
+---
+
+# Toggle API Mismatch — Silent Runtime Bug
+
+## Problem Statement
+
+`index.web.tsx` exports `Switch as Toggle` directly from `@packrat/web-ui`. The shadcn `Switch` uses `checked` + `onCheckedChange`, but nativewindui's `Toggle` (wrapping RN's `Switch`) uses `value` + `onValueChange`. Screen code passes `value`/`onValueChange` (e.g. `weather-alert-preferences.tsx` lines 96–116). On web, the Radix Switch receives unknown props — the toggle renders visually but never responds to interaction.
+
+**Why it matters:** Toggle UI appears functional but is completely broken. Users cannot change toggle state on web. No error is thrown.
+
+## Findings
+
+- Architecture reviewer confirmed screen code at `apps/expo/app/(app)/weather-alert-preferences.tsx:96–116` passes `value`/`onValueChange`
+- `@packrat/web-ui` `Switch` component uses `checked`/`onCheckedChange` (Radix UI)
+- Direct re-export `Switch as Toggle` provides no prop bridging
+
+## Proposed Solutions
+
+### Option A: Wrapper component in index.web.tsx (Recommended)
+```tsx
+export function Toggle({ value, onValueChange, checked, onCheckedChange, disabled, ...rest }) {
+ return (
+
+ );
+}
+```
+**Pros:** Handles both native and shadcn props, drop-in fix.
+**Cons:** Must keep in sync if Toggle native API evolves.
+**Effort:** Small. **Risk:** Low.
+
+### Option B: Dedicated `toggle.web.tsx` shim file
+Same logic but in its own file, imported by the barrel.
+**Pros:** Consistent with other shim files.
+**Cons:** One more file.
+**Effort:** Small. **Risk:** Low.
+
+## Acceptance Criteria
+- [ ] Toggle renders correctly on web
+- [ ] Toggling state updates correctly (onValueChange fires)
+- [ ] `bun check-types` still passes
+- [ ] Biome passes
+
+## Work Log
+- 2026-05-01: Found during ce:review (architecture-strategist agent)
+
+## Resources
+- `packages/ui/nativewindui/index.web.tsx` — current direct re-export
+- `apps/expo/app/(app)/weather-alert-preferences.tsx:96–116` — confirmed call site
diff --git a/todos/002-pending-p1-missing-react-type-imports.md b/todos/002-pending-p1-missing-react-type-imports.md
new file mode 100644
index 0000000000..dc730fe305
--- /dev/null
+++ b/todos/002-pending-p1-missing-react-type-imports.md
@@ -0,0 +1,45 @@
+---
+status: pending
+priority: p1
+issue_id: "002"
+tags: [code-review, typescript, web-shims]
+dependencies: []
+---
+
+# Missing `import type * as React` in Three Shim Files
+
+## Problem Statement
+
+`text-field.web.tsx`, `search-input.web.tsx`, and `icon.web.tsx` use `React.` namespace for type references (`React.ChangeEventHandler`, `React.ReactNode`, `React.CSSProperties`, `React.Ref`, `React.ChangeEvent`) but none import the `React` namespace. The new JSX transform handles JSX without a React import, but it does not inject the namespace for explicit type references. TypeScript will error on these under `strict: true`.
+
+## Findings
+
+From TypeScript reviewer:
+- `text-field.web.tsx`: uses `React.ChangeEventHandler`, `React.ReactNode`, `React.CSSProperties`, `React.Ref`
+- `search-input.web.tsx`: uses `React.ChangeEventHandler`, `React.ReactNode`
+- `icon.web.tsx`: uses `React.CSSProperties`
+- All three import named exports from `react` (forwardRef, useId, useEffect) but not the namespace
+
+## Proposed Solutions
+
+### Option A: Add `import type * as React from 'react'` (Recommended)
+Add to each of the three files. The `type` modifier keeps it type-only with zero bundle impact.
+**Effort:** Small. **Risk:** None.
+
+### Option B: Replace namespace usage with direct type imports
+e.g., `import type { CSSProperties } from 'react'` and replace `React.CSSProperties` → `CSSProperties`
+**Pros:** More explicit, no namespace needed.
+**Cons:** More lines to change.
+**Effort:** Small. **Risk:** None.
+
+## Acceptance Criteria
+- [ ] `bun check-types` passes with no React namespace errors
+- [ ] Biome passes (import should be `import type`)
+
+## Work Log
+- 2026-05-01: Found during ce:review (kieran-typescript-reviewer)
+
+## Resources
+- `packages/ui/nativewindui/text-field.web.tsx`
+- `packages/ui/nativewindui/search-input.web.tsx`
+- `packages/ui/nativewindui/icon.web.tsx`
diff --git a/todos/003-pending-p1-securetext-multiline-plaintext.md b/todos/003-pending-p1-securetext-multiline-plaintext.md
new file mode 100644
index 0000000000..244aca5c42
--- /dev/null
+++ b/todos/003-pending-p1-securetext-multiline-plaintext.md
@@ -0,0 +1,48 @@
+---
+status: pending
+priority: p1
+issue_id: "003"
+tags: [code-review, security, web-shims]
+dependencies: []
+---
+
+# secureTextEntry + multiline Silently Renders Passwords as Plaintext
+
+## Problem Statement
+
+In `text-field.web.tsx`, when `multiline={true}` a `