@shaykec/app-agent 1.0.8 → 1.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (25) hide show
  1. package/.claude/agents/android-customizer.md +9 -1
  2. package/.claude/agents/ios-customizer.md +9 -1
  3. package/.claude/agents/react-native-customizer.md +71 -0
  4. package/.claude/skills/android-customizer/SKILL.md +95 -23
  5. package/.claude/skills/ios-customizer/SKILL.md +102 -23
  6. package/.claude/skills/react-native-customizer/SKILL.md +85 -11
  7. package/.cursor/agents/android-customizer.md +15 -11
  8. package/.cursor/agents/ios-customizer.md +15 -10
  9. package/.cursor/agents/react-native-customizer.md +170 -0
  10. package/.cursor/mcp.json +2 -10
  11. package/.cursor/skills/android-customizer/SKILL.md +3 -3
  12. package/.cursor/skills/ios-customizer/SKILL.md +3 -3
  13. package/.cursor/skills/react-native-customizer/SKILL.md +69 -9
  14. package/package.json +1 -1
  15. package/templates/android/Skeleton/TESTING_MANIFEST.md +2 -1
  16. package/templates/android/Skeleton/app/src/main/kotlin/com/appship/skeleton/MainActivity.kt +23 -2
  17. package/templates/android/Skeleton/app/src/main/kotlin/com/appship/skeleton/core/theme/AppearanceManager.kt +42 -0
  18. package/templates/android/Skeleton/app/src/main/kotlin/com/appship/skeleton/features/profile/ProfileScreen.kt +20 -8
  19. package/templates/android/Skeleton/tests/03_detail_screen.yaml +2 -1
  20. package/templates/android/Skeleton/tests/04_favorites.yaml +2 -1
  21. package/templates/android/Skeleton/tests/08_full_e2e.yaml +2 -1
  22. package/templates/android/Skeleton/tests/09_dark_mode.yaml +50 -0
  23. package/templates/ios/Skeleton/tests/09_dark_mode.yaml +52 -0
  24. package/templates/react-native/Skeleton/TESTING_MANIFEST.md +1 -0
  25. package/templates/react-native/Skeleton/tests/09_dark_mode.yaml +46 -0
@@ -48,10 +48,18 @@ POTENTIAL ISSUES:
48
48
  - {concerns needing manual review}
49
49
  ```
50
50
 
51
+ ### Data Layer
52
+ - Use `DataSourceResolver.repository`, not `MockDataProvider` directly
53
+ - Preserve MockDataProvider CRUD functions and `reset()` method
54
+ - Do NOT modify NetworkMonitor, LocalPersistence, or OfflineBanner
55
+
51
56
  ## Rules
52
57
  - ONLY edit files under `output/`
53
58
  - Ensure all package declarations match directory structure
54
59
  - Keep Hilt annotations intact (@HiltAndroidApp, @HiltViewModel, @AndroidEntryPoint)
55
60
  - Keep build.gradle.kts syntactically valid
56
- - Preserve MockDataProvider function signatures
61
+ - Preserve MockDataProvider CRUD functions and `reset()` method
62
+ - Do NOT modify NetworkMonitor, LocalPersistence, or OfflineBanner
57
63
  - Use Material 3 components (not legacy Material 2)
64
+ - Preserve ALL `Modifier.testTag()` calls — do NOT remove test IDs
65
+ - Follow `{screen}_{element}_{role}` convention for new test tags (see `templates/TEST_ID_CONVENTIONS.md`)
@@ -46,9 +46,17 @@ POTENTIAL ISSUES:
46
46
  - {concerns needing manual review}
47
47
  ```
48
48
 
49
+ ### Data Layer
50
+ - Use `DataSourceResolver.repository` in ViewModels, not `MockDataProvider.shared` directly
51
+ - Preserve MockDataProvider CRUD methods and `reset()` function
52
+ - Do NOT modify NetworkMonitor, LocalPersistence, or OfflineBanner
53
+
49
54
  ## Rules
50
55
  - ONLY edit files under `output/` — never touch `templates/`
51
56
  - Ensure all imports resolve after changes
52
57
  - Keep `@main` struct and `App` protocol conformance intact
53
- - Preserve MockDataProvider function signatures
58
+ - Preserve MockDataProvider CRUD methods and `reset()` function
59
+ - Do NOT modify NetworkMonitor, LocalPersistence, or OfflineBanner
54
60
  - Use SF Symbols for tab and navigation icons
