[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M70K24THEXGD2B9CEV1Z34ZQ":3,"skill-same:01M70K24THEXGD2B9CEV1Z34ZQ":73},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":23,"publishedAt":44,"linkOnly":45,"markdownContent":46,"markdownBody":47,"frontMatter":48,"refName":49,"commitSha":50,"skillDirectory":51,"forksCount":52,"repositoryHtmlUrl":53,"repositoryHomepageUrl":54,"ownerHtmlUrl":55,"ownerBlogUrl":56,"ownerDescription":57,"updatedAt":58,"install":59,"resources":72},"01M70K24THEXGD2B9CEV1Z34ZQ","react-native-architecture",null,"Build production React Native apps with Expo, navigation, native modules, offline sync, and cross-platform patterns. Use when developing mobile apps, implementing native integrations, or architecting React Native projects.","shengdabai\u002FTony-Claude-Code-Skills","shengdabai","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F116417419?v=4","01M70F6D2WY5PQV5A92BKYJQEW","Tony Sheng","01M70F6D3GAAAYZ5H4CC1G8VXM",3,"MIT","https:\u002F\u002Fgithub.com\u002Fshengdabai\u002FTony-Claude-Code-Skills\u002Fblob\u002Fmain\u002Fskills\u002F_collections\u002Fwshobson-agents\u002Fplugins\u002Ffrontend-mobile-development\u002Fskills\u002Freact-native-architecture\u002FSKILL.md",[18],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000009","前端开发","SCENARIO","使用场景",[24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43],"architecture","react native architecture","native","react native","react","mobile","when","implementing","with","apps","platform","native apps","patterns","expo","modules","use","integrations","projects","offline","sync","2026-11-10 10:12:30",false,"---\nname: react-native-architecture\ndescription: Build production React Native apps with Expo, navigation, native modules, offline sync, and cross-platform patterns. Use when developing mobile apps, implementing native integrations, or architecting React Native projects.\n---\n\n# React Native Architecture\n\nProduction-ready patterns for React Native development with Expo, including navigation, state management, native modules, and offline-first architecture.\n\n## When to Use This Skill\n\n- Starting a new React Native or Expo project\n- Implementing complex navigation patterns\n- Integrating native modules and platform APIs\n- Building offline-first mobile applications\n- Optimizing React Native performance\n- Setting up CI\u002FCD for mobile releases\n\n## Core Concepts\n\n### 1. Project Structure\n\n```\nsrc\u002F\n├── app\u002F                    # Expo Router screens\n│   ├── (auth)\u002F            # Auth group\n│   ├── (tabs)\u002F            # Tab navigation\n│   └── _layout.tsx        # Root layout\n├── components\u002F\n│   ├── ui\u002F                # Reusable UI components\n│   └── features\u002F          # Feature-specific components\n├── hooks\u002F                 # Custom hooks\n├── services\u002F              # API and native services\n├── stores\u002F                # State management\n├── utils\u002F                 # Utilities\n└── types\u002F                 # TypeScript types\n```\n\n### 2. Expo vs Bare React Native\n\n| Feature | Expo | Bare RN |\n|---------|------|---------|\n| Setup complexity | Low | High |\n| Native modules | EAS Build | Manual linking |\n| OTA updates | Built-in | Manual setup |\n| Build service | EAS | Custom CI |\n| Custom native code | Config plugins | Direct access |\n\n## Quick Start\n\n```bash\n# Create new Expo project\nnpx create-expo-app@latest my-app -t expo-template-blank-typescript\n\n# Install essential dependencies\nnpx expo install expo-router expo-status-bar react-native-safe-area-context\nnpx expo install @react-native-async-storage\u002Fasync-storage\nnpx expo install expo-secure-store expo-haptics\n```\n\n```typescript\n\u002F\u002F app\u002F_layout.tsx\nimport { Stack } from 'expo-router'\nimport { ThemeProvider } from '@\u002Fproviders\u002FThemeProvider'\nimport { QueryProvider } from '@\u002Fproviders\u002FQueryProvider'\n\nexport default function RootLayout() {\n  return (\n    \u003CQueryProvider>\n      \u003CThemeProvider>\n        \u003CStack screenOptions={{ headerShown: false }}>\n          \u003CStack.Screen name=\"(tabs)\" \u002F>\n          \u003CStack.Screen name=\"(auth)\" \u002F>\n          \u003CStack.Screen name=\"modal\" options={{ presentation: 'modal' }} \u002F>\n        \u003C\u002FStack>\n      \u003C\u002FThemeProvider>\n    \u003C\u002FQueryProvider>\n  )\n}\n```\n\n## Patterns\n\n### Pattern 1: Expo Router Navigation\n\n```typescript\n\u002F\u002F app\u002F(tabs)\u002F_layout.tsx\nimport { Tabs } from 'expo-router'\nimport { Home, Search, User, Settings } from 'lucide-react-native'\nimport { useTheme } from '@\u002Fhooks\u002FuseTheme'\n\nexport default function TabLayout() {\n  const { colors } = useTheme()\n\n  return (\n    \u003CTabs\n      screenOptions={{\n        tabBarActiveTintColor: colors.primary,\n        tabBarInactiveTintColor: colors.textMuted,\n        tabBarStyle: { backgroundColor: colors.background },\n        headerShown: false,\n      }}\n    >\n      \u003CTabs.Screen\n        name=\"index\"\n        options={{\n          title: 'Home',\n          tabBarIcon: ({ color, size }) => \u003CHome size={size} color={color} \u002F>,\n        }}\n      \u002F>\n      \u003CTabs.Screen\n        name=\"search\"\n        options={{\n          title: 'Search',\n          tabBarIcon: ({ color, size }) => \u003CSearch size={size} color={color} \u002F>,\n        }}\n      \u002F>\n      \u003CTabs.Screen\n        name=\"profile\"\n        options={{\n          title: 'Profile',\n          tabBarIcon: ({ color, size }) => \u003CUser size={size} color={color} \u002F>,\n        }}\n      \u002F>\n      \u003CTabs.Screen\n        name=\"settings\"\n        options={{\n          title: 'Settings',\n          tabBarIcon: ({ color, size }) => \u003CSettings size={size} color={color} \u002F>,\n        }}\n      \u002F>\n    \u003C\u002FTabs>\n  )\n}\n\n\u002F\u002F app\u002F(tabs)\u002Fprofile\u002F[id].tsx - Dynamic route\nimport { useLocalSearchParams } from 'expo-router'\n\nexport default function ProfileScreen() {\n  const { id } = useLocalSearchParams\u003C{ id: string }>()\n\n  return \u003CUserProfile userId={id} \u002F>\n}\n\n\u002F\u002F Navigation from anywhere\nimport { router } from 'expo-router'\n\n\u002F\u002F Programmatic navigation\nrouter.push('\u002Fprofile\u002F123')\nrouter.replace('\u002Flogin')\nrouter.back()\n\n\u002F\u002F With params\nrouter.push({\n  pathname: '\u002Fproduct\u002F[id]',\n  params: { id: '123', referrer: 'home' },\n})\n```\n\n### Pattern 2: Authentication Flow\n\n```typescript\n\u002F\u002F providers\u002FAuthProvider.tsx\nimport { createContext, useContext, useEffect, useState } from 'react'\nimport { useRouter, useSegments } from 'expo-router'\nimport * as SecureStore from 'expo-secure-store'\n\ninterface AuthContextType {\n  user: User | null\n  isLoading: boolean\n  signIn: (credentials: Credentials) => Promise\u003Cvoid>\n  signOut: () => Promise\u003Cvoid>\n}\n\nconst AuthContext = createContext\u003CAuthContextType | null>(null)\n\nexport function AuthProvider({ children }: { children: React.ReactNode }) {\n  const [user, setUser] = useState\u003CUser | null>(null)\n  const [isLoading, setIsLoading] = useState(true)\n  const segments = useSegments()\n  const router = useRouter()\n\n  \u002F\u002F Check authentication on mount\n  useEffect(() => {\n    checkAuth()\n  }, [])\n\n  \u002F\u002F Protect routes\n  useEffect(() => {\n    if (isLoading) return\n\n    const inAuthGroup = segments[0] === '(auth)'\n\n    if (!user && !inAuthGroup) {\n      router.replace('\u002Flogin')\n    } else if (user && inAuthGroup) {\n      router.replace('\u002F(tabs)')\n    }\n  }, [user, segments, isLoading])\n\n  async function checkAuth() {\n    try {\n      const token = await SecureStore.getItemAsync('authToken')\n      if (token) {\n        const userData = await api.getUser(token)\n        setUser(userData)\n      }\n    } catch (error) {\n      await SecureStore.deleteItemAsync('authToken')\n    } finally {\n      setIsLoading(false)\n    }\n  }\n\n  async function signIn(credentials: Credentials) {\n    const { token, user } = await api.login(credentials)\n    await SecureStore.setItemAsync('authToken', token)\n    setUser(user)\n  }\n\n  async function signOut() {\n    await SecureStore.deleteItemAsync('authToken')\n    setUser(null)\n  }\n\n  if (isLoading) {\n    return \u003CSplashScreen \u002F>\n  }\n\n  return (\n    \u003CAuthContext.Provider value={{ user, isLoading, signIn, signOut }}>\n      {children}\n    \u003C\u002FAuthContext.Provider>\n  )\n}\n\nexport const useAuth = () => {\n  const context = useContext(AuthContext)\n  if (!context) throw new Error('useAuth must be used within AuthProvider')\n  return context\n}\n```\n\n### Pattern 3: Offline-First with React Query\n\n```typescript\n\u002F\u002F providers\u002FQueryProvider.tsx\nimport { QueryClient, QueryClientProvider } from '@tanstack\u002Freact-query'\nimport { createAsyncStoragePersister } from '@tanstack\u002Fquery-async-storage-persister'\nimport { PersistQueryClientProvider } from '@tanstack\u002Freact-query-persist-client'\nimport AsyncStorage from '@react-native-async-storage\u002Fasync-storage'\nimport NetInfo from '@react-native-community\u002Fnetinfo'\nimport { onlineManager } from '@tanstack\u002Freact-query'\n\n\u002F\u002F Sync online status\nonlineManager.setEventListener((setOnline) => {\n  return NetInfo.addEventListener((state) => {\n    setOnline(!!state.isConnected)\n  })\n})\n\nconst queryClient = new QueryClient({\n  defaultOptions: {\n    queries: {\n      gcTime: 1000 * 60 * 60 * 24, \u002F\u002F 24 hours\n      staleTime: 1000 * 60 * 5, \u002F\u002F 5 minutes\n      retry: 2,\n      networkMode: 'offlineFirst',\n    },\n    mutations: {\n      networkMode: 'offlineFirst',\n    },\n  },\n})\n\nconst asyncStoragePersister = createAsyncStoragePersister({\n  storage: AsyncStorage,\n  key: 'REACT_QUERY_OFFLINE_CACHE',\n})\n\nexport function QueryProvider({ children }: { children: React.ReactNode }) {\n  return (\n    \u003CPersistQueryClientProvider\n      client={queryClient}\n      persistOptions={{ persister: asyncStoragePersister }}\n    >\n      {children}\n    \u003C\u002FPersistQueryClientProvider>\n  )\n}\n\n\u002F\u002F hooks\u002FuseProducts.ts\nimport { useQuery, useMutation, useQueryClient } from '@tanstack\u002Freact-query'\n\nexport function useProducts() {\n  return useQuery({\n    queryKey: ['products'],\n    queryFn: api.getProducts,\n    \u002F\u002F Use stale data while revalidating\n    placeholderData: (previousData) => previousData,\n  })\n}\n\nexport function useCreateProduct() {\n  const queryClient = useQueryClient()\n\n  return useMutation({\n    mutationFn: api.createProduct,\n    \u002F\u002F Optimistic update\n    onMutate: async (newProduct) => {\n      await queryClient.cancelQueries({ queryKey: ['products'] })\n      const previous = queryClient.getQueryData(['products'])\n\n      queryClient.setQueryData(['products'], (old: Product[]) => [\n        ...old,\n        { ...newProduct, id: 'temp-' + Date.now() },\n      ])\n\n      return { previous }\n    },\n    onError: (err, newProduct, context) => {\n      queryClient.setQueryData(['products'], context?.previous)\n    },\n    onSettled: () => {\n      queryClient.invalidateQueries({ queryKey: ['products'] })\n    },\n  })\n}\n```\n\n### Pattern 4: Native Module Integration\n\n```typescript\n\u002F\u002F services\u002Fhaptics.ts\nimport * as Haptics from 'expo-haptics'\nimport { Platform } from 'react-native'\n\nexport const haptics = {\n  light: () => {\n    if (Platform.OS !== 'web') {\n      Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light)\n    }\n  },\n  medium: () => {\n    if (Platform.OS !== 'web') {\n      Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium)\n    }\n  },\n  heavy: () => {\n    if (Platform.OS !== 'web') {\n      Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Heavy)\n    }\n  },\n  success: () => {\n    if (Platform.OS !== 'web') {\n      Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success)\n    }\n  },\n  error: () => {\n    if (Platform.OS !== 'web') {\n      Haptics.notificationAsync(Haptics.NotificationFeedbackType.Error)\n    }\n  },\n}\n\n\u002F\u002F services\u002Fbiometrics.ts\nimport * as LocalAuthentication from 'expo-local-authentication'\n\nexport async function authenticateWithBiometrics(): Promise\u003Cboolean> {\n  const hasHardware = await LocalAuthentication.hasHardwareAsync()\n  if (!hasHardware) return false\n\n  const isEnrolled = await LocalAuthentication.isEnrolledAsync()\n  if (!isEnrolled) return false\n\n  const result = await LocalAuthentication.authenticateAsync({\n    promptMessage: 'Authenticate to continue',\n    fallbackLabel: 'Use passcode',\n    disableDeviceFallback: false,\n  })\n\n  return result.success\n}\n\n\u002F\u002F services\u002Fnotifications.ts\nimport * as Notifications from 'expo-notifications'\nimport { Platform } from 'react-native'\nimport Constants from 'expo-constants'\n\nNotifications.setNotificationHandler({\n  handleNotification: async () => ({\n    shouldShowAlert: true,\n    shouldPlaySound: true,\n    shouldSetBadge: true,\n  }),\n})\n\nexport async function registerForPushNotifications() {\n  let token: string | undefined\n\n  if (Platform.OS === 'android') {\n    await Notifications.setNotificationChannelAsync('default', {\n      name: 'default',\n      importance: Notifications.AndroidImportance.MAX,\n      vibrationPattern: [0, 250, 250, 250],\n    })\n  }\n\n  const { status: existingStatus } = await Notifications.getPermissionsAsync()\n  let finalStatus = existingStatus\n\n  if (existingStatus !== 'granted') {\n    const { status } = await Notifications.requestPermissionsAsync()\n    finalStatus = status\n  }\n\n  if (finalStatus !== 'granted') {\n    return null\n  }\n\n  const projectId = Constants.expoConfig?.extra?.eas?.projectId\n  token = (await Notifications.getExpoPushTokenAsync({ projectId })).data\n\n  return token\n}\n```\n\n### Pattern 5: Platform-Specific Code\n\n```typescript\n\u002F\u002F components\u002Fui\u002FButton.tsx\nimport { Platform, Pressable, StyleSheet, Text, ViewStyle } from 'react-native'\nimport * as Haptics from 'expo-haptics'\nimport Animated, {\n  useAnimatedStyle,\n  useSharedValue,\n  withSpring,\n} from 'react-native-reanimated'\n\nconst AnimatedPressable = Animated.createAnimatedComponent(Pressable)\n\ninterface ButtonProps {\n  title: string\n  onPress: () => void\n  variant?: 'primary' | 'secondary' | 'outline'\n  disabled?: boolean\n}\n\nexport function Button({\n  title,\n  onPress,\n  variant = 'primary',\n  disabled = false,\n}: ButtonProps) {\n  const scale = useSharedValue(1)\n\n  const animatedStyle = useAnimatedStyle(() => ({\n    transform: [{ scale: scale.value }],\n  }))\n\n  const handlePressIn = () => {\n    scale.value = withSpring(0.95)\n    if (Platform.OS !== 'web') {\n      Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light)\n    }\n  }\n\n  const handlePressOut = () => {\n    scale.value = withSpring(1)\n  }\n\n  return (\n    \u003CAnimatedPressable\n      onPress={onPress}\n      onPressIn={handlePressIn}\n      onPressOut={handlePressOut}\n      disabled={disabled}\n      style={[\n        styles.button,\n        styles[variant],\n        disabled && styles.disabled,\n        animatedStyle,\n      ]}\n    >\n      \u003CText style={[styles.text, styles[`${variant}Text`]]}>{title}\u003C\u002FText>\n    \u003C\u002FAnimatedPressable>\n  )\n}\n\n\u002F\u002F Platform-specific files\n\u002F\u002F Button.ios.tsx - iOS-specific implementation\n\u002F\u002F Button.android.tsx - Android-specific implementation\n\u002F\u002F Button.web.tsx - Web-specific implementation\n\n\u002F\u002F Or use Platform.select\nconst styles = StyleSheet.create({\n  button: {\n    paddingVertical: 12,\n    paddingHorizontal: 24,\n    borderRadius: 8,\n    alignItems: 'center',\n    ...Platform.select({\n      ios: {\n        shadowColor: '#000',\n        shadowOffset: { width: 0, height: 2 },\n        shadowOpacity: 0.1,\n        shadowRadius: 4,\n      },\n      android: {\n        elevation: 4,\n      },\n    }),\n  },\n  primary: {\n    backgroundColor: '#007AFF',\n  },\n  secondary: {\n    backgroundColor: '#5856D6',\n  },\n  outline: {\n    backgroundColor: 'transparent',\n    borderWidth: 1,\n    borderColor: '#007AFF',\n  },\n  disabled: {\n    opacity: 0.5,\n  },\n  text: {\n    fontSize: 16,\n    fontWeight: '600',\n  },\n  primaryText: {\n    color: '#FFFFFF',\n  },\n  secondaryText: {\n    color: '#FFFFFF',\n  },\n  outlineText: {\n    color: '#007AFF',\n  },\n})\n```\n\n### Pattern 6: Performance Optimization\n\n```typescript\n\u002F\u002F components\u002FProductList.tsx\nimport { FlashList } from '@shopify\u002Fflash-list'\nimport { memo, useCallback } from 'react'\n\ninterface ProductListProps {\n  products: Product[]\n  onProductPress: (id: string) => void\n}\n\n\u002F\u002F Memoize list item\nconst ProductItem = memo(function ProductItem({\n  item,\n  onPress,\n}: {\n  item: Product\n  onPress: (id: string) => void\n}) {\n  const handlePress = useCallback(() => onPress(item.id), [item.id, onPress])\n\n  return (\n    \u003CPressable onPress={handlePress} style={styles.item}>\n      \u003CFastImage\n        source={{ uri: item.image }}\n        style={styles.image}\n        resizeMode=\"cover\"\n      \u002F>\n      \u003CText style={styles.title}>{item.name}\u003C\u002FText>\n      \u003CText style={styles.price}>${item.price}\u003C\u002FText>\n    \u003C\u002FPressable>\n  )\n})\n\nexport function ProductList({ products, onProductPress }: ProductListProps) {\n  const renderItem = useCallback(\n    ({ item }: { item: Product }) => (\n      \u003CProductItem item={item} onPress={onProductPress} \u002F>\n    ),\n    [onProductPress]\n  )\n\n  const keyExtractor = useCallback((item: Product) => item.id, [])\n\n  return (\n    \u003CFlashList\n      data={products}\n      renderItem={renderItem}\n      keyExtractor={keyExtractor}\n      estimatedItemSize={100}\n      \u002F\u002F Performance optimizations\n      removeClippedSubviews={true}\n      maxToRenderPerBatch={10}\n      windowSize={5}\n      \u002F\u002F Pull to refresh\n      onRefresh={onRefresh}\n      refreshing={isRefreshing}\n    \u002F>\n  )\n}\n```\n\n## EAS Build & Submit\n\n```json\n\u002F\u002F eas.json\n{\n  \"cli\": { \"version\": \">= 5.0.0\" },\n  \"build\": {\n    \"development\": {\n      \"developmentClient\": true,\n      \"distribution\": \"internal\",\n      \"ios\": { \"simulator\": true }\n    },\n    \"preview\": {\n      \"distribution\": \"internal\",\n      \"android\": { \"buildType\": \"apk\" }\n    },\n    \"production\": {\n      \"autoIncrement\": true\n    }\n  },\n  \"submit\": {\n    \"production\": {\n      \"ios\": { \"appleId\": \"your@email.com\", \"ascAppId\": \"123456789\" },\n      \"android\": { \"serviceAccountKeyPath\": \".\u002Fgoogle-services.json\" }\n    }\n  }\n}\n```\n\n```bash\n# Build commands\neas build --platform ios --profile development\neas build --platform android --profile preview\neas build --platform all --profile production\n\n# Submit to stores\neas submit --platform ios\neas submit --platform android\n\n# OTA updates\neas update --branch production --message \"Bug fixes\"\n```\n\n## Best Practices\n\n### Do's\n- **Use Expo** - Faster development, OTA updates, managed native code\n- **FlashList over FlatList** - Better performance for long lists\n- **Memoize components** - Prevent unnecessary re-renders\n- **Use Reanimated** - 60fps animations on native thread\n- **Test on real devices** - Simulators miss real-world issues\n\n### Don'ts\n- **Don't inline styles** - Use StyleSheet.create for performance\n- **Don't fetch in render** - Use useEffect or React Query\n- **Don't ignore platform differences** - Test on both iOS and Android\n- **Don't store secrets in code** - Use environment variables\n- **Don't skip error boundaries** - Mobile crashes are unforgiving\n\n## Resources\n\n- [Expo Documentation](https:\u002F\u002Fdocs.expo.dev\u002F)\n- [Expo Router](https:\u002F\u002Fdocs.expo.dev\u002Frouter\u002Fintroduction\u002F)\n- [React Native Performance](https:\u002F\u002Freactnative.dev\u002Fdocs\u002Fperformance)\n- [FlashList](https:\u002F\u002Fshopify.github.io\u002Fflash-list\u002F)\n","# React Native Architecture\n\nProduction-ready patterns for React Native development with Expo, including navigation, state management, native modules, and offline-first architecture.\n\n## When to Use This Skill\n\n- Starting a new React Native or Expo project\n- Implementing complex navigation patterns\n- Integrating native modules and platform APIs\n- Building offline-first mobile applications\n- Optimizing React Native performance\n- Setting up CI\u002FCD for mobile releases\n\n## Core Concepts\n\n### 1. Project Structure\n\n```\nsrc\u002F\n├── app\u002F                    # Expo Router screens\n│   ├── (auth)\u002F            # Auth group\n│   ├── (tabs)\u002F            # Tab navigation\n│   └── _layout.tsx        # Root layout\n├── components\u002F\n│   ├── ui\u002F                # Reusable UI components\n│   └── features\u002F          # Feature-specific components\n├── hooks\u002F                 # Custom hooks\n├── services\u002F              # API and native services\n├── stores\u002F                # State management\n├── utils\u002F                 # Utilities\n└── types\u002F                 # TypeScript types\n```\n\n### 2. Expo vs Bare React Native\n\n| Feature | Expo | Bare RN |\n|---------|------|---------|\n| Setup complexity | Low | High |\n| Native modules | EAS Build | Manual linking |\n| OTA updates | Built-in | Manual setup |\n| Build service | EAS | Custom CI |\n| Custom native code | Config plugins | Direct access |\n\n## Quick Start\n\n```bash\n# Create new Expo project\nnpx create-expo-app@latest my-app -t expo-template-blank-typescript\n\n# Install essential dependencies\nnpx expo install expo-router expo-status-bar react-native-safe-area-context\nnpx expo install @react-native-async-storage\u002Fasync-storage\nnpx expo install expo-secure-store expo-haptics\n```\n\n```typescript\n\u002F\u002F app\u002F_layout.tsx\nimport { Stack } from 'expo-router'\nimport { ThemeProvider } from '@\u002Fproviders\u002FThemeProvider'\nimport { QueryProvider } from '@\u002Fproviders\u002FQueryProvider'\n\nexport default function RootLayout() {\n  return (\n    \u003CQueryProvider>\n      \u003CThemeProvider>\n        \u003CStack screenOptions={{ headerShown: false }}>\n          \u003CStack.Screen name=\"(tabs)\" \u002F>\n          \u003CStack.Screen name=\"(auth)\" \u002F>\n          \u003CStack.Screen name=\"modal\" options={{ presentation: 'modal' }} \u002F>\n        \u003C\u002FStack>\n      \u003C\u002FThemeProvider>\n    \u003C\u002FQueryProvider>\n  )\n}\n```\n\n## Patterns\n\n### Pattern 1: Expo Router Navigation\n\n```typescript\n\u002F\u002F app\u002F(tabs)\u002F_layout.tsx\nimport { Tabs } from 'expo-router'\nimport { Home, Search, User, Settings } from 'lucide-react-native'\nimport { useTheme } from '@\u002Fhooks\u002FuseTheme'\n\nexport default function TabLayout() {\n  const { colors } = useTheme()\n\n  return (\n    \u003CTabs\n      screenOptions={{\n        tabBarActiveTintColor: colors.primary,\n        tabBarInactiveTintColor: colors.textMuted,\n        tabBarStyle: { backgroundColor: colors.background },\n        headerShown: false,\n      }}\n    >\n      \u003CTabs.Screen\n        name=\"index\"\n        options={{\n          title: 'Home',\n          tabBarIcon: ({ color, size }) => \u003CHome size={size} color={color} \u002F>,\n        }}\n      \u002F>\n      \u003CTabs.Screen\n        name=\"search\"\n        options={{\n          title: 'Search',\n          tabBarIcon: ({ color, size }) => \u003CSearch size={size} color={color} \u002F>,\n        }}\n      \u002F>\n      \u003CTabs.Screen\n        name=\"profile\"\n        options={{\n          title: 'Profile',\n          tabBarIcon: ({ color, size }) => \u003CUser size={size} color={color} \u002F>,\n        }}\n      \u002F>\n      \u003CTabs.Screen\n        name=\"settings\"\n        options={{\n          title: 'Settings',\n          tabBarIcon: ({ color, size }) => \u003CSettings size={size} color={color} \u002F>,\n        }}\n      \u002F>\n    \u003C\u002FTabs>\n  )\n}\n\n\u002F\u002F app\u002F(tabs)\u002Fprofile\u002F[id].tsx - Dynamic route\nimport { useLocalSearchParams } from 'expo-router'\n\nexport default function ProfileScreen() {\n  const { id } = useLocalSearchParams\u003C{ id: string }>()\n\n  return \u003CUserProfile userId={id} \u002F>\n}\n\n\u002F\u002F Navigation from anywhere\nimport { router } from 'expo-router'\n\n\u002F\u002F Programmatic navigation\nrouter.push('\u002Fprofile\u002F123')\nrouter.replace('\u002Flogin')\nrouter.back()\n\n\u002F\u002F With params\nrouter.push({\n  pathname: '\u002Fproduct\u002F[id]',\n  params: { id: '123', referrer: 'home' },\n})\n```\n\n### Pattern 2: Authentication Flow\n\n```typescript\n\u002F\u002F providers\u002FAuthProvider.tsx\nimport { createContext, useContext, useEffect, useState } from 'react'\nimport { useRouter, useSegments } from 'expo-router'\nimport * as SecureStore from 'expo-secure-store'\n\ninterface AuthContextType {\n  user: User | null\n  isLoading: boolean\n  signIn: (credentials: Credentials) => Promise\u003Cvoid>\n  signOut: () => Promise\u003Cvoid>\n}\n\nconst AuthContext = createContext\u003CAuthContextType | null>(null)\n\nexport function AuthProvider({ children }: { children: React.ReactNode }) {\n  const [user, setUser] = useState\u003CUser | null>(null)\n  const [isLoading, setIsLoading] = useState(true)\n  const segments = useSegments()\n  const router = useRouter()\n\n  \u002F\u002F Check authentication on mount\n  useEffect(() => {\n    checkAuth()\n  }, [])\n\n  \u002F\u002F Protect routes\n  useEffect(() => {\n    if (isLoading) return\n\n    const inAuthGroup = segments[0] === '(auth)'\n\n    if (!user && !inAuthGroup) {\n      router.replace('\u002Flogin')\n    } else if (user && inAuthGroup) {\n      router.replace('\u002F(tabs)')\n    }\n  }, [user, segments, isLoading])\n\n  async function checkAuth() {\n    try {\n      const token = await SecureStore.getItemAsync('authToken')\n      if (token) {\n        const userData = await api.getUser(token)\n        setUser(userData)\n      }\n    } catch (error) {\n      await SecureStore.deleteItemAsync('authToken')\n    } finally {\n      setIsLoading(false)\n    }\n  }\n\n  async function signIn(credentials: Credentials) {\n    const { token, user } = await api.login(credentials)\n    await SecureStore.setItemAsync('authToken', token)\n    setUser(user)\n  }\n\n  async function signOut() {\n    await SecureStore.deleteItemAsync('authToken')\n    setUser(null)\n  }\n\n  if (isLoading) {\n    return \u003CSplashScreen \u002F>\n  }\n\n  return (\n    \u003CAuthContext.Provider value={{ user, isLoading, signIn, signOut }}>\n      {children}\n    \u003C\u002FAuthContext.Provider>\n  )\n}\n\nexport const useAuth = () => {\n  const context = useContext(AuthContext)\n  if (!context) throw new Error('useAuth must be used within AuthProvider')\n  return context\n}\n```\n\n### Pattern 3: Offline-First with React Query\n\n```typescript\n\u002F\u002F providers\u002FQueryProvider.tsx\nimport { QueryClient, QueryClientProvider } from '@tanstack\u002Freact-query'\nimport { createAsyncStoragePersister } from '@tanstack\u002Fquery-async-storage-persister'\nimport { PersistQueryClientProvider } from '@tanstack\u002Freact-query-persist-client'\nimport AsyncStorage from '@react-native-async-storage\u002Fasync-storage'\nimport NetInfo from '@react-native-community\u002Fnetinfo'\nimport { onlineManager } from '@tanstack\u002Freact-query'\n\n\u002F\u002F Sync online status\nonlineManager.setEventListener((setOnline) => {\n  return NetInfo.addEventListener((state) => {\n    setOnline(!!state.isConnected)\n  })\n})\n\nconst queryClient = new QueryClient({\n  defaultOptions: {\n    queries: {\n      gcTime: 1000 * 60 * 60 * 24, \u002F\u002F 24 hours\n      staleTime: 1000 * 60 * 5, \u002F\u002F 5 minutes\n      retry: 2,\n      networkMode: 'offlineFirst',\n    },\n    mutations: {\n      networkMode: 'offlineFirst',\n    },\n  },\n})\n\nconst asyncStoragePersister = createAsyncStoragePersister({\n  storage: AsyncStorage,\n  key: 'REACT_QUERY_OFFLINE_CACHE',\n})\n\nexport function QueryProvider({ children }: { children: React.ReactNode }) {\n  return (\n    \u003CPersistQueryClientProvider\n      client={queryClient}\n      persistOptions={{ persister: asyncStoragePersister }}\n    >\n      {children}\n    \u003C\u002FPersistQueryClientProvider>\n  )\n}\n\n\u002F\u002F hooks\u002FuseProducts.ts\nimport { useQuery, useMutation, useQueryClient } from '@tanstack\u002Freact-query'\n\nexport function useProducts() {\n  return useQuery({\n    queryKey: ['products'],\n    queryFn: api.getProducts,\n    \u002F\u002F Use stale data while revalidating\n    placeholderData: (previousData) => previousData,\n  })\n}\n\nexport function useCreateProduct() {\n  const queryClient = useQueryClient()\n\n  return useMutation({\n    mutationFn: api.createProduct,\n    \u002F\u002F Optimistic update\n    onMutate: async (newProduct) => {\n      await queryClient.cancelQueries({ queryKey: ['products'] })\n      const previous = queryClient.getQueryData(['products'])\n\n      queryClient.setQueryData(['products'], (old: Product[]) => [\n        ...old,\n        { ...newProduct, id: 'temp-' + Date.now() },\n      ])\n\n      return { previous }\n    },\n    onError: (err, newProduct, context) => {\n      queryClient.setQueryData(['products'], context?.previous)\n    },\n    onSettled: () => {\n      queryClient.invalidateQueries({ queryKey: ['products'] })\n    },\n  })\n}\n```\n\n### Pattern 4: Native Module Integration\n\n```typescript\n\u002F\u002F services\u002Fhaptics.ts\nimport * as Haptics from 'expo-haptics'\nimport { Platform } from 'react-native'\n\nexport const haptics = {\n  light: () => {\n    if (Platform.OS !== 'web') {\n      Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light)\n    }\n  },\n  medium: () => {\n    if (Platform.OS !== 'web') {\n      Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium)\n    }\n  },\n  heavy: () => {\n    if (Platform.OS !== 'web') {\n      Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Heavy)\n    }\n  },\n  success: () => {\n    if (Platform.OS !== 'web') {\n      Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success)\n    }\n  },\n  error: () => {\n    if (Platform.OS !== 'web') {\n      Haptics.notificationAsync(Haptics.NotificationFeedbackType.Error)\n    }\n  },\n}\n\n\u002F\u002F services\u002Fbiometrics.ts\nimport * as LocalAuthentication from 'expo-local-authentication'\n\nexport async function authenticateWithBiometrics(): Promise\u003Cboolean> {\n  const hasHardware = await LocalAuthentication.hasHardwareAsync()\n  if (!hasHardware) return false\n\n  const isEnrolled = await LocalAuthentication.isEnrolledAsync()\n  if (!isEnrolled) return false\n\n  const result = await LocalAuthentication.authenticateAsync({\n    promptMessage: 'Authenticate to continue',\n    fallbackLabel: 'Use passcode',\n    disableDeviceFallback: false,\n  })\n\n  return result.success\n}\n\n\u002F\u002F services\u002Fnotifications.ts\nimport * as Notifications from 'expo-notifications'\nimport { Platform } from 'react-native'\nimport Constants from 'expo-constants'\n\nNotifications.setNotificationHandler({\n  handleNotification: async () => ({\n    shouldShowAlert: true,\n    shouldPlaySound: true,\n    shouldSetBadge: true,\n  }),\n})\n\nexport async function registerForPushNotifications() {\n  let token: string | undefined\n\n  if (Platform.OS === 'android') {\n    await Notifications.setNotificationChannelAsync('default', {\n      name: 'default',\n      importance: Notifications.AndroidImportance.MAX,\n      vibrationPattern: [0, 250, 250, 250],\n    })\n  }\n\n  const { status: existingStatus } = await Notifications.getPermissionsAsync()\n  let finalStatus = existingStatus\n\n  if (existingStatus !== 'granted') {\n    const { status } = await Notifications.requestPermissionsAsync()\n    finalStatus = status\n  }\n\n  if (finalStatus !== 'granted') {\n    return null\n  }\n\n  const projectId = Constants.expoConfig?.extra?.eas?.projectId\n  token = (await Notifications.getExpoPushTokenAsync({ projectId })).data\n\n  return token\n}\n```\n\n### Pattern 5: Platform-Specific Code\n\n```typescript\n\u002F\u002F components\u002Fui\u002FButton.tsx\nimport { Platform, Pressable, StyleSheet, Text, ViewStyle } from 'react-native'\nimport * as Haptics from 'expo-haptics'\nimport Animated, {\n  useAnimatedStyle,\n  useSharedValue,\n  withSpring,\n} from 'react-native-reanimated'\n\nconst AnimatedPressable = Animated.createAnimatedComponent(Pressable)\n\ninterface ButtonProps {\n  title: string\n  onPress: () => void\n  variant?: 'primary' | 'secondary' | 'outline'\n  disabled?: boolean\n}\n\nexport function Button({\n  title,\n  onPress,\n  variant = 'primary',\n  disabled = false,\n}: ButtonProps) {\n  const scale = useSharedValue(1)\n\n  const animatedStyle = useAnimatedStyle(() => ({\n    transform: [{ scale: scale.value }],\n  }))\n\n  const handlePressIn = () => {\n    scale.value = withSpring(0.95)\n    if (Platform.OS !== 'web') {\n      Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light)\n    }\n  }\n\n  const handlePressOut = () => {\n    scale.value = withSpring(1)\n  }\n\n  return (\n    \u003CAnimatedPressable\n      onPress={onPress}\n      onPressIn={handlePressIn}\n      onPressOut={handlePressOut}\n      disabled={disabled}\n      style={[\n        styles.button,\n        styles[variant],\n        disabled && styles.disabled,\n        animatedStyle,\n      ]}\n    >\n      \u003CText style={[styles.text, styles[`${variant}Text`]]}>{title}\u003C\u002FText>\n    \u003C\u002FAnimatedPressable>\n  )\n}\n\n\u002F\u002F Platform-specific files\n\u002F\u002F Button.ios.tsx - iOS-specific implementation\n\u002F\u002F Button.android.tsx - Android-specific implementation\n\u002F\u002F Button.web.tsx - Web-specific implementation\n\n\u002F\u002F Or use Platform.select\nconst styles = StyleSheet.create({\n  button: {\n    paddingVertical: 12,\n    paddingHorizontal: 24,\n    borderRadius: 8,\n    alignItems: 'center',\n    ...Platform.select({\n      ios: {\n        shadowColor: '#000',\n        shadowOffset: { width: 0, height: 2 },\n        shadowOpacity: 0.1,\n        shadowRadius: 4,\n      },\n      android: {\n        elevation: 4,\n      },\n    }),\n  },\n  primary: {\n    backgroundColor: '#007AFF',\n  },\n  secondary: {\n    backgroundColor: '#5856D6',\n  },\n  outline: {\n    backgroundColor: 'transparent',\n    borderWidth: 1,\n    borderColor: '#007AFF',\n  },\n  disabled: {\n    opacity: 0.5,\n  },\n  text: {\n    fontSize: 16,\n    fontWeight: '600',\n  },\n  primaryText: {\n    color: '#FFFFFF',\n  },\n  secondaryText: {\n    color: '#FFFFFF',\n  },\n  outlineText: {\n    color: '#007AFF',\n  },\n})\n```\n\n### Pattern 6: Performance Optimization\n\n```typescript\n\u002F\u002F components\u002FProductList.tsx\nimport { FlashList } from '@shopify\u002Fflash-list'\nimport { memo, useCallback } from 'react'\n\ninterface ProductListProps {\n  products: Product[]\n  onProductPress: (id: string) => void\n}\n\n\u002F\u002F Memoize list item\nconst ProductItem = memo(function ProductItem({\n  item,\n  onPress,\n}: {\n  item: Product\n  onPress: (id: string) => void\n}) {\n  const handlePress = useCallback(() => onPress(item.id), [item.id, onPress])\n\n  return (\n    \u003CPressable onPress={handlePress} style={styles.item}>\n      \u003CFastImage\n        source={{ uri: item.image }}\n        style={styles.image}\n        resizeMode=\"cover\"\n      \u002F>\n      \u003CText style={styles.title}>{item.name}\u003C\u002FText>\n      \u003CText style={styles.price}>${item.price}\u003C\u002FText>\n    \u003C\u002FPressable>\n  )\n})\n\nexport function ProductList({ products, onProductPress }: ProductListProps) {\n  const renderItem = useCallback(\n    ({ item }: { item: Product }) => (\n      \u003CProductItem item={item} onPress={onProductPress} \u002F>\n    ),\n    [onProductPress]\n  )\n\n  const keyExtractor = useCallback((item: Product) => item.id, [])\n\n  return (\n    \u003CFlashList\n      data={products}\n      renderItem={renderItem}\n      keyExtractor={keyExtractor}\n      estimatedItemSize={100}\n      \u002F\u002F Performance optimizations\n      removeClippedSubviews={true}\n      maxToRenderPerBatch={10}\n      windowSize={5}\n      \u002F\u002F Pull to refresh\n      onRefresh={onRefresh}\n      refreshing={isRefreshing}\n    \u002F>\n  )\n}\n```\n\n## EAS Build & Submit\n\n```json\n\u002F\u002F eas.json\n{\n  \"cli\": { \"version\": \">= 5.0.0\" },\n  \"build\": {\n    \"development\": {\n      \"developmentClient\": true,\n      \"distribution\": \"internal\",\n      \"ios\": { \"simulator\": true }\n    },\n    \"preview\": {\n      \"distribution\": \"internal\",\n      \"android\": { \"buildType\": \"apk\" }\n    },\n    \"production\": {\n      \"autoIncrement\": true\n    }\n  },\n  \"submit\": {\n    \"production\": {\n      \"ios\": { \"appleId\": \"your@email.com\", \"ascAppId\": \"123456789\" },\n      \"android\": { \"serviceAccountKeyPath\": \".\u002Fgoogle-services.json\" }\n    }\n  }\n}\n```\n\n```bash\n# Build commands\neas build --platform ios --profile development\neas build --platform android --profile preview\neas build --platform all --profile production\n\n# Submit to stores\neas submit --platform ios\neas submit --platform android\n\n# OTA updates\neas update --branch production --message \"Bug fixes\"\n```\n\n## Best Practices\n\n### Do's\n- **Use Expo** - Faster development, OTA updates, managed native code\n- **FlashList over FlatList** - Better performance for long lists\n- **Memoize components** - Prevent unnecessary re-renders\n- **Use Reanimated** - 60fps animations on native thread\n- **Test on real devices** - Simulators miss real-world issues\n\n### Don'ts\n- **Don't inline styles** - Use StyleSheet.create for performance\n- **Don't fetch in render** - Use useEffect or React Query\n- **Don't ignore platform differences** - Test on both iOS and Android\n- **Don't store secrets in code** - Use environment variables\n- **Don't skip error boundaries** - Mobile crashes are unforgiving\n\n## Resources\n\n- [Expo Documentation](https:\u002F\u002Fdocs.expo.dev\u002F)\n- [Expo Router](https:\u002F\u002Fdocs.expo.dev\u002Frouter\u002Fintroduction\u002F)\n- [React Native Performance](https:\u002F\u002Freactnative.dev\u002Fdocs\u002Fperformance)\n- [FlashList](https:\u002F\u002Fshopify.github.io\u002Fflash-list\u002F)\n",{"name":5,"description":7},"main","7850740bd837adb32f0a109ab2af82482196c6f2","skills\u002F_collections\u002Fwshobson-agents\u002Fplugins\u002Ffrontend-mobile-development\u002Fskills\u002Freact-native-architecture",1,"https:\u002F\u002Fgithub.com\u002Fshengdabai\u002FTony-Claude-Code-Skills","https:\u002F\u002Fzturnsgo.com","https:\u002F\u002Fgithub.com\u002Fshengdabai","https:\u002F\u002Fzturnsgo.com\u002F","AI × education creator — producer, not consumer. Taught 6,000+ learners; shipping learning tools & daily essays with Claude Code, practicing @xiaolai's way 🥋","2026-11-01 21:28:23",{"source":8,"skillName":5,"commands":60,"prompt":70,"zipAvailable":71},[61,64,67],{"tool":62,"command":63},"npx","npx skills add shengdabai\u002FTony-Claude-Code-Skills --skill react-native-architecture",{"tool":65,"command":66},"bunx","bunx skills add shengdabai\u002FTony-Claude-Code-Skills --skill react-native-architecture",{"tool":68,"command":69},"pnpm","pnpm dlx skills add shengdabai\u002FTony-Claude-Code-Skills --skill react-native-architecture","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fshengdabai\u002FTony-Claude-Code-Skills\u002Fblob\u002Fmain\u002Fskills\u002F_collections\u002Fwshobson-agents\u002Fplugins\u002Ffrontend-mobile-development\u002Fskills\u002Freact-native-architecture\u002FSKILL.md",true,[],[74,91,124,149,170,190],{"skillNo":75,"name":76,"nameZh":6,"description":77,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":78,"categories":79,"keywords":83,"publishedAt":44},"01M70P17DAD4C1C47NPH6A96EM","xiaolai-methodology","以李笑来(Li Xiaolai)的思考方式、方法论与表达风格,为用户做全方位 1-on-1 教练指导,覆盖创业、赚钱、投资、学习、自学、读书写作、用 AI、以及人生心态与自我管理。当用户提到 \"李笑来\"、\"笑来\"、\"笑来老师\"、\"扮演李笑来\"、\"找笑来聊聊\"、\"笑来怎么看\"、\"xiaolai\"、\"把时间当作朋友\"、\"通往财富自由之路\"、\"定投改变命运\"、\"韭菜的自我修养\"、\"自学是门手艺\"、\"让时间陪你慢慢变富\"、\"七年就是一辈子\"、\"元认知\"、\"活在未来\"、\"复利\u002F长期主义\"、\"践行\"、\"痴迷改进\"、\"个人商业模式\u002F出售时间\"、\"睡后收入\"、\"定投\"、\"财富自由\"、\"操作系统升级\"、\"最少必要知识\"、\"北极星提示词\"、\"用 AI 该怎么学\u002F思考\" 等任一触发词,或当用户带着关于创业\u002F赚钱\u002F投资\u002F学习\u002F职业\u002F重大人生决策的困惑、希望被人用笑来式的「逻辑 + 大白话 + 类比 + 重新定义 + 践行导向」来点醒和指导时,自动进入李笑来教练模式。","https:\u002F\u002Fgithub.com\u002Fshengdabai\u002FTony-Claude-Code-Skills\u002Fblob\u002Fmain\u002Fxiaolai-methodology\u002FSKILL.md",[80],{"categoryNo":81,"categoryName":82,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000018","写作与文案",[84,85,86,87,88,89,90],"methodology","xiaolai","xiaolai methodology","朋友","投资","writing","收入",{"skillNo":92,"name":93,"nameZh":6,"description":94,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":95,"categories":96,"keywords":105,"publishedAt":44},"01M70P13NB9YP7ACJR1S861CDS","zread","Produce and consume a wiki-style knowledge base for a code repository via the `zread` CLI and its on-disk output under `.\u002F.zread\u002Fwiki\u002F`. Use this skill whenever the user wants to understand, onboard onto, explore, summarize, map, or get an overview of an unfamiliar codebase; asks for architecture docs, a project wiki, a repo walkthrough, module\u002Fpackage explanations, or \"what does this repo do\"; wants to generate, regenerate, resume, browse, or serve code documentation locally; or mentions zread \u002F zread.ai directly. Also use it proactively before diving into a large unknown repo — if `.\u002F.zread\u002Fwiki\u002Fcurrent` exists, read the generated pages instead of crawling source file-by-file; if it doesn't, consider offering to run `zread generate`. The trigger is the intent (understand a codebase through generated docs), not the literal word \"zread\".","https:\u002F\u002Fgithub.com\u002Fshengdabai\u002FTony-Claude-Code-Skills\u002Fblob\u002Fmain\u002Fskills\u002Fzread\u002FSKILL.md",[97,100],{"categoryNo":98,"categoryName":99,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000023","Skill与智能体开发",{"categoryNo":101,"categoryName":102,"taxonomyType":103,"taxonomyTypeDesc":104},"01JBSEEDCAT000000000000030","开发工程师","PROFESSION","适用职业",[93,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,24,121,122,123],"knowledge","wiki","explore","walkthrough","project","get","knowledge base","run","generate","intent","cli","map","documentation","project wiki","disk","code","unknown","generated",{"skillNo":125,"name":126,"nameZh":6,"description":127,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":128,"categories":129,"keywords":134,"publishedAt":44},"01M70P10S4SWMZQ8BJAFBFDRW3","youtube","Launch the YouTube Creator Agent to auto-generate videos.\nUse when: user types \u002Fyoutube, wants to create YouTube videos,\ngenerate video content, or upload to YouTube.\nKeywords: youtube, video, 视频, 创作视频, 发布视频","https:\u002F\u002Fgithub.com\u002Fshengdabai\u002FTony-Claude-Code-Skills\u002Fblob\u002Fmain\u002Fskills\u002Fyoutube\u002FSKILL.md",[130,131],{"categoryNo":98,"categoryName":99,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":132,"categoryName":133,"taxonomyType":103,"taxonomyTypeDesc":104},"01JBSEEDCAT000000000000036","内容创作者",[126,135,136,137,138,139,140,141,114,142,143,144,30,39,145,146,147,148],"user","content","视频","creator","generate video","videos","launch","video content","create","upload","auto","types","Video","创作",{"skillNo":150,"name":151,"nameZh":6,"description":152,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":153,"categories":154,"keywords":156,"publishedAt":44},"01M70P0YE95D8BACZCGV8BK90K","youtube-script-sync","Sync, update, or optimize Tony's GetNote knowledge base `Youtube视频逐字稿` by turning each parent recording or first-person idea note into an idempotent child note containing a directly recordable YouTube long-video or Shorts script. Use when the user says “更新\u002F同步\u002F优化 Youtube视频逐字稿知识库”, or says “更新\u002F同步\u002F优化该知识库” while this knowledge base is the active context.","https:\u002F\u002Fgithub.com\u002Fshengdabai\u002FTony-Claude-Code-Skills\u002Fblob\u002Fmain\u002Fskills\u002Fyoutube-script-sync\u002FSKILL.md",[155],{"categoryNo":98,"categoryName":99,"taxonomyType":21,"taxonomyTypeDesc":22},[43,157,126,158,159,147,160,161,162,163,164,165,166,167,112,168,30,39,106,169],"script","youtube script sync","recording","shorts","first","base","person","update","active","child","optimize","idempotent","long",{"skillNo":171,"name":172,"nameZh":6,"description":173,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":174,"categories":175,"keywords":180,"publishedAt":44},"01M70P0WMV5XT0EEC0N445PW07","writing-skills","How to write SKILL.md files that trigger reliably and teach effectively. Use when creating, improving, or reviewing Claude Code skills.","https:\u002F\u002Fgithub.com\u002Fshengdabai\u002FTony-Claude-Code-Skills\u002Fblob\u002Fmain\u002Fskills\u002Fxiaolai\u002Fnlpm\u002Fskills\u002Fnlpm\u002Fwriting-skills\u002FSKILL.md",[176,177,178,179],{"categoryNo":81,"categoryName":82,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":98,"categoryName":99,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":101,"categoryName":102,"taxonomyType":103,"taxonomyTypeDesc":104},{"categoryNo":132,"categoryName":133,"taxonomyType":103,"taxonomyTypeDesc":104},[181,89,182,183,184,185,121,30,39,186,187,188,189],"writing skills","how","teach","write skill","write","claude code skills","files","how to","trigger",{"skillNo":191,"name":192,"nameZh":6,"description":193,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":194,"categories":195,"keywords":198,"publishedAt":44},"01M70P0SJFR4YVJQBGMHKGSKXT","writing-rules","How to write .claude\u002Frules\u002F files that Claude actually follows. Use when creating, improving, or reviewing project rules.","https:\u002F\u002Fgithub.com\u002Fshengdabai\u002FTony-Claude-Code-Skills\u002Fblob\u002Fmain\u002Fskills\u002Fxiaolai\u002Fnlpm\u002Fskills\u002Fnlpm\u002Fwriting-rules\u002FSKILL.md",[196,197],{"categoryNo":81,"categoryName":82,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":132,"categoryName":133,"taxonomyType":103,"taxonomyTypeDesc":104},[199,89,200,110,201,187,30,182,185,39,188],"Rules","writing rules","project rules"]