@buivietphi/skill-mobile-mt 2.0.1 → 2.1.0
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.
- package/AGENTS.md +56 -38
- package/README.md +68 -40
- package/SKILL.md +471 -50
- package/package.json +1 -1
- package/shared/bug-detection.md +411 -27
- package/shared/code-review.md +899 -37
- package/shared/debugging-intelligence.md +787 -0
- package/shared/i18n-localization.md +426 -0
- package/shared/prompt-engineering.md +176 -20
- package/shared/storage-patterns.md +312 -0
|
@@ -0,0 +1,426 @@
|
|
|
1
|
+
# Mobile i18n & Localization
|
|
2
|
+
|
|
3
|
+
> Multi-language support for React Native, Flutter, iOS, Android.
|
|
4
|
+
> Covers: setup, translation management, RTL, date/number formatting, dynamic locale switching.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Decision Matrix — Pick Your Stack
|
|
9
|
+
|
|
10
|
+
```
|
|
11
|
+
PLATFORM → RECOMMENDED LIBRARY
|
|
12
|
+
──────────────────────────────────────────────────────────────────
|
|
13
|
+
React Native → i18next + react-i18next (most popular, flexible)
|
|
14
|
+
OR expo-localization + custom (Expo projects)
|
|
15
|
+
|
|
16
|
+
Flutter → slang (type-safe, code generation) ← recommended
|
|
17
|
+
OR flutter_localizations + .arb files (official)
|
|
18
|
+
OR easy_localization (simple)
|
|
19
|
+
|
|
20
|
+
iOS Native → .xcstrings / Localizable.strings (built-in Xcode)
|
|
21
|
+
Android Native → strings.xml per locale (built-in Android Studio)
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## React Native — i18next
|
|
27
|
+
|
|
28
|
+
### Setup
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npm install i18next react-i18next
|
|
32
|
+
npm install @react-native-async-storage/async-storage # for locale persistence
|
|
33
|
+
# or with expo:
|
|
34
|
+
npx expo install expo-localization
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### File Structure
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
src/
|
|
41
|
+
├── i18n/
|
|
42
|
+
│ ├── index.ts ← i18next init
|
|
43
|
+
│ ├── en.json ← English (base language)
|
|
44
|
+
│ ├── vi.json ← Vietnamese
|
|
45
|
+
│ ├── ja.json ← Japanese
|
|
46
|
+
│ ├── ar.json ← Arabic (RTL)
|
|
47
|
+
│ └── types.ts ← TypeScript type for translation keys
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Translation Files
|
|
51
|
+
|
|
52
|
+
```json
|
|
53
|
+
// i18n/en.json
|
|
54
|
+
{
|
|
55
|
+
"common": {
|
|
56
|
+
"ok": "OK",
|
|
57
|
+
"cancel": "Cancel",
|
|
58
|
+
"loading": "Loading...",
|
|
59
|
+
"error": "Something went wrong",
|
|
60
|
+
"retry": "Try again",
|
|
61
|
+
"empty": "No results found"
|
|
62
|
+
},
|
|
63
|
+
"auth": {
|
|
64
|
+
"login": "Login",
|
|
65
|
+
"logout": "Logout",
|
|
66
|
+
"email": "Email address",
|
|
67
|
+
"password": "Password",
|
|
68
|
+
"forgotPassword": "Forgot password?",
|
|
69
|
+
"errors": {
|
|
70
|
+
"invalidEmail": "Please enter a valid email",
|
|
71
|
+
"wrongPassword": "Incorrect password"
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
"profile": {
|
|
75
|
+
"title": "My Profile",
|
|
76
|
+
"greeting": "Hello, {{name}}!",
|
|
77
|
+
"itemCount": "{{count}} item",
|
|
78
|
+
"itemCount_other": "{{count}} items"
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
```json
|
|
84
|
+
// i18n/vi.json
|
|
85
|
+
{
|
|
86
|
+
"common": {
|
|
87
|
+
"ok": "Đồng ý",
|
|
88
|
+
"cancel": "Hủy",
|
|
89
|
+
"loading": "Đang tải...",
|
|
90
|
+
"error": "Có lỗi xảy ra",
|
|
91
|
+
"retry": "Thử lại",
|
|
92
|
+
"empty": "Không có kết quả"
|
|
93
|
+
},
|
|
94
|
+
"auth": {
|
|
95
|
+
"login": "Đăng nhập",
|
|
96
|
+
"logout": "Đăng xuất",
|
|
97
|
+
"email": "Địa chỉ email",
|
|
98
|
+
"password": "Mật khẩu",
|
|
99
|
+
"forgotPassword": "Quên mật khẩu?",
|
|
100
|
+
"errors": {
|
|
101
|
+
"invalidEmail": "Vui lòng nhập email hợp lệ",
|
|
102
|
+
"wrongPassword": "Mật khẩu không đúng"
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
"profile": {
|
|
106
|
+
"title": "Hồ sơ của tôi",
|
|
107
|
+
"greeting": "Xin chào, {{name}}!",
|
|
108
|
+
"itemCount": "{{count}} mục"
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### i18next Init
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
// i18n/index.ts
|
|
117
|
+
import i18n from 'i18next';
|
|
118
|
+
import { initReactI18next } from 'react-i18next';
|
|
119
|
+
import * as Localization from 'expo-localization';
|
|
120
|
+
import MMKV from '../storage'; // your MMKV wrapper
|
|
121
|
+
|
|
122
|
+
import en from './en.json';
|
|
123
|
+
import vi from './vi.json';
|
|
124
|
+
import ar from './ar.json';
|
|
125
|
+
|
|
126
|
+
const LANGUAGE_KEY = 'user_language';
|
|
127
|
+
|
|
128
|
+
export const SUPPORTED_LANGUAGES = ['en', 'vi', 'ja', 'ar'] as const;
|
|
129
|
+
export type Language = typeof SUPPORTED_LANGUAGES[number];
|
|
130
|
+
|
|
131
|
+
// Get saved or device language
|
|
132
|
+
const savedLang = MMKV.getString(LANGUAGE_KEY);
|
|
133
|
+
const deviceLang = Localization.getLocales()[0]?.languageCode ?? 'en';
|
|
134
|
+
const initialLang = savedLang ?? (SUPPORTED_LANGUAGES.includes(deviceLang as Language) ? deviceLang : 'en');
|
|
135
|
+
|
|
136
|
+
i18n.use(initReactI18next).init({
|
|
137
|
+
resources: { en: { translation: en }, vi: { translation: vi }, ar: { translation: ar } },
|
|
138
|
+
lng: initialLang,
|
|
139
|
+
fallbackLng: 'en',
|
|
140
|
+
interpolation: { escapeValue: false },
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
export function changeLanguage(lang: Language) {
|
|
144
|
+
i18n.changeLanguage(lang);
|
|
145
|
+
MMKV.setString(LANGUAGE_KEY, lang); // persist choice
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export default i18n;
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Usage in Components
|
|
152
|
+
|
|
153
|
+
```typescript
|
|
154
|
+
import { useTranslation } from 'react-i18next';
|
|
155
|
+
import { I18nManager } from 'react-native';
|
|
156
|
+
|
|
157
|
+
export function ProfileScreen() {
|
|
158
|
+
const { t, i18n } = useTranslation();
|
|
159
|
+
const isRTL = I18nManager.isRTL;
|
|
160
|
+
|
|
161
|
+
return (
|
|
162
|
+
<View style={[styles.container, isRTL && styles.rtl]}>
|
|
163
|
+
<Text>{t('profile.title')}</Text>
|
|
164
|
+
<Text>{t('profile.greeting', { name: 'Phi' })}</Text>
|
|
165
|
+
<Text>{t('profile.itemCount', { count: 5 })}</Text>
|
|
166
|
+
|
|
167
|
+
{/* Language switcher */}
|
|
168
|
+
<Button title="Tiếng Việt" onPress={() => changeLanguage('vi')} />
|
|
169
|
+
<Button title="English" onPress={() => changeLanguage('en')} />
|
|
170
|
+
</View>
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// TypeScript type-safe keys (optional but recommended)
|
|
175
|
+
// Generate from en.json with i18next-resources-for-ts
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### RTL Support (Arabic, Hebrew)
|
|
179
|
+
|
|
180
|
+
```typescript
|
|
181
|
+
// App.tsx — apply RTL on init
|
|
182
|
+
import { I18nManager } from 'react-native';
|
|
183
|
+
import * as Updates from 'expo-updates';
|
|
184
|
+
|
|
185
|
+
function applyRTL(language: string) {
|
|
186
|
+
const isRTL = language === 'ar' || language === 'he';
|
|
187
|
+
|
|
188
|
+
if (I18nManager.isRTL !== isRTL) {
|
|
189
|
+
I18nManager.allowRTL(isRTL);
|
|
190
|
+
I18nManager.forceRTL(isRTL);
|
|
191
|
+
// Requires reload to take effect
|
|
192
|
+
Updates.reloadAsync(); // Expo
|
|
193
|
+
// RNRestart.Restart(); // react-native-restart (bare RN)
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// RTL-aware styles
|
|
198
|
+
const styles = StyleSheet.create({
|
|
199
|
+
row: {
|
|
200
|
+
flexDirection: I18nManager.isRTL ? 'row-reverse' : 'row',
|
|
201
|
+
},
|
|
202
|
+
text: {
|
|
203
|
+
textAlign: I18nManager.isRTL ? 'right' : 'left',
|
|
204
|
+
writingDirection: I18nManager.isRTL ? 'rtl' : 'ltr',
|
|
205
|
+
},
|
|
206
|
+
});
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### Date / Number Formatting
|
|
210
|
+
|
|
211
|
+
```typescript
|
|
212
|
+
// Use Intl API (built into Hermes/V8)
|
|
213
|
+
const locale = i18n.language;
|
|
214
|
+
|
|
215
|
+
// Date
|
|
216
|
+
const formatDate = (date: Date) =>
|
|
217
|
+
new Intl.DateTimeFormat(locale, { dateStyle: 'medium', timeStyle: 'short' }).format(date);
|
|
218
|
+
|
|
219
|
+
// Number / Currency
|
|
220
|
+
const formatCurrency = (amount: number, currency = 'USD') =>
|
|
221
|
+
new Intl.NumberFormat(locale, { style: 'currency', currency }).format(amount);
|
|
222
|
+
|
|
223
|
+
// Relative time (2 days ago, in 3 hours)
|
|
224
|
+
const formatRelative = (date: Date) =>
|
|
225
|
+
new Intl.RelativeTimeFormat(locale, { numeric: 'auto' }).format(
|
|
226
|
+
Math.round((date.getTime() - Date.now()) / 86400000), 'day'
|
|
227
|
+
);
|
|
228
|
+
|
|
229
|
+
// Usage
|
|
230
|
+
formatDate(new Date()); // "Jan 15, 2025, 2:30 PM"
|
|
231
|
+
formatCurrency(99.99); // "$99.99" / "99,99 €"
|
|
232
|
+
formatRelative(yesterday); // "yesterday" / "hôm qua"
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
## Flutter — slang (Recommended)
|
|
238
|
+
|
|
239
|
+
### Setup
|
|
240
|
+
|
|
241
|
+
```yaml
|
|
242
|
+
# pubspec.yaml
|
|
243
|
+
dependencies:
|
|
244
|
+
flutter_localizations:
|
|
245
|
+
sdk: flutter
|
|
246
|
+
slang: ^4.0.0
|
|
247
|
+
slang_flutter: ^4.0.0
|
|
248
|
+
|
|
249
|
+
dev_dependencies:
|
|
250
|
+
slang_build_runner: ^4.0.0
|
|
251
|
+
build_runner: ^2.0.0
|
|
252
|
+
|
|
253
|
+
flutter:
|
|
254
|
+
generate: true
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
### Translation Files
|
|
258
|
+
|
|
259
|
+
```json
|
|
260
|
+
// assets/i18n/en.i18n.json
|
|
261
|
+
{
|
|
262
|
+
"common": {
|
|
263
|
+
"ok": "OK",
|
|
264
|
+
"cancel": "Cancel",
|
|
265
|
+
"loading": "Loading..."
|
|
266
|
+
},
|
|
267
|
+
"auth": {
|
|
268
|
+
"login": "Login",
|
|
269
|
+
"greeting": "Hello, $name!",
|
|
270
|
+
"itemCount(n)": {
|
|
271
|
+
"one": "$n item",
|
|
272
|
+
"other": "$n items"
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
```json
|
|
279
|
+
// assets/i18n/vi.i18n.json
|
|
280
|
+
{
|
|
281
|
+
"common": {
|
|
282
|
+
"ok": "Đồng ý",
|
|
283
|
+
"cancel": "Hủy",
|
|
284
|
+
"loading": "Đang tải..."
|
|
285
|
+
},
|
|
286
|
+
"auth": {
|
|
287
|
+
"login": "Đăng nhập",
|
|
288
|
+
"greeting": "Xin chào, $name!",
|
|
289
|
+
"itemCount(n)": "$n mục"
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
### Generate + Use
|
|
295
|
+
|
|
296
|
+
```bash
|
|
297
|
+
dart run build_runner build
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
```dart
|
|
301
|
+
// main.dart
|
|
302
|
+
import 'package:flutter_localizations/flutter_localizations.dart';
|
|
303
|
+
import 'i18n/strings.g.dart'; // generated
|
|
304
|
+
|
|
305
|
+
void main() {
|
|
306
|
+
WidgetsFlutterBinding.ensureInitialized();
|
|
307
|
+
LocaleSettings.useDeviceLocale(); // or .setLocale(AppLocale.vi)
|
|
308
|
+
runApp(TranslationProvider(child: MyApp()));
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
class MyApp extends StatelessWidget {
|
|
312
|
+
Widget build(BuildContext context) => MaterialApp(
|
|
313
|
+
locale: TranslationProvider.of(context).flutterLocale,
|
|
314
|
+
supportedLocales: AppLocaleUtils.supportedLocales,
|
|
315
|
+
localizationsDelegates: GlobalMaterialLocalizations.delegates,
|
|
316
|
+
home: HomeScreen(),
|
|
317
|
+
);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Usage — fully type-safe, autocomplete
|
|
321
|
+
final t = Translations.of(context);
|
|
322
|
+
|
|
323
|
+
Text(t.common.ok)
|
|
324
|
+
Text(t.auth.greeting(name: 'Phi'))
|
|
325
|
+
Text(t.auth.itemCount(n: 5))
|
|
326
|
+
|
|
327
|
+
// Change language
|
|
328
|
+
LocaleSettings.setLocale(AppLocale.vi);
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
---
|
|
332
|
+
|
|
333
|
+
## iOS Native (Xcode .xcstrings)
|
|
334
|
+
|
|
335
|
+
```
|
|
336
|
+
Localizable.xcstrings (modern, Xcode 15+):
|
|
337
|
+
- Edit in Xcode string catalog UI
|
|
338
|
+
- Automatic plural rules per language
|
|
339
|
+
- Supports all Apple platforms
|
|
340
|
+
|
|
341
|
+
File structure:
|
|
342
|
+
App/
|
|
343
|
+
├── en.lproj/Localizable.xcstrings ← base language
|
|
344
|
+
├── vi.lproj/Localizable.xcstrings
|
|
345
|
+
└── ar.lproj/Localizable.xcstrings
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
```swift
|
|
349
|
+
// Usage
|
|
350
|
+
Text(String(localized: "auth.login")) // SwiftUI (auto-localizes)
|
|
351
|
+
label.text = NSLocalizedString("auth.login", comment: "") // UIKit
|
|
352
|
+
|
|
353
|
+
// With interpolation
|
|
354
|
+
Text("profile.greeting \(userName)")
|
|
355
|
+
// Localizable.xcstrings: "profile.greeting %@" → "Xin chào, %@!"
|
|
356
|
+
|
|
357
|
+
// Date/Number formatting
|
|
358
|
+
let formatted = Date().formatted(.dateTime.month(.wide).day().year())
|
|
359
|
+
let price = amount.formatted(.currency(code: "VND").locale(Locale(identifier: "vi_VN")))
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
---
|
|
363
|
+
|
|
364
|
+
## Android Native (strings.xml)
|
|
365
|
+
|
|
366
|
+
```xml
|
|
367
|
+
<!-- res/values/strings.xml (English, default) -->
|
|
368
|
+
<resources>
|
|
369
|
+
<string name="common_ok">OK</string>
|
|
370
|
+
<string name="auth_login">Login</string>
|
|
371
|
+
<string name="profile_greeting">Hello, %s!</string>
|
|
372
|
+
<plurals name="profile_item_count">
|
|
373
|
+
<item quantity="one">%d item</item>
|
|
374
|
+
<item quantity="other">%d items</item>
|
|
375
|
+
</plurals>
|
|
376
|
+
</resources>
|
|
377
|
+
|
|
378
|
+
<!-- res/values-vi/strings.xml (Vietnamese) -->
|
|
379
|
+
<resources>
|
|
380
|
+
<string name="common_ok">Đồng ý</string>
|
|
381
|
+
<string name="auth_login">Đăng nhập</string>
|
|
382
|
+
<string name="profile_greeting">Xin chào, %s!</string>
|
|
383
|
+
<plurals name="profile_item_count">
|
|
384
|
+
<item quantity="other">%d mục</item>
|
|
385
|
+
</plurals>
|
|
386
|
+
</resources>
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
```kotlin
|
|
390
|
+
// Usage
|
|
391
|
+
getString(R.string.auth_login)
|
|
392
|
+
getString(R.string.profile_greeting, "Phi")
|
|
393
|
+
resources.getQuantityString(R.plurals.profile_item_count, 5, 5)
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
---
|
|
397
|
+
|
|
398
|
+
## i18n Checklist
|
|
399
|
+
|
|
400
|
+
```
|
|
401
|
+
SETUP:
|
|
402
|
+
□ Translation keys are namespaced (auth.login, not just "login")
|
|
403
|
+
□ Base language file has ALL keys
|
|
404
|
+
□ Missing key fallback configured (falls back to English)
|
|
405
|
+
□ Language preference persisted on device (MMKV/SharedPreferences)
|
|
406
|
+
□ Language detected from device locale on first launch
|
|
407
|
+
|
|
408
|
+
CONTENT:
|
|
409
|
+
□ No hardcoded strings in components (all through t() / getString)
|
|
410
|
+
□ Plurals handled correctly (1 item vs 2 items)
|
|
411
|
+
□ Interpolation used for dynamic values (name, count)
|
|
412
|
+
□ Date/number/currency formatted per locale (Intl API)
|
|
413
|
+
|
|
414
|
+
RTL (if supporting Arabic/Hebrew):
|
|
415
|
+
□ I18nManager.forceRTL() applied and app reloaded
|
|
416
|
+
□ flexDirection mirrors on RTL
|
|
417
|
+
□ textAlign mirrors on RTL
|
|
418
|
+
□ Icons and chevrons mirror on RTL
|
|
419
|
+
□ Back navigation arrow mirrors on RTL
|
|
420
|
+
|
|
421
|
+
TESTING:
|
|
422
|
+
□ Test with locale set to each supported language
|
|
423
|
+
□ Test with system language set to unsupported → falls back to English
|
|
424
|
+
□ Test RTL layout on Arabic device or simulator
|
|
425
|
+
□ Test long strings (German/Finnish) don't break UI layout
|
|
426
|
+
```
|
|
@@ -64,40 +64,138 @@
|
|
|
64
64
|
```xml
|
|
65
65
|
<think>
|
|
66
66
|
BUG: [description]
|
|
67
|
-
|
|
67
|
+
ERROR MESSAGE: [paste exact error]
|
|
68
|
+
|
|
69
|
+
⛔ STOP — CLASSIFY + SEARCH PROJECT FIRST (before ANY analysis):
|
|
70
|
+
|
|
71
|
+
<error_classification>
|
|
72
|
+
TYPE: [RUNTIME CRASH / BUILD ERROR / TYPE MISMATCH / NETWORK ERROR /
|
|
73
|
+
RENDER ERROR / NAVIGATION ERROR / PERFORMANCE / STATE ERROR /
|
|
74
|
+
NATIVE ERROR / MEMORY ERROR / INVESTIGATION]
|
|
75
|
+
|
|
76
|
+
SEARCH STRATEGY based on type:
|
|
77
|
+
→ RUNTIME/STATE/RENDER/NAVIGATION → Search src/ FIRST → then trace outward
|
|
78
|
+
→ BUILD/NATIVE → Search config files FIRST (tsconfig/gradle/Pod/pubspec)
|
|
79
|
+
→ NETWORK/API → Search API service files → then .env → then interceptors
|
|
80
|
+
→ INVESTIGATION → Search by feature name → read → report (don't fix yet)
|
|
81
|
+
|
|
82
|
+
If complex bug → Read shared/debugging-intelligence.md for pattern match
|
|
83
|
+
</error_classification>
|
|
84
|
+
|
|
85
|
+
<project_search>
|
|
86
|
+
STEP 1: Extract keywords from error:
|
|
87
|
+
→ File/path in stack trace: [extract]
|
|
88
|
+
→ Function/class/component name: [extract]
|
|
89
|
+
→ Module/package name: [extract]
|
|
90
|
+
→ Line number: [extract if available]
|
|
91
|
+
→ Error code / HTTP status: [extract if available]
|
|
92
|
+
|
|
93
|
+
STEP 2: Filter noise from log (if user pasted log):
|
|
94
|
+
→ SKIP: node_modules/*, React internals, engine frames
|
|
95
|
+
→ FOCUS: lines with src/ paths, "Error:", "Caused by:", YOUR component names
|
|
96
|
+
|
|
97
|
+
STEP 3: Search project source code (MANDATORY):
|
|
98
|
+
→ Grep "[keyword]" src/ ← ALWAYS start here (unless BUILD error)
|
|
99
|
+
→ Grep "[function_name]" src/ ← find the actual function
|
|
100
|
+
→ Glob "**/*[ComponentName]*" ← find the actual file
|
|
101
|
+
→ Results: [list files found]
|
|
102
|
+
|
|
103
|
+
STEP 4: Read matched files (TOP 3-5):
|
|
104
|
+
→ Read [file1] — [what I found: actual code, types, imports]
|
|
105
|
+
→ Read [file2] — [what I found: related logic, callers]
|
|
106
|
+
→ Read [file3] — [what I found: state/store connected to this]
|
|
107
|
+
|
|
108
|
+
⛔ If I skipped Step 1-4 → GO BACK AND DO THEM NOW
|
|
109
|
+
⛔ If I found 0 results in src/ → widen: lib/ → app/ → project root
|
|
110
|
+
</project_search>
|
|
68
111
|
|
|
69
112
|
<source_verification>
|
|
70
|
-
⚠️
|
|
71
|
-
- [ ]
|
|
72
|
-
- [ ]
|
|
113
|
+
⚠️ Verify I have REAL project data (not assumptions):
|
|
114
|
+
- [ ] Classified error type and picked correct search strategy
|
|
115
|
+
- [ ] Filtered noise from log (if applicable)
|
|
116
|
+
- [ ] Searched src/ with Grep for error keywords → found files
|
|
117
|
+
- [ ] Read the actual file(s) where bug occurs
|
|
118
|
+
- [ ] Verified function/class names exist in project (grep result)
|
|
73
119
|
- [ ] Checked package versions in package.json/pubspec.yaml
|
|
74
120
|
- [ ] Identified data types from actual code (not assumed)
|
|
121
|
+
- [ ] Traced the call chain: who calls this → what it returns
|
|
75
122
|
</source_verification>
|
|
76
123
|
|
|
77
|
-
<
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
-
|
|
88
|
-
|
|
89
|
-
|
|
124
|
+
<root_cause_tracing>
|
|
125
|
+
⛔ NO FIXES WITHOUT ROOT CAUSE INVESTIGATION FIRST
|
|
126
|
+
|
|
127
|
+
STEP 1 — IMMEDIATE CAUSE (what throws):
|
|
128
|
+
- Error type: [from classification above]
|
|
129
|
+
- Crash/error at: [file:line from project search]
|
|
130
|
+
- What code does at that line: [describe from reading]
|
|
131
|
+
- What value is wrong: [actual vs expected]
|
|
132
|
+
|
|
133
|
+
STEP 2 — TRACE BACKWARD (what called this):
|
|
134
|
+
- Who calls this function? → [grep callers in src/]
|
|
135
|
+
- What data does caller pass? → [trace data origin]
|
|
136
|
+
- Go up one level: who calls the caller? → [trace further]
|
|
137
|
+
|
|
138
|
+
STEP 3 — ROOT CAUSE (where chain breaks):
|
|
139
|
+
- Root cause at: [file:line — where correct data becomes incorrect]
|
|
140
|
+
- WHY it fails: [based on actual code read, NOT guess]
|
|
141
|
+
- Does this match a known pattern? → [check debugging-intelligence.md if loaded]
|
|
142
|
+
- Does root cause explain ALL symptoms? YES/NO
|
|
143
|
+
→ If NO → theory is wrong → re-trace from Step 2
|
|
144
|
+
</root_cause_tracing>
|
|
145
|
+
|
|
146
|
+
<working_example>
|
|
147
|
+
Search for similar working code in SAME project:
|
|
148
|
+
→ Grep for similar pattern that works: [search term]
|
|
149
|
+
→ Found working example at: [file:line] (or "none found")
|
|
150
|
+
→ Differences between broken vs working:
|
|
151
|
+
1. [difference]
|
|
152
|
+
2. [difference]
|
|
153
|
+
→ The fix should align broken code with working pattern
|
|
154
|
+
</working_example>
|
|
155
|
+
|
|
156
|
+
<hypothesis>
|
|
157
|
+
⛔ 1 HYPOTHESIS → 1 MINIMAL CHANGE → VERIFY
|
|
158
|
+
|
|
159
|
+
HYPOTHESIS: [specific theory based on root cause]
|
|
160
|
+
CHANGE: [smallest possible change to test this — 1 change only]
|
|
161
|
+
EXPECTED RESULT: [what should happen if hypothesis is correct]
|
|
162
|
+
|
|
163
|
+
⛔ If this fails → REVERT → form NEW hypothesis (never stack fixes)
|
|
164
|
+
⛔ If 3 hypotheses fail → STOP → question architecture
|
|
165
|
+
</hypothesis>
|
|
90
166
|
|
|
91
167
|
<fix>
|
|
92
|
-
[Specific change with code snippet]
|
|
168
|
+
[Specific change with code snippet — before → after]
|
|
93
169
|
|
|
94
170
|
WHY IT WORKS:
|
|
95
|
-
[Explain
|
|
171
|
+
[Explain based on root cause tracing — not guess]
|
|
96
172
|
SOURCE: [where this fix pattern comes from — project code / skill file / official docs]
|
|
173
|
+
|
|
174
|
+
DEFENSE IN DEPTH (make bug structurally impossible):
|
|
175
|
+
- Layer 1 (input): [validation added]
|
|
176
|
+
- Layer 2 (state): [guard added]
|
|
177
|
+
- Layer 3 (render): [null check / fallback added]
|
|
97
178
|
</fix>
|
|
98
179
|
|
|
180
|
+
<verification>
|
|
181
|
+
⛔ NO COMPLETION CLAIMS WITHOUT EVIDENCE
|
|
182
|
+
|
|
183
|
+
🚩 Anti-rationalization check:
|
|
184
|
+
- Am I saying "should work now" without running it? → RUN IT
|
|
185
|
+
- Am I "confident" without evidence? → PROVE IT
|
|
186
|
+
- Am I stacking 3+ changes? → REVERT. 1 change only.
|
|
187
|
+
|
|
188
|
+
Evidence:
|
|
189
|
+
- [ ] Fix addresses root cause (not just symptoms)
|
|
190
|
+
- [ ] All symptoms explained by this root cause
|
|
191
|
+
- [ ] Working example pattern followed (if found)
|
|
192
|
+
- [ ] Defense-in-depth added at [N] layers
|
|
193
|
+
- [ ] Side effects checked (grep for other callers)
|
|
194
|
+
- [ ] Both platforms considered (iOS + Android)
|
|
195
|
+
</verification>
|
|
196
|
+
|
|
99
197
|
<side_effects>
|
|
100
|
-
- Files that import this: [list
|
|
198
|
+
- Files that import this: [list from grep results]
|
|
101
199
|
- Tests affected: [list]
|
|
102
200
|
- Platform-specific: iOS [impact] / Android [impact]
|
|
103
201
|
</side_effects>
|
|
@@ -116,6 +214,64 @@ FILE: [path]
|
|
|
116
214
|
</think>
|
|
117
215
|
```
|
|
118
216
|
|
|
217
|
+
### Diagnostic Scan (user unsure / vague / "check this for me")
|
|
218
|
+
|
|
219
|
+
```xml
|
|
220
|
+
<think>
|
|
221
|
+
USER SAID: [what user described or asked — could be vague]
|
|
222
|
+
AREA: [extract: screen name / feature name / module name / file name]
|
|
223
|
+
|
|
224
|
+
<area_identification>
|
|
225
|
+
What did user mention or show?
|
|
226
|
+
→ Screen/feature name: [extract from user's words]
|
|
227
|
+
→ File pasted/referenced: [if any]
|
|
228
|
+
→ Behavior described: [if any]
|
|
229
|
+
→ If unclear → I should ask: "Which screen or feature should I check?"
|
|
230
|
+
</area_identification>
|
|
231
|
+
|
|
232
|
+
<project_search>
|
|
233
|
+
Search broadly for this area:
|
|
234
|
+
→ Grep "[feature]" src/ → found: [list files]
|
|
235
|
+
→ Glob "**/*[ScreenName]*" → found: [list files]
|
|
236
|
+
→ Also search: related hooks, services, stores, utils
|
|
237
|
+
→ Total files to scan: [N files]
|
|
238
|
+
</project_search>
|
|
239
|
+
|
|
240
|
+
<diagnostic_scan>
|
|
241
|
+
For EACH file, run the checklist:
|
|
242
|
+
|
|
243
|
+
FILE: [file1:path]
|
|
244
|
+
□ Crash risks: [findings or "clean"]
|
|
245
|
+
□ Memory leaks: [findings or "clean"]
|
|
246
|
+
□ Race conditions: [findings or "clean"]
|
|
247
|
+
□ Security: [findings or "clean"]
|
|
248
|
+
□ Performance: [findings or "clean"]
|
|
249
|
+
□ UX/states: [findings or "clean"]
|
|
250
|
+
□ Data flow: [trace API → state → render — any break?]
|
|
251
|
+
□ Edge cases: [empty data? error response? offline? slow?]
|
|
252
|
+
|
|
253
|
+
FILE: [file2:path]
|
|
254
|
+
□ ... (repeat for each file)
|
|
255
|
+
</diagnostic_scan>
|
|
256
|
+
|
|
257
|
+
<report>
|
|
258
|
+
Scanned: [N files] in [area name]
|
|
259
|
+
|
|
260
|
+
🔴 Issues found:
|
|
261
|
+
1. [SEVERITY] [file:line] — [description]
|
|
262
|
+
2. [SEVERITY] [file:line] — [description]
|
|
263
|
+
|
|
264
|
+
🟡 Suspicious (might be intentional):
|
|
265
|
+
1. [file:line] — [what looks off and why]
|
|
266
|
+
|
|
267
|
+
✅ Looks good:
|
|
268
|
+
- [aspect that's well-implemented]
|
|
269
|
+
|
|
270
|
+
Recommendation: [what to fix first / what to investigate deeper]
|
|
271
|
+
</report>
|
|
272
|
+
</think>
|
|
273
|
+
```
|
|
274
|
+
|
|
119
275
|
**Example:**
|
|
120
276
|
```xml
|
|
121
277
|
<think>
|