61
+ - Preserve ALL `.accessibilityIdentifier()` calls — do NOT remove test IDs
62
+ - Follow `{screen}_{element}_{role}` convention for new test IDs (see `templates/TEST_ID_CONVENTIONS.md`)
@@ -0,0 +1,71 @@
1
+ ---
2
+ name: react-native-customizer
3
+ description: Specialized React Native/TypeScript subagent for customizing React Native app templates. Handles package config updates, component rewriting, React Navigation adjustments, theme application, and cross-platform features.
4
+ tools: Read, Edit, Write, Grep, Glob, LS, Bash
5
+ model: sonnet
6
+ ---
7
+
8
+ You are an expert React Native developer specializing in TypeScript and modern mobile development. You handle all React Native-specific customization tasks for the AppAgent pipeline.
9
+
10
+ ## Your Expertise
11
+ - React Native 0.73+, TypeScript 5+, React Navigation 6+
12
+ - MVVM architecture with custom hooks (`useXxxViewModel`)
13
+ - StyleSheet.create() for performant styling
14
+ - FlatList / SectionList for virtualized lists
15
+ - React Context for theming and global state
16
+
17
+ ## Tasks You Handle
18
+
19
+ ### Package & Project Renaming
20
+ 1. Update `package.json` — `name` field
21
+ 2. Update `app.json` — `name` and `displayName`
22
+ 3. Update `android/app/build.gradle` — `applicationId`, `namespace`
23
+ 4. Update `android/settings.gradle` — `rootProject.name`
24
+ 5. Update `android/app/src/main/res/values/strings.xml` — `app_name`
25
+ 6. Update `android/app/src/main/java/` — package directories, `MainActivity.kt`, `MainApplication.kt`
26
+ 7. Update `ios/Podfile` — project target name
27
+ 8. Update all imports if module paths changed
28
+
29
+ ### React Native Component Customization
30
+ - Use functional components with `React.FC` typing
31
+ - Use custom hooks (`useXxxViewModel`) for state and business logic
32
+ - Use `StyleSheet.create()` for all styles — no inline style objects
33
+ - Use `FlatList` / `SectionList` for lists (not `map()` on `ScrollView`)
34
+ - Add `testID` props to ALL interactive elements
35
+
36
+ ### Navigation Structure
37
+ Templates use React Navigation with bottom tabs and native stack. When modifying:
38
+ - Update tab names and components in `AppNavigator.tsx`
39
+ - Update `RootStackParamList` type for type-safe navigation
40
+ - Apply tab labels from content brief and icons from design brief
41
+
42
+ ### Theme Customization
43
+ - Update `ThemeContext.tsx` colors to match brand palette
44
+ - Provide both light and dark mode variants
45
+ - Apply corner radius, spacing, and typography tokens
46
+
47
+ ## Output
48
+ ```
49
+ FILES MODIFIED:
50
+ - {path}: {what changed}
51
+ POTENTIAL ISSUES:
52
+ - {concerns needing manual review}
53
+ ```
54
+
55
+ ### Data Layer
56
+ - Use `DataSourceResolver.repository` in ViewModel hooks, not `MockDataProvider` directly
57
+ - Preserve MockDataProvider CRUD methods and `reset()` function
58
+ - Do NOT modify NetworkMonitor, LocalPersistence, or OfflineBanner
59
+
60
+ ## Rules
61
+ - ONLY edit files under `output/` — never touch `templates/`
62
+ - Ensure all imports resolve after changes
63
+ - Preserve MockDataProvider CRUD methods and `reset()` function
64
+ - Do NOT modify NetworkMonitor, LocalPersistence, or OfflineBanner
65
+ - Ensure OfflineBanner is integrated into main navigation screens
66
+ - Use `testID` props on ALL interactive elements
67
+ - Follow `{screen}_{element}_{role}` convention for test IDs (see `templates/TEST_ID_CONVENTIONS.md`)
68
+ - Use `StyleSheet.create()` for styles, not inline objects
69
+ - Keep React Navigation structure intact
70
+ - Preserve ThemeContext provider wrapping in App.tsx
71
+ - Do NOT introduce new npm dependencies
@@ -9,7 +9,7 @@ Use this skill to perform deep Android/Kotlin/Compose customization on a cloned
9
9
 
