16 KiB
AGENTS.md
This file contains optimizations and best practices for working with this TabataFit Expo project.
🚀 Command Optimizations
Use the following rtk commands for optimal performance:
| Command | rtk Equivalent | Speedup | Tokens Before | Tokens After | Savings |
|---|---|---|---|---|---|
ls / tree |
rtk ls |
10x | 2,000 | 400 | -80% |
cat / read |
rtk read |
20x | 40,000 | 12,000 | -70% |
grep / rg |
rtk grep |
8x | 16,000 | 3,200 | -80% |
git status |
rtk git status |
10x | 3,000 | 600 | -80% |
git diff |
rtk git diff |
5x | 10,000 | 2,500 | -75% |
git log |
rtk git log |
5x | 2,500 | 500 | -80% |
git add/commit/push |
rtk git |
8x | 1,600 | 120 | -92% |
cargo test / npm test |
rtk test |
5x | 25,000 | 2,500 | -90% |
ruff check |
rtk lint |
3x | 3,000 | 600 | -80% |
pytest |
rtk pytest |
4x | 8,000 | 800 | -90% |
go test |
rtk gotest |
3x | 6,000 | 600 | -90% |
docker ps |
rtk docker |
- | - | - | - |
Usage Examples
# Instead of: ls -la src/
rtk ls src/
# Instead of: cat package.json
rtk read package.json
# Instead of: grep -r "useEffect" src/
rtk grep "useEffect" src/
# Instead of: git status
rtk git status
# Instead of: npm test
rtk test
# Instead of: ruff check .
rtk lint
📱 Expo Best Practices
Development Workflow
CRITICAL: Always try Expo Go first before creating custom builds.
- Start with Expo Go: Run
npx expo startand scan the QR code with Expo Go - Check if features work: Test your app thoroughly in Expo Go
- Only create custom builds when required
When Custom Builds Are Required
Use npx expo run:ios/android or eas build ONLY when using:
- Local Expo modules (custom native code in
modules/) - Apple targets (widgets, app clips, extensions via
@bacons/apple-targets) - Third-party native modules not included in Expo Go
- Custom native configuration that can't be expressed in
app.json
When Expo Go Works
Expo Go supports a huge range of features out of the box:
- All
expo-*packages (camera, location, notifications, etc.) - Expo Router navigation
- Most UI libraries (reanimated, gesture handler, etc.)
- Push notifications, deep links, and more
Common Commands
# Development
npx expo start # Start development server
npx expo start --tunnel # If network issues
npx expo start --clear # Clear cache
npx tsc --noEmit # Type check
npx expo install <pkg> # Install Expo-compatible packages
# Building
npx eas-cli@latest build --profile development # Dev build
npx eas-cli@latest build -p ios --profile production --submit # Build and submit iOS
npx testflight # Quick TestFlight shortcut
# Development Client (when native code changes)
npx expo start --dev-client
Code Style Rules
File Naming
- Use kebab-case for file names (e.g.,
workout-card.tsx) - Never use special characters in file names
- Always remove old route files when moving or restructuring navigation
Imports
- Use path aliases from
tsconfig.jsoninstead of relative imports - Prefer aliases over relative imports for refactors
- Always use import statements at the top of the file
Library Preferences
- ✅
expo-audionotexpo-av - ✅
expo-videonotexpo-av - ✅
expo-imagewithsource="sf:name"for SF Symbols - ✅
react-native-safe-area-contextnot react-native SafeAreaView - ✅
process.env.EXPO_OSnotPlatform.OS - ✅
React.usenotReact.useContext - ❌ Never use modules removed from React Native: Picker, WebView, SafeAreaView, AsyncStorage
- ❌ Never use legacy expo-permissions
- ❌ Never use intrinsic elements like 'img' or 'div' unless in webview
Route Structure
app/
_layout.tsx # Root layout with tabs
(tabs)/ # Group routes for tabs
_layout.tsx # Tabs layout
index.tsx # Home tab
workouts.tsx # Workouts tab
activity.tsx # Activity tab
browse.tsx # Browse tab
profile.tsx # Profile tab
player/
[id].tsx # Workout player
Route Rules
- Routes belong in the
appdirectory - Never co-locate components, types, or utilities in the app directory
- Ensure the app always has a route that matches "/"
- Always use
_layout.tsxfiles to define stacks
UI Guidelines
Responsiveness
- Always wrap root component in a scroll view for responsiveness
- Use
<ScrollView contentInsetAdjustmentBehavior="automatic" />instead of<SafeAreaView> contentInsetAdjustmentBehavior="automatic"should be applied to FlatList and SectionList- Use flexbox instead of Dimensions API
- ALWAYS prefer
useWindowDimensionsoverDimensions.get()
Styling
- Prefer flex gap over margin and padding styles
- Prefer padding over margin where possible
- Inline styles not StyleSheet.create unless reusing styles is faster
- Use
{ borderCurve: 'continuous' }for rounded corners - ALWAYS use a navigation stack title instead of a custom text element
- When padding a ScrollView, use
contentContainerStylepadding - Add entering and exiting animations for state changes
Shadows
Use CSS boxShadow style prop. NEVER use legacy React Native shadow or elevation styles.
<View style={{ boxShadow: "0 1px 2px rgba(0, 0, 0, 0.05)" }} />
Text Styling
- Add the
selectableprop to every<Text/>element displaying important data - Counters should use
{ fontVariant: 'tabular-nums' }for alignment
Safe Area
- Always account for safe area with stack headers, tabs, or ScrollView/FlatList
contentInsetAdjustmentBehavior="automatic" - Ensure both top and bottom safe area insets are accounted for
Navigation Patterns
Stack Configuration
import { Stack } from 'expo-router/stack';
<Stack
screenOptions={{
headerTransparent: true,
headerShadowVisible: false,
headerLargeTitleShadowVisible: false,
headerLargeStyle: { backgroundColor: "transparent" },
headerTitleStyle: { color: PlatformColor("label") },
headerLargeTitle: true,
headerBlurEffect: "none",
headerBackButtonDisplayMode: "minimal",
}}
>
<Stack.Screen name="index" options={{ title: "Home" }} />
</Stack>
Links with Previews
import { Link } from 'expo-router';
<Link href="/settings">
<Link.Trigger>
<Pressable>
<Card />
</Pressable>
</Link.Trigger>
<Link.Preview />
<Link.Menu>
<Link.MenuAction title="Share" icon="square.and.arrow.up" />
<Link.MenuAction title="Delete" icon="trash" destructive />
</Link.Menu>
</Link>
Modal/Sheet Presentations
// Modal
<Stack.Screen name="modal" options={{ presentation: "modal" }} />
// Form Sheet
<Stack.Screen
name="sheet"
options={{
presentation: "formSheet",
sheetGrabberVisible: true,
sheetAllowedDetents: [0.5, 1.0],
contentStyle: { backgroundColor: "transparent" },
}}
/>
Data Fetching
Environment Variables
- Use
EXPO_PUBLIC_prefix for client-side environment variables - Never put secrets in
EXPO_PUBLIC_variables (visible in built app) - Restart the dev server after changing
.envfiles
# .env
EXPO_PUBLIC_API_URL=https://api.example.com
EXPO_PUBLIC_SUPABASE_URL=your-supabase-url
API Client Pattern
const BASE_URL = process.env.EXPO_PUBLIC_API_URL;
export const apiClient = {
get: async <T,>(path: string): Promise<T> => {
const response = await fetch(`${BASE_URL}${path}`);
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return response.json();
},
};
React Query Setup
// app/_layout.tsx
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 1000 * 60 * 5, // 5 minutes
retry: 2,
},
},
});
EAS Build Configuration
{
"cli": {
"version": ">= 16.0.1",
"appVersionSource": "remote"
},
"build": {
"production": {
"autoIncrement": true,
"ios": {
"resourceClass": "m-medium"
}
},
"development": {
"developmentClient": true,
"distribution": "internal"
}
},
"submit": {
"production": {}
}
}
Testing & Type Checking
# TypeScript
npx tsc --noEmit
# Run unit tests (Vitest)
npm test
# Run tests in watch mode
npm run test:watch
# Run tests with coverage report
npm run test:coverage
# Run Maestro E2E tests
npm run test:maestro
# Lint
npx eslint .
Test Structure
src/__tests__/
setup.ts # Mocks and test configuration
stores/ # Zustand store tests
hooks/ # React hooks tests
services/ # Service layer tests
components/ # Component logic tests
data/ # Data validation tests
Coverage Goals
- Stores: 80%+ (business logic)
- Services: 80%+ (API integration)
- Hooks: 70%+ (timer, purchases)
- Components: 50%+ (critical UI)
Key Takeaways
- Start simple: Always test in Expo Go before creating custom builds
- Follow conventions: Use Expo Router patterns, kebab-case filenames
- Use modern APIs: Prefer
expo-audio,expo-video,expo-image - Handle safe areas: Use
contentInsetAdjustmentBehavior="automatic" - Style with CSS: Use
boxShadowinstead of legacy shadow props - Type everything: Use TypeScript strict mode, no
any - Secure tokens: Use
expo-secure-storefor sensitive data - Environment config: Use
EXPO_PUBLIC_prefix for client env vars
📝 Project Context
TabataFit — "Apple Fitness+ for Tabata"
- Framework: Expo SDK 52 (managed)
- Navigation: Expo Router v3
- State: Zustand + AsyncStorage
- Video: expo-av → HLS streaming
- Audio: expo-av (coaching + music)
- Animations: React Native Animated
- Payments: RevenueCat
- Analytics: PostHog
Design System
BACKGROUND: '#000000' // Pure black
SURFACE: '#1C1C1E' // Charcoal
BRAND: '#FF6B35' // Flame orange
REST: '#5AC8FA' // Ice blue
SUCCESS: '#30D158' // Energy green
Phase Colors (Critical)
PREP: '#FF9500' // Orange-yellow
WORK: '#FF6B35' // Flame orange
REST: '#5AC8FA' // Ice blue
COMPLETE: '#30D158' // Green
Last updated: March 14, 2026
context-mode — MANDATORY routing rules
You have context-mode MCP tools available. These rules are NOT optional — they protect your context window from flooding. A single unrouted command can dump 56 KB into context and waste the entire session.
BLOCKED commands — do NOT attempt these
curl / wget — BLOCKED
Any shell command containing curl or wget will be intercepted and blocked by the context-mode plugin. Do NOT retry.
Instead use:
context-mode_ctx_fetch_and_index(url, source)to fetch and index web pagescontext-mode_ctx_execute(language: "javascript", code: "const r = await fetch(...)")to run HTTP calls in sandbox
Inline HTTP — BLOCKED
Any shell command containing fetch('http, requests.get(, requests.post(, http.get(, or http.request( will be intercepted and blocked. Do NOT retry with shell.
Instead use:
context-mode_ctx_execute(language, code)to run HTTP calls in sandbox — only stdout enters context
Direct web fetching — BLOCKED
Do NOT use any direct URL fetching tool. Use the sandbox equivalent. Instead use:
context-mode_ctx_fetch_and_index(url, source)thencontext-mode_ctx_search(queries)to query the indexed content
REDIRECTED tools — use sandbox equivalents
Shell (>20 lines output)
Shell is ONLY for: git, mkdir, rm, mv, cd, ls, npm install, pip install, and other short-output commands.
For everything else, use:
context-mode_ctx_batch_execute(commands, queries)— run multiple commands + search in ONE callcontext-mode_ctx_execute(language: "shell", code: "...")— run in sandbox, only stdout enters context
File reading (for analysis)
If you are reading a file to edit it → reading is correct (edit needs content in context).
If you are reading to analyze, explore, or summarize → use context-mode_ctx_execute_file(path, language, code) instead. Only your printed summary enters context.
grep / search (large results)
Search results can flood context. Use context-mode_ctx_execute(language: "shell", code: "grep ...") to run searches in sandbox. Only your printed summary enters context.
Tool selection hierarchy
- GATHER:
context-mode_ctx_batch_execute(commands, queries)— Primary tool. Runs all commands, auto-indexes output, returns search results. ONE call replaces 30+ individual calls. - FOLLOW-UP:
context-mode_ctx_search(queries: ["q1", "q2", ...])— Query indexed content. Pass ALL questions as array in ONE call. - PROCESSING:
context-mode_ctx_execute(language, code)|context-mode_ctx_execute_file(path, language, code)— Sandbox execution. Only stdout enters context. - WEB:
context-mode_ctx_fetch_and_index(url, source)thencontext-mode_ctx_search(queries)— Fetch, chunk, index, query. Raw HTML never enters context. - INDEX:
context-mode_ctx_index(content, source)— Store content in FTS5 knowledge base for later search.
Output constraints
- Keep responses under 500 words.
- Write artifacts (code, configs, PRDs) to FILES — never return them as inline text. Return only: file path + 1-line description.
- When indexing content, use descriptive source labels so others can
search(source: "label")later.
ctx commands
| Command | Action |
|---|---|
ctx stats |
Call the stats MCP tool and display the full output verbatim |
ctx doctor |
Call the doctor MCP tool, run the returned shell command, display as checklist |
ctx upgrade |
Call the upgrade MCP tool, run the returned shell command, display as checklist |
GitNexus — Code Intelligence
This project is indexed by GitNexus as tabatago (3362 symbols, 9407 relationships, 129 execution flows). Use the GitNexus MCP tools to understand code, assess impact, and navigate safely.
If any GitNexus tool warns the index is stale, run
npx gitnexus analyzein terminal first.
Always Do
- MUST run impact analysis before editing any symbol. Before modifying a function, class, or method, run
gitnexus_impact({target: "symbolName", direction: "upstream"})and report the blast radius (direct callers, affected processes, risk level) to the user. - MUST run
gitnexus_detect_changes()before committing to verify your changes only affect expected symbols and execution flows. - MUST warn the user if impact analysis returns HIGH or CRITICAL risk before proceeding with edits.
- When exploring unfamiliar code, use
gitnexus_query({query: "concept"})to find execution flows instead of grepping. It returns process-grouped results ranked by relevance. - When you need full context on a specific symbol — callers, callees, which execution flows it participates in — use
gitnexus_context({name: "symbolName"}).
Never Do
- NEVER edit a function, class, or method without first running
gitnexus_impacton it. - NEVER ignore HIGH or CRITICAL risk warnings from impact analysis.
- NEVER rename symbols with find-and-replace — use
gitnexus_renamewhich understands the call graph. - NEVER commit changes without running
gitnexus_detect_changes()to check affected scope.
Resources
| Resource | Use for |
|---|---|
gitnexus://repo/tabatago/context |
Codebase overview, check index freshness |
gitnexus://repo/tabatago/clusters |
All functional areas |
gitnexus://repo/tabatago/processes |
All execution flows |
gitnexus://repo/tabatago/process/{name} |
Step-by-step execution trace |
CLI
| Task | Read this skill file |
|---|---|
| Understand architecture / "How does X work?" | .claude/skills/gitnexus/gitnexus-exploring/SKILL.md |
| Blast radius / "What breaks if I change X?" | .claude/skills/gitnexus/gitnexus-impact-analysis/SKILL.md |
| Trace bugs / "Why is X failing?" | .claude/skills/gitnexus/gitnexus-debugging/SKILL.md |
| Rename / extract / split / refactor | .claude/skills/gitnexus/gitnexus-refactoring/SKILL.md |
| Tools, resources, schema reference | .claude/skills/gitnexus/gitnexus-guide/SKILL.md |
| Index, status, clean, wiki CLI commands | .claude/skills/gitnexus/gitnexus-cli/SKILL.md |