Files
tabatago/AGENTS.md
Millian Lamiaux 791f432334 refactor: code quality cleanup — remove any types, add logger, rename Kine to Tabata
- Phase 0: Rename all Kine references to Tabata (types, files, imports, i18n, analytics events)
- Phase 1: Add test coverage for tabataProgramStore, workoutProgramStore, and color utils (47 tests)
- Phase 2: Remove all `any` types from production code with proper typed replacements
- Phase 3: Replace ~60 raw console.* calls with __DEV__-gated logger utility
- Phase 4: Verify .DS_Store housekeeping (already clean)

0 TypeScript errors, 583/583 tests passing.
2026-04-17 18:56:24 +02:00

550 lines
19 KiB
Markdown

# 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
```bash
# 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.**
1. **Start with Expo Go**: Run `npx expo start` and scan the QR code with Expo Go
2. **Check if features work**: Test your app thoroughly in Expo Go
3. **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
```bash
# 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.json` instead of relative imports
- Prefer aliases over relative imports for refactors
- Always use import statements at the top of the file
#### Library Preferences
-`expo-audio` not `expo-av`
-`expo-video` not `expo-av`
-`expo-image` with `source="sf:name"` for SF Symbols
-`react-native-safe-area-context` not react-native SafeAreaView
-`process.env.EXPO_OS` not `Platform.OS`
-`React.use` not `React.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 `app` directory
- Never co-locate components, types, or utilities in the app directory
- Ensure the app always has a route that matches "/"
- Always use `_layout.tsx` files 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 `useWindowDimensions` over `Dimensions.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 `contentContainerStyle` padding
- Add entering and exiting animations for state changes
#### Shadows
Use CSS `boxShadow` style prop. NEVER use legacy React Native shadow or elevation styles.
```tsx
<View style={{ boxShadow: "0 1px 2px rgba(0, 0, 0, 0.05)" }} />
```
#### Text Styling
- Add the `selectable` prop 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
```tsx
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
```tsx
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
```tsx
// 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 `.env` files
```bash
# .env
EXPO_PUBLIC_API_URL=https://api.example.com
EXPO_PUBLIC_SUPABASE_URL=your-supabase-url
```
#### API Client Pattern
```tsx
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
```tsx
// 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
```json
{
"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
```bash
# 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
1. **Start simple**: Always test in Expo Go before creating custom builds
2. **Follow conventions**: Use Expo Router patterns, kebab-case filenames
3. **Use modern APIs**: Prefer `expo-audio`, `expo-video`, `expo-image`
4. **Handle safe areas**: Use `contentInsetAdjustmentBehavior="automatic"`
5. **Style with CSS**: Use `boxShadow` instead of legacy shadow props
6. **Type everything**: Use TypeScript strict mode, no `any`
7. **Secure tokens**: Use `expo-secure-store` for sensitive data
8. **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
```typescript
BACKGROUND: '#000000' // Pure black
SURFACE: '#1C1C1E' // Charcoal
BRAND: '#FF6B35' // Flame orange
REST: '#5AC8FA' // Ice blue
SUCCESS: '#30D158' // Energy green
```
### Phase Colors (Critical)
```typescript
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 pages
- `context-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)` then `context-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 call
- `context-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
1. **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.
2. **FOLLOW-UP**: `context-mode_ctx_search(queries: ["q1", "q2", ...])` — Query indexed content. Pass ALL questions as array in ONE call.
3. **PROCESSING**: `context-mode_ctx_execute(language, code)` | `context-mode_ctx_execute_file(path, language, code)` — Sandbox execution. Only stdout enters context.
4. **WEB**: `context-mode_ctx_fetch_and_index(url, source)` then `context-mode_ctx_search(queries)` — Fetch, chunk, index, query. Raw HTML never enters context.
5. **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:start -->
# GitNexus — Code Intelligence
This project is indexed by GitNexus as **tabatago** (1839 symbols, 3401 relationships, 52 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 analyze` in 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"})`.
## When Debugging
1. `gitnexus_query({query: "<error or symptom>"})` — find execution flows related to the issue
2. `gitnexus_context({name: "<suspect function>"})` — see all callers, callees, and process participation
3. `READ gitnexus://repo/tabatago/process/{processName}` — trace the full execution flow step by step
4. For regressions: `gitnexus_detect_changes({scope: "compare", base_ref: "main"})` — see what your branch changed
## When Refactoring
- **Renaming**: MUST use `gitnexus_rename({symbol_name: "old", new_name: "new", dry_run: true})` first. Review the preview — graph edits are safe, text_search edits need manual review. Then run with `dry_run: false`.
- **Extracting/Splitting**: MUST run `gitnexus_context({name: "target"})` to see all incoming/outgoing refs, then `gitnexus_impact({target: "target", direction: "upstream"})` to find all external callers before moving code.
- After any refactor: run `gitnexus_detect_changes({scope: "all"})` to verify only expected files changed.
## Never Do
- NEVER edit a function, class, or method without first running `gitnexus_impact` on it.
- NEVER ignore HIGH or CRITICAL risk warnings from impact analysis.
- NEVER rename symbols with find-and-replace — use `gitnexus_rename` which understands the call graph.
- NEVER commit changes without running `gitnexus_detect_changes()` to check affected scope.
## Tools Quick Reference
| Tool | When to use | Command |
|------|-------------|---------|
| `query` | Find code by concept | `gitnexus_query({query: "auth validation"})` |
| `context` | 360-degree view of one symbol | `gitnexus_context({name: "validateUser"})` |
| `impact` | Blast radius before editing | `gitnexus_impact({target: "X", direction: "upstream"})` |
| `detect_changes` | Pre-commit scope check | `gitnexus_detect_changes({scope: "staged"})` |
| `rename` | Safe multi-file rename | `gitnexus_rename({symbol_name: "old", new_name: "new", dry_run: true})` |
| `cypher` | Custom graph queries | `gitnexus_cypher({query: "MATCH ..."})` |
## Impact Risk Levels
| Depth | Meaning | Action |
|-------|---------|--------|
| d=1 | WILL BREAK — direct callers/importers | MUST update these |
| d=2 | LIKELY AFFECTED — indirect deps | Should test |
| d=3 | MAY NEED TESTING — transitive | Test if critical path |
## 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 |
## Self-Check Before Finishing
Before completing any code modification task, verify:
1. `gitnexus_impact` was run for all modified symbols
2. No HIGH/CRITICAL risk warnings were ignored
3. `gitnexus_detect_changes()` confirms changes match expected scope
4. All d=1 (WILL BREAK) dependents were updated
## Keeping the Index Fresh
After committing code changes, the GitNexus index becomes stale. Re-run analyze to update it:
```bash
npx gitnexus analyze
```
If the index previously included embeddings, preserve them by adding `--embeddings`:
```bash
npx gitnexus analyze --embeddings
```
To check whether embeddings exist, inspect `.gitnexus/meta.json` — the `stats.embeddings` field shows the count (0 means no embeddings). **Running analyze without `--embeddings` will delete any previously generated embeddings.**
> Claude Code users: A PostToolUse hook handles this automatically after `git commit` and `git merge`.
## 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` |
<!-- gitnexus:end -->