10
10
  ## When to Use
11
11
 
12
- - At Step 8 of the AppAgent workflow, when the platform is Android
12
+ - At Step 5 of the AppAgent workflow, when the platform is Android
13
13
  - After AppConfig, mock data, and design tokens are already in place
14
14
  - When you need to modify Compose screens, navigation, and themes
15
15
 
@@ -102,6 +102,17 @@ Android templates use Material 3 dynamic theming in `Theme.kt`:
102
102
  - Apply typography scale if the design brief specifies custom fonts
103
103
  - Apply shape theme (corner radius) from the design brief
104
104
 
105
+ ### Step 5.5 — Generate App Launcher Icon
106
+
107
+ Customize the adaptive icon using the manifest's **App Launcher Icon** section:
108
+
109
+ 1. Read the icon spec: `Android vector keyword` (e.g., `shopping`) and `Background` hex color
110
+ 2. Read `templates/android/ICON_VECTORS.md`, find the matching keyword section, copy the XML into `app/src/main/res/drawable/ic_launcher_foreground.xml`
111
+ 3. Update `app/src/main/res/values/colors.xml`: set `ic_launcher_background` to the background hex
112
+ 4. Verify `ic_launcher.xml` and `ic_launcher_round.xml` reference the correct foreground/background
113
+
114
+ If `ICON_VECTORS.md` does not exist or the keyword is not found, keep the default icon and only update the background color.
115
+
105
116
  ### Step 6 — Data Layer & Offline Infrastructure
106
117
 
107
118
  Templates now include an **offline-first data layer**. Understand these components:
@@ -119,19 +130,26 @@ Templates now include an **offline-first data layer**. Understand these componen
119
130
  - `reset()` function references correct defaults
120
131
  - `data/SyncManager.kt` — if entity types were renamed, update domain methods
121
132
 
122
- **ViewModel/Screen patterns** (how screens consume data):
133
+ **Screen/ViewModel patterns** (how screens consume data):
134
+
135
+ Screens and ViewModels use the `DataRepository` interface abstraction, not `MockDataProvider` directly:
123
136
  ```kotlin
124
- // Screens collect StateFlow
125
- val products by MockDataProvider.products.collectAsState()
137
+ // Screens get the repository from DataSourceResolver
138
+ val repository = remember { DataSourceResolver.repository }
139
+ val items by repository.items.collectAsState()
140
+ val categories by repository.categories.collectAsState()
126
141
 
127
- // ViewModels read from MockDataProvider
128
- val products = MockDataProvider.products.value
142
+ // CRUD via the repository
143
+ repository.addItem(newItem)
144
+ repository.deleteItem(item)
145
+ repository.toggleFavorite(item)
129
146
 
130
- // CRUD via SyncManager
147
+ // If SyncManager exists, it wraps the repository
131
148
  SyncManager.addProduct(newProduct)
132
- SyncManager.deleteProduct(id)
133
149
  ```
134
150
 
151
+ When customizing screens or ViewModels, ALWAYS use `DataSourceResolver.repository`, never `MockDataProvider` directly. This ensures the app works with any data source.
152
+
135
153
  **OfflineBanner integration** — add to main navigation:
136
154
  ```kotlin
137
155
  Column {
@@ -151,26 +169,75 @@ class MyApplication : Application() {
151
169
  }
152
170
  ```
153
171
 
154
- ### Step 7 — MockDataProvider Verification
172
+ ### Step 7 — Data Layer Verification
155
173
 
156
- Verify MockDataProvider uses the correct patterns:
174
+ Verify the data layer uses the correct patterns:
157
175
 
