Imported from em1-ly/Stream-Agri (
AGENTS.md). Install upstream withnpx skills add em1-ly/Stream-Agri. Copyright stays with the author.
AGENTS.md
This document provides guidelines for agentic coding agents working in this repository.
Project Overview
This is an Expo React Native application (Stream-ALP) built with:
- Expo SDK 54 with Expo Router (file-based routing)
- TypeScript (strict mode enabled)
- NativeWind (TailwindCSS for React Native)
- PowerSync (offline-first SQLite database with sync capabilities)
- Lucide React Native for icons
Build/Lint/Test Commands
# Install dependencies
npm install
# Start development server
npm start
# or
npx expo start
# Platform-specific builds
npm run android # Run on Android
npm run ios # Run on iOS
npm run web # Run on web
# Linting
npm run lint # Run ESLint via expo lint
# Testing
npm test # Run all tests with Jest (watch mode)
npm test -- --watchAll=false # Run tests once (no watch)
npm test -- --testPathPattern="ThemedText" # Run single test file
# Type checking
npx tsc --noEmit # Run TypeScript type checking
# Reset project (moves starter code to app-example)
npm run reset-project
Project Structure
├── app/ # Expo Router file-based routes
│ ├── _layout.tsx # Root layout (providers, fonts)
│ ├── (auth)/ # Authentication routes group
│ └── (app)/ # Main app routes (after auth)
│ └── (tabs)/ # Tab-based navigation
├── components/ # Reusable UI components
│ ├── __tests__/ # Jest tests and snapshots
│ └── ui/ # Platform-specific UI components
├── hooks/ # Custom React hooks
├── utils/ # Utility functions and services
├── powersync/ # Database schema and sync logic
├── constants/ # Theme colors and constants
├── authContext.tsx # Authentication context provider
└── NetworkContext.tsx # Network status context
Code Style Guidelines
Imports
// 1. React and React Native imports (alphabetically)
import { View, Text, TouchableOpacity } from 'react-native'
import React, { useCallback, useState, useEffect } from 'react'
// 2. Third-party libraries (alphabetically)
import { useNavigation, useRouter } from 'expo-router'
import { User, Settings } from 'lucide-react-native'
// 3. Internal imports using @ alias (alphabetically)
import { useSession } from '@/authContext'
import { powersync } from '@/powersync/system'
import { Colors } from '@/constants/Colors'
Components
- Use functional components with named exports
- Use arrow function syntax for component definitions
- Destructure props in function parameters
// Correct pattern
export function ThemedText({ style, lightColor, darkColor, type = 'default' }: ThemedTextProps) {
// Component logic
}
// Route components use default export
const index = () => {
return <View>...</View>
}
export default index
Styling
Use NativeWind (TailwindCSS) className prop for styling:
<View className='flex-1 p-4 bg-[#65435C]'>
<Text className='text-lg font-semibold text-[#1AD3BB]'>Title</Text>
<TouchableOpacity className='bg-white rounded-2xl p-4 shadow-sm'>
{/* content */}
</TouchableOpacity>
</View>
Theme Colors:
- Primary:
#65435C(dark purple) - Accent:
#1AD3BB(teal) - Use Tailwind utility classes combined with hex colors in brackets
TypeScript
- Strict mode is enabled - ensure all types are properly defined
- Define interfaces for component props:
export type ThemedTextProps = TextProps & {
lightColor?: string;
darkColor?: string;
type?: 'default' | 'title' | 'defaultSemiBold' | 'subtitle' | 'link';
};
- Use PowerSync generated types from Schema:
import { GrowerRecord, EmployeeRecord } from '@/powersync/Schema';
Naming Conventions
- Components: PascalCase (
ThemedText.tsx,HapticTab.tsx) - Hooks: camelCase with
useprefix (useThemeColor.ts,useColorScheme.ts) - Utils: camelCase (
exportUtils.ts,imageUploadService.ts) - Routes: lowercase with hyphens (
index.tsx,add-contact.tsx,[id].tsx) - Contexts: PascalCase with
Contextsuffix (AuthContext,NetworkContext)
Error Handling
Use try-catch with console.error for async operations:
const fetchData = async () => {
try {
const result = await powersync.execute('SELECT * FROM table');
setData(result.rows?._array || []);
} catch (error) {
console.error('Error fetching data:', error);
setData([]);
} finally {
setLoading(false);
}
};
Database Operations
PowerSync is used for local SQLite with sync:
// Execute queries
const result = await powersync.execute('SELECT * FROM growers WHERE id = ?', [id]);
// Get all rows
const rows = await powersync.getAll('SELECT * FROM employees');
// Access results
const data = result.rows?._array?.[0]?.column_name;
Context Pattern
Use React Context for global state:
// Create context with type definition
const AuthContext = createContext<{
logIn: () => Promise<boolean>;
session: User | null;
isLoading: boolean;
}>({...});
// Export hook for easy access
export function useSession() {
return use(AuthContext);
}
Testing
Tests use Jest with react-test-renderer for snapshots:
import * as React from 'react';
import renderer from 'react-test-renderer';
import { ThemedText } from '../ThemedText';
it('renders correctly', () => {
const tree = renderer.create(<ThemedText>Snapshot test!</ThemedText>).toJSON();
expect(tree).toMatchSnapshot();
});
Route Navigation
Expo Router uses file-based routing:
import { useRouter, Stack } from 'expo-router';
const router = useRouter();
// Navigate to route
router.push('/(app)/growers');
router.push(`/growers/${id}` as any);
// Configure header in component
<Stack.Screen options={{
headerTitle: 'Title',
headerShown: true
}} />
Secure Storage
Use expo-secure-store for sensitive data:
import * as SecureStore from 'expo-secure-store';
// Store
await SecureStore.setItemAsync('key', 'value');
// Retrieve
const value = await SecureStore.getItemAsync('key');
// Delete
await SecureStore.deleteItemAsync('key');
Environment Variables
Access via process.env.EXPO_PUBLIC_*:
const apiUrl = process.env.EXPO_PUBLIC_ODOO_SERVER_IP;
const database = process.env.EXPO_PUBLIC_ODOO_DATABASE;
Key Dependencies
expo-router: File-based routing@powersync/react-native: Offline-first databasenativewind: TailwindCSS stylinglucide-react-native: Iconsaxios: HTTP requestsexpo-secure-store: Secure local storage@sentry/react-native: Error tracking