176
+ **DataRepository interface:**
158
177
  ```kotlin
159
- object MockDataProvider {
178
+ interface DataRepository {
179
+ val items: StateFlow<List<Item>>
180
+ val categories: StateFlow<List<Category>>
181
+ val favoriteItems: StateFlow<List<Item>>
182
+ fun loadData()
183
+ fun addItem(item: Item)
184
+ // ... all CRUD + query methods
185
+ }
186
+ ```
187
+
188
+ **MockDataProvider (implements DataRepository):**
189
+ ```kotlin
190
+ object MockDataProvider : DataRepository {
160
191
  private val _products = MutableStateFlow(defaultProducts)
161
- val products: StateFlow<List<Product>> = _products.asStateFlow()
162
-
163
- val defaultProducts = listOf(...) // Seed data
164
-
165
- fun addProduct(product: Product) { ... } // CRUD
166
- fun reset() { ... } // Reset to defaults
192
+ override val products: StateFlow<List<Product>> = _products.asStateFlow()
193
+ val defaultProducts = listOf(...)
194
+ override fun addProduct(product: Product) { ... }
195
+ fun reset() { ... }
196
+ }
197
+ ```
198
+
199
+ **DataSourceResolver:**
200
+ ```kotlin
201
+ object DataSourceResolver {
202
+ val repository: DataRepository
203
+ get() = when (AppConfig.DataSource.active) {
204
+ AppConfig.DataSource.SourceType.MOCK -> MockDataProvider
205
+ AppConfig.DataSource.SourceType.LOCAL_STORAGE -> MockDataProvider // TODO
206
+ }
167
207
  }
168
208
  ```
169
209
 
170
- - Verify seed data matches the new domain
171
- - Ensure CRUD functions reference correct entity types
172
- - Verify `reset()` works correctly
173
- - Check that helper functions use `.value` to read from StateFlows
210
+ Verify:
211
+ - DataRepository interface matches MockDataProvider's public members
212
+ - MockDataProvider implements DataRepository with `override` on all members
213
+ - DataSourceResolver returns the correct implementation
214
+ - Screens use `DataSourceResolver.repository`, not `MockDataProvider` directly
215
+ - Seed data matches the new domain
216
+ - CRUD functions reference correct entity types
217
+
218
+ ### Step 7.5 — Apply Animation Modifiers (if animation modules are integrated)
219
+
220
+ If the customization manifest includes an **"Animation & Sensory Plan"** section:
221
+
222
+ 1. Read the "Per-Screen Animation Modifiers" table from the manifest
223
+ 2. For each screen in your batch, apply the assigned modifiers:
224
+
225
+ ```kotlin
226
+ // MicroInteractions — press feedback
227
+ Button(onClick = { }, modifier = Modifier.scaleOnPress().hapticFeedback(HapticFeedbackStyle.IMPACT_MEDIUM))
228
+
229
+ // ScrollEffects — parallax header
230
+ ParallaxHeader(height = 280.dp, scrollState = scrollState) { AsyncImage(model = item.imageUrl) }
231
+
232
+ // AnimatedTransitions — staggered list items
233
+ ItemRow(item = item, modifier = Modifier.staggeredAppearance(index = index))
234
+
235
+ // CelebrationEffects — success overlay
236
+ Box(modifier = Modifier.celebrationOverlay(isActive = showCelebration, style = CelebrationStyle.CONFETTI))
237
+ ```
238
+
239
+ 3. Wrap all applied animations with `motionAwareAnimationSpec()` if MotionPreferences is integrated
240
+ 4. Do NOT add animation modifiers to screens not listed in the manifest
174
241
 
175
242
  ## Report Output
176
243
 
@@ -179,7 +246,7 @@ Write the report to `output/{app-name}/reports/05-customization.md` (append Andr
179
246
  ```markdown
180
247
  # Customization Report
181
248
 
182
- **Step:** 8
249
+ **Step:** 5
183
250
  **Skill:** android-customizer
184
251
  **Timestamp:** {ISO 8601}
185
252
  **Result:** PASS
@@ -214,6 +281,11 @@ Android (Kotlin / Jetpack Compose)
214
281
  - SyncManager methods updated: {list or "no changes needed"}
215
282
  - Data persistence verified: {yes/no}
216
283
 
284
+ ## Animation Modifiers Applied
285
+
286
+ - {modifier}: applied to {screen} — {description}
287
+ - MotionPreferences respected: {yes/no/N/A}
288
+
217
289
  ## Potential Issues
218
290
 
219
291
  - {any concerns or items needing manual review}
@@ -225,7 +297,7 @@ Update `output/{app-name}/reports/summary.json` — read the file, append this s
225
297
 
226
298
  ```json
227
299
  {
228
- "step": 8,
300
+ "step": 5,
229
301
  "name": "android-customization",
230
302
  "startedAt": "{ISO 8601 timestamp}",
231
303
  "durationSeconds": 0,
@@ -9,7 +9,7 @@ Use this skill to perform deep iOS/SwiftUI customization on a cloned template.
9
9
 
10
10
  ## When to Use
11
11
 
12
- - At Step 8 of the AppAgent workflow, when the platform is iOS
12
+ - At Step 5 of the AppAgent workflow, when the platform is iOS
13
13
  - After AppConfig, mock data, and design tokens are already in place
14
14
  - When you need to modify SwiftUI views, navigation, and themes
15
15
 
@@ -100,6 +100,16 @@ iOS templates use a `Colors.swift` theme file:
100
100
  - Use semantic colors where possible (`Color.primary`, `Color.secondary`)
101
101
  - Apply corner radius and spacing tokens from the design brief
102
102
 
103
+ ### Step 4.5 — Generate App Launcher Icon
104
+
105
+ Generate a domain-specific app icon using the manifest's **App Launcher Icon** section:
106
+
107
+ 1. Read the icon spec: `iOS SF Symbol` (e.g., `bag.fill`) and `Background` hex color
108
+ 2. Run: `swift scripts/generate-app-icon.swift "{symbol}" "{background}" "output/{app-name}/{AppName}/Resources/Assets.xcassets/AppIcon.appiconset/"`
109
+ 3. Verify `AppIcon-1024.png` exists in the output directory
110
+
111
+ If `scripts/generate-app-icon.swift` does not exist or fails, skip — the template's default icon will be used.
112
+
103
113
  ### Step 5 — Data Layer & Offline Infrastructure
104
114
 
105
115
  Templates now include an **offline-first data layer**. Understand these components:
@@ -118,18 +128,31 @@ Templates now include an **offline-first data layer**. Understand these componen
118
128
  - `Data/SyncManager.swift` — if entity types were renamed, update domain methods
119
129
 
120
130
  **ViewModel patterns** (how views consume data):
131
+
132
+ ViewModels use the `DataRepository` protocol abstraction, not `MockDataProvider` directly:
121
133
  ```swift
122
- // ViewModels observe MockDataProvider.shared
123
- @ObservedObject var store = MockDataProvider.shared
134
+ // ViewModels depend on the DataRepository protocol
135
+ private let repository: any DataRepository
136
+
137
+ init(repository: any DataRepository = DataSourceResolver.repository) {
138
+ self.repository = repository
139
+ }
140
+
141
+ // Read data via the protocol
142
+ let items = repository.items
143
+ let categories = repository.categories
124
144
 
125
- // Or use EnvironmentObject if injected
126
- @EnvironmentObject var store: MockDataProvider
145
+ // CRUD via the protocol
146
+ repository.addItem(newItem)
147
+ repository.deleteItem(item)
148
+ repository.toggleFavorite(item)
127
149
 
128
- // CRUD via SyncManager
150
+ // If SyncManager exists, it wraps the repository
129
151
  SyncManager.shared.addProduct(newProduct)
130
- SyncManager.shared.deleteProduct(id)
131
152
  ```
132
153
 
154
+ When customizing ViewModels, ALWAYS use `repository` (from `DataSourceResolver`), never `MockDataProvider.shared` directly. This ensures the app works with any data source.
155
+
133
156
  **OfflineBanner integration** — add to main navigation views:
134
157
  ```swift
135
158
  VStack(spacing: 0) {
@@ -138,27 +161,78 @@ VStack(spacing: 0) {
138
161
  }
139
162
  ```
140
163
 
141
- ### Step 6 — MockDataProvider Verification
164
+ ### Step 6 — Data Layer Verification
142
165
 
143
- Verify the MockDataProvider uses the correct patterns:
166
+ Verify the data layer uses the correct patterns:
144
167
 
168
+ **DataRepository protocol:**
145
169
  ```swift
146
- class MockDataProvider: ObservableObject {
170
+ protocol DataRepository: AnyObject {
171
+ var items: [Item] { get }
172
+ var categories: [Category] { get }
173
+ var favoriteItems: [Item] { get }
174
+ func loadData()
175
+ func addItem(_ item: Item)
176
+ // ... all CRUD + query methods
177
+ }
178
+ ```
179
+
180
+ **MockDataProvider (conforms to DataRepository):**
181
+ ```swift
182
+ class MockDataProvider: ObservableObject, DataRepository {
147
183
  static let shared = MockDataProvider()
148
-
149
- @Published var products: [Product] // Reactive, mutable
150
-
151
- static let defaultProducts: [Product] = [...] // Seed data
152
-
153
- func addProduct(_ product: Product) { ... } // CRUD
154
- func reset() { ... } // Reset to defaults
184
+ @Published var products: [Product]
185
+ static let defaultProducts: [Product] = [...]
186
+ func addProduct(_ product: Product) { ... }
187
+ func reset() { ... }
188
+ }
189
+ ```
190
+
191
+ **DataSourceResolver:**
192
+ ```swift
193
+ enum DataSourceResolver {
194
+ static var repository: any DataRepository {
195
+ switch AppConfig.DataSource.active {
196
+ case .mock: return MockDataProvider.shared
197
+ case .localStorage: return MockDataProvider.shared // TODO: LocalStorageProvider
198
+ }
199
+ }
155
200
  }
156
201
  ```
157
202
 
158
- - Verify seed data matches the new domain
159
- - Ensure CRUD methods reference correct entity types
160
- - Verify `reset()` works correctly
161
- - Check that `@Published` properties are being observed by ViewModels
203
+ Verify:
204
+ - DataRepository protocol matches MockDataProvider's public interface
205
+ - MockDataProvider conforms to DataRepository
206
+ - DataSourceResolver returns the correct implementation
207
+ - ViewModels use `DataSourceResolver.repository`, not `MockDataProvider.shared` directly
208
+ - Seed data matches the new domain
209
+ - CRUD methods reference correct entity types
210
+
211
+ ### Step 6.5 — Apply Animation Modifiers (if animation modules are integrated)
212
+
213
+ If the customization manifest includes an **"Animation & Sensory Plan"** section:
214
+
215
+ 1. Read the "Per-Screen Animation Modifiers" table from the manifest
216
+ 2. For each screen in your batch, apply the assigned modifiers:
217
+
218
+ ```swift
219
+ // MicroInteractions — press feedback
220
+ Button("Add to Cart") { ... }
221
+ .scaleOnPress()
222
+ .hapticFeedback(.impact(.medium))
223
+
224
+ // ScrollEffects — parallax header
225
+ ParallaxHeaderView(height: 280) { AsyncImage(url: item.imageURL) }
226
+
227
+ // AnimatedTransitions — staggered list items
228
+ ItemRow(item: items[index]).staggeredAppearance(index: index)
229
+
230
+ // CelebrationEffects — success overlay
231
+ .celebrationOverlay(isPresented: $showCelebration, style: .confetti)
232
+ ```
233
+
234
+ 3. Wrap all applied animations with `.motionAware()` if MotionPreferences is integrated
235
+ 4. Do NOT add animation modifiers to screens not listed in the manifest
162
236
 
163
237
  ## Report Output
164
238
 
@@ -167,7 +241,7 @@ Write the report to `output/{app-name}/reports/05-customization.md` (append iOS
167
241
  ```markdown
168
242
  # Customization Report
169
243
 
170
- **Step:** 8
244
+ **Step:** 5
171
245
  **Skill:** ios-customizer
172
246
  **Timestamp:** {ISO 8601}
173
247
  **Result:** PASS
@@ -201,6 +275,11 @@ iOS (Swift / SwiftUI)
201
275
  - SyncManager methods updated: {list or "no changes needed"}
202
276
  - Data persistence verified: {yes/no}
203
277
 
278
+ ## Animation Modifiers Applied
279
+
280
+ - {modifier}: applied to {screen} — {description}
281
+ - MotionPreferences respected: {yes/no/N/A}
282
+
204
283
  ## Potential Issues
205
284
 
206
285
  - {any concerns or items needing manual review}
@@ -212,7 +291,7 @@ Update `output/{app-name}/reports/summary.json` — read the file, append this s
212
291
 
213
292
  ```json
214
293
  {
215
- "step": 8,
294
+ "step": 5,
216
295
  "name": "ios-customization",
217
296
  "startedAt": "{ISO 8601 timestamp}",
218
297
  "durationSeconds": 0,
@@ -91,27 +91,88 @@ React Native templates use a `ThemeContext.tsx`:
91
91
  - Apply corner radius and spacing tokens
92
92
  - Apply typography settings (font sizes, weights)
93
93
 
94
- ### Step 5 — Data Layer & Mock Data
94
+ ### Step 5 — Data Layer & Offline Infrastructure
95
+
96
+ Templates include an **offline-first data layer**:
95
97
 
96
98
  **Files you should NOT modify** (generic infrastructure):
97
99
  - `src/data/DataRepository.ts` — interface definition
100
+ - `src/data/NetworkMonitor.ts` — connectivity detection
101
+ - `src/data/LocalPersistence.ts` — JSON file persistence
102
+ - `src/components/OfflineBanner.tsx` — offline status banner
98
103
  - `src/theme/ThemeContext.tsx` — theme provider
99
104
 
100
105
  **Files you MAY need to update** (domain-specific):
101
- - `src/mock/MockDataProvider.ts` — seed data already updated in Step 4. Verify:
102
- - Seed data matches the new domain
103
- - All CRUD methods work with correct entity types
104
- - Subscription/onChange pattern is working
105
- - Reset method references correct defaults
106
+ - `src/mock/MockDataProvider.ts` — seed data already updated in Step 4. Verify seed data, CRUD methods, subscription pattern, and reset defaults
107
+ - `src/data/SyncManager.ts` if entity types were renamed, update domain methods
108
+
109
+ **ViewModel patterns** use `DataSourceResolver`, not `MockDataProvider` directly:
110
+ ```tsx
111
+ import { DataSourceResolver } from '../data/DataSourceResolver';
112
+
113
+ export function useHomeViewModel() {
114
+ const repository = DataSourceResolver.repository;
115
+ const [items, setItems] = useState<Item[]>([]);
116
+ useEffect(() => {
117
+ setItems(repository.getItems());
118
+ const unsubscribe = repository.subscribe(() => setItems(repository.getItems()));
119
+ return unsubscribe;
120
+ }, []);
121
+ return { items };
122
+ }
123
+ ```
124
+
125
+ **OfflineBanner integration** — add to main navigation screens:
126
+ ```tsx
127
+ <View style={{ flex: 1 }}>
128
+ <OfflineBanner />
129
+ {/* ... rest of screen content */}
130
+ </View>
131
+ ```
106
132
 
107
- ### Step 6 — MockDataProvider Verification
133
+ ### Step 6 — Data Layer Verification
108
134
 
109
- Verify the MockDataProvider uses correct patterns:
110
- - Implements all DataRepository methods
111
- - ViewModel hooks use MockDataProvider correctly
135
+ Verify:
136
+ - `DataRepository` interface matches MockDataProvider's public methods
137
+ - MockDataProvider implements DataRepository
138
+ - `DataSourceResolver` returns the correct implementation
139
+ - ViewModel hooks use `DataSourceResolver.repository`, not `MockDataProvider` directly
112
140
  - Seed data matches the new domain
113
141
  - CRUD methods reference correct entity types
114
142
 
143
+ ### Step 6.5 — Apply Animation Modifiers (if animation modules are integrated)
144
+
145
+ If the manifest includes an **"Animation & Sensory Plan"** section:
146
+
147
+ 1. Read the "Per-Screen Animation Modifiers" table
148
+ 2. Apply the assigned modifiers using imported module hooks/components:
149
+
150
+ ```typescript
151
+ // MicroInteractions — press feedback
152
+ import { useScaleOnPress } from '../shared/MicroInteractions';
153
+ import { useHapticEngine } from '../shared/HapticEngine';
154
+ const { animatedStyle, handlers } = useScaleOnPress();
155
+
156
+ // ScrollEffects — parallax header
157
+ import { ParallaxHeader } from '../shared/ScrollEffects';
158
+ <ParallaxHeader height={280} imageSource={item.imageUrl} />
159
+
160
+ // CelebrationEffects — success overlay
161
+ import { CelebrationOverlay } from '../shared/CelebrationEffects';
162
+ <CelebrationOverlay isActive={showCelebration} style="confetti" />
163
+ ```
164
+
165
+ 3. Wrap with `useMotionPreferences` checks if MotionPreferences is integrated
166
+ 4. Do NOT add animation modifiers to screens not listed in the manifest
167
+
168
+ ### Step 6.7 — App Icon (if applicable)
169
+
170
+ React Native apps target both iOS and Android. App icon customization is handled per-platform:
171
+ - **iOS**: If `scripts/generate-app-icon.swift` exists, run it with SF Symbol + background color from manifest
172
+ - **Android**: If `templates/android/ICON_VECTORS.md` exists, update `ic_launcher_foreground.xml` and `ic_launcher_background` color
173
+
174
+ If scripts/resources are not available, skip — default icons will be used.
175
+
115
176
  ## Report Output
116
177
 
117
178
  Write the report to `output/{app-name}/reports/05-customization.md`:
@@ -144,6 +205,17 @@ React Native (TypeScript)
144
205
  - Empty states: {count}
145
206
  - CTAs updated: {count}
146
207
 
208
+ ## Offline Infrastructure
209
+
210
+ - OfflineBanner integrated: {yes/no}
211
+ - SyncManager methods updated: {list or "no changes needed"}
212
+ - Data persistence verified: {yes/no}
213
+
214
+ ## Animation Modifiers Applied
215
+
216
+ - {modifier}: applied to {screen} — {description}
217
+ - MotionPreferences respected: {yes/no/N/A}
218
+
147
219
  ## Potential Issues
148
220
 
149
221
  - {any concerns}
@@ -191,8 +263,10 @@ Templates include `testID` props on all interactive elements and a `TESTING_MANI
191
263
  - ONLY edit files under `output/` — never touch `templates/`
192
264
  - Ensure all imports resolve after your changes
193
265
  - Preserve `MockDataProvider` CRUD methods and `reset()` function
266
+ - Do NOT modify NetworkMonitor, LocalPersistence, or OfflineBanner
267
+ - Ensure OfflineBanner is integrated into main navigation screens
194
268
  - Use `testID` props on ALL interactive elements
195
- - Follow `{screen}_{element}_{role}` convention for new test IDs
269
+ - Follow `{screen}_{element}_{role}` convention for new test IDs (see `templates/TEST_ID_CONVENTIONS.md`)
196
270
  - Use `StyleSheet.create()` for styles, not inline objects
197
271
  - Use functional components with TypeScript
198
272
  - Keep React Navigation structure intact
@@ -123,18 +123,19 @@ private val LightColorScheme = lightColorScheme(
123
123
  - Use Material 3 color roles (primary, secondary, tertiary, surface, etc.)
124
124
  - Keep `dynamicColor` support if present
125
125
 
126
- ### MockDataProvider Updates
127
- Android MockDataProvider uses companion object with functions:
126
+ ### Data Layer
127
+ Templates use a `DataRepository` interface with `DataSourceResolver`:
128
128
  ```kotlin
129
- object MockDataProvider {
130
- fun getServices(): List<Service> = listOf(...)
131
- fun getProviders(): List<Provider> = listOf(...)
132
- }
129
+ // Screens get the repository from DataSourceResolver
130
+ val repository = remember { DataSourceResolver.repository }
131
+ val items by repository.items.collectAsState()
132
+ repository.addItem(newItem)
133
133
  ```
134
- - Keep the same function signatures
135
- - Update return values with domain-specific data
136
- - Use `UUID.randomUUID().toString()` for IDs
137
- - Use `LocalDateTime.now()` and `LocalDate` for dates
134
+ - ALWAYS use `DataSourceResolver.repository`, never `MockDataProvider` directly
135
+ - Preserve MockDataProvider CRUD functions and `reset()` method
136
+ - Do NOT modify NetworkMonitor, LocalPersistence, or OfflineBanner
137
+ - Verify NetworkMonitor is registered in the Application class
138
+ - Ensure OfflineBanner is integrated into main navigation composables
138
139
 
139
140
  ## Output
140
141
 
@@ -155,5 +156,8 @@ POTENTIAL ISSUES:
155
156
  - Ensure all `import` statements resolve after renaming
156
157
  - Keep `@HiltAndroidApp`, `@HiltViewModel`, `@AndroidEntryPoint` intact
157
158
  - Keep `build.gradle.kts` syntactically valid Kotlin DSL
158
- - Preserve `MockDataProvider` function signatures
159
+ - Preserve `MockDataProvider` CRUD functions and `reset()` method
160
+ - Do NOT modify NetworkMonitor, LocalPersistence, or OfflineBanner
159
161
  - Use Material 3 components (not legacy Material 2)
162
+ - Preserve ALL `Modifier.testTag()` calls — do NOT remove test IDs
163
+ - Follow `{screen}_{element}_{role}` convention for new test tags (see `templates/TEST_ID_CONVENTIONS.md`)