@jakkrichm/create-nexus-devflow 2.0.0 → 2.0.1

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 (394) hide show
  1. package/README.md +96 -50
  2. package/package.json +1 -1
  3. package/template/.agents/skills/20-spec/SKILL.md +136 -136
  4. package/template/.agents/skills/30-plan/SKILL.md +206 -206
  5. package/template/.agents/skills/40-implement/SKILL.md +152 -152
  6. package/template/.agents/skills/50-verify/SKILL.md +174 -174
  7. package/template/.agents/skills/60-report/SKILL.md +168 -168
  8. package/template/.agents/skills/70-release/SKILL.md +141 -141
  9. package/template/.agents/skills/9arm-skills/README.md +51 -51
  10. package/template/.agents/skills/9arm-skills/debug-mantra/SKILL.md +86 -86
  11. package/template/.agents/skills/9arm-skills/management-talk/SKILL.md +79 -79
  12. package/template/.agents/skills/9arm-skills/post-mortem/SKILL.md +71 -71
  13. package/template/.agents/skills/9arm-skills/scrutinize/SKILL.md +72 -72
  14. package/template/.agents/skills/agent/SKILL.md +164 -164
  15. package/template/.agents/skills/api-and-interface-design/SKILL.md +294 -294
  16. package/template/.agents/skills/app-builder/SKILL.md +75 -75
  17. package/template/.agents/skills/app-builder/agent-coordination.md +71 -71
  18. package/template/.agents/skills/app-builder/feature-building.md +53 -53
  19. package/template/.agents/skills/app-builder/project-detection.md +34 -34
  20. package/template/.agents/skills/app-builder/scaffolding.md +118 -118
  21. package/template/.agents/skills/app-builder/tech-stack.md +40 -40
  22. package/template/.agents/skills/app-builder/templates/SKILL.md +39 -39
  23. package/template/.agents/skills/app-builder/templates/astro-static/TEMPLATE.md +76 -76
  24. package/template/.agents/skills/app-builder/templates/chrome-extension/TEMPLATE.md +92 -92
  25. package/template/.agents/skills/app-builder/templates/cli-tool/TEMPLATE.md +88 -88
  26. package/template/.agents/skills/app-builder/templates/electron-desktop/TEMPLATE.md +88 -88
  27. package/template/.agents/skills/app-builder/templates/express-api/TEMPLATE.md +83 -83
  28. package/template/.agents/skills/app-builder/templates/flutter-app/TEMPLATE.md +90 -90
  29. package/template/.agents/skills/app-builder/templates/monorepo-turborepo/TEMPLATE.md +90 -90
  30. package/template/.agents/skills/app-builder/templates/nextjs-fullstack/TEMPLATE.md +122 -122
  31. package/template/.agents/skills/app-builder/templates/nextjs-saas/TEMPLATE.md +122 -122
  32. package/template/.agents/skills/app-builder/templates/nextjs-static/TEMPLATE.md +169 -169
  33. package/template/.agents/skills/app-builder/templates/nuxt-app/TEMPLATE.md +134 -134
  34. package/template/.agents/skills/app-builder/templates/python-fastapi/TEMPLATE.md +83 -83
  35. package/template/.agents/skills/app-builder/templates/react-native-app/TEMPLATE.md +119 -119
  36. package/template/.agents/skills/architecture/SKILL.md +55 -55
  37. package/template/.agents/skills/architecture/context-discovery.md +43 -43
  38. package/template/.agents/skills/architecture/examples.md +94 -94
  39. package/template/.agents/skills/architecture/pattern-selection.md +68 -68
  40. package/template/.agents/skills/architecture/patterns-reference.md +50 -50
  41. package/template/.agents/skills/architecture/trade-off-analysis.md +77 -77
  42. package/template/.agents/skills/bash-linux/SKILL.md +199 -199
  43. package/template/.agents/skills/behavioral-modes/SKILL.md +242 -242
  44. package/template/.agents/skills/brainstorm/SKILL.md +157 -157
  45. package/template/.agents/skills/browser-testing-with-devtools/SKILL.md +302 -302
  46. package/template/.agents/skills/changelog/SKILL.md +108 -108
  47. package/template/.agents/skills/check-for-updates/SKILL.md +116 -116
  48. package/template/.agents/skills/ci-cd-and-automation/SKILL.md +390 -390
  49. package/template/.agents/skills/code-review-and-quality/SKILL.md +392 -392
  50. package/template/.agents/skills/code-simplification/SKILL.md +331 -331
  51. package/template/.agents/skills/commit/SKILL.md +62 -62
  52. package/template/.agents/skills/competitor-analysis/SKILL.md +66 -66
  53. package/template/.agents/skills/context-engineering/SKILL.md +290 -290
  54. package/template/.agents/skills/database-design/SKILL.md +52 -52
  55. package/template/.agents/skills/database-design/database-selection.md +43 -43
  56. package/template/.agents/skills/database-design/indexing.md +39 -39
  57. package/template/.agents/skills/database-design/migrations.md +48 -48
  58. package/template/.agents/skills/database-design/optimization.md +36 -36
  59. package/template/.agents/skills/database-design/orm-selection.md +30 -30
  60. package/template/.agents/skills/database-design/schema-design.md +56 -56
  61. package/template/.agents/skills/database-design/scripts/schema_validator.py +172 -172
  62. package/template/.agents/skills/debug/SKILL.md +116 -116
  63. package/template/.agents/skills/debugging-and-error-recovery/SKILL.md +298 -298
  64. package/template/.agents/skills/deploy/SKILL.md +90 -90
  65. package/template/.agents/skills/deployment-procedures/SKILL.md +241 -241
  66. package/template/.agents/skills/deprecation-and-migration/SKILL.md +206 -206
  67. package/template/.agents/skills/devflow-concept-intake/SKILL.md +150 -150
  68. package/template/.agents/skills/devflow-concept-intake/agents/openai.yaml +4 -4
  69. package/template/.agents/skills/devflow-concept-intake/references/adoption-proposal-template.md +60 -60
  70. package/template/.agents/skills/devflow-concept-intake/references/concept-report-template.md +47 -47
  71. package/template/.agents/skills/devflow-concept-intake/references/upstream-tracking-template.md +51 -51
  72. package/template/.agents/skills/devflow-wiki/SKILL.md +75 -75
  73. package/template/.agents/skills/documentation-and-adrs/SKILL.md +278 -278
  74. package/template/.agents/skills/english-to-thai-translator/SKILL.md +125 -125
  75. package/template/.agents/skills/followup/SKILL.md +78 -78
  76. package/template/.agents/skills/frontend-ui-engineering/SKILL.md +326 -326
  77. package/template/.agents/skills/frontend-ui-engineering/accessibility-checklist.md +159 -159
  78. package/template/.agents/skills/git-workflow-and-versioning/SKILL.md +300 -300
  79. package/template/.agents/skills/goal/SKILL.md +132 -132
  80. package/template/.agents/skills/help/SKILL.md +242 -242
  81. package/template/.agents/skills/human-review-decisions/SKILL.md +74 -74
  82. package/template/.agents/skills/i18n-localization/SKILL.md +154 -154
  83. package/template/.agents/skills/i18n-localization/scripts/i18n_checker.py +241 -241
  84. package/template/.agents/skills/idea-refine/SKILL.md +178 -178
  85. package/template/.agents/skills/idea-refine/examples.md +238 -238
  86. package/template/.agents/skills/idea-refine/frameworks.md +99 -99
  87. package/template/.agents/skills/idea-refine/refinement-criteria.md +113 -113
  88. package/template/.agents/skills/idea-refine/scripts/idea-refine.sh +15 -15
  89. package/template/.agents/skills/incremental-implementation/SKILL.md +248 -248
  90. package/template/.agents/skills/insight/SKILL.md +130 -130
  91. package/template/.agents/skills/insight-capture/SKILL.md +84 -84
  92. package/template/.agents/skills/intelligent-routing/SKILL.md +171 -171
  93. package/template/.agents/skills/issue-triage/SKILL.md +106 -106
  94. package/template/.agents/skills/lint-and-validate/SKILL.md +45 -45
  95. package/template/.agents/skills/lint-and-validate/scripts/lint_runner.py +184 -184
  96. package/template/.agents/skills/lint-and-validate/scripts/type_coverage.py +173 -173
  97. package/template/.agents/skills/mcp-builder/SKILL.md +176 -176
  98. package/template/.agents/skills/md2html/SKILL.md +154 -154
  99. package/template/.agents/skills/md2html/components.md +505 -505
  100. package/template/.agents/skills/md2html/template.html +1152 -1152
  101. package/template/.agents/skills/merge/SKILL.md +75 -75
  102. package/template/.agents/skills/mobile-design/SKILL.md +394 -394
  103. package/template/.agents/skills/mobile-design/decision-trees.md +516 -516
  104. package/template/.agents/skills/mobile-design/mobile-backend.md +491 -491
  105. package/template/.agents/skills/mobile-design/mobile-color-system.md +420 -420
  106. package/template/.agents/skills/mobile-design/mobile-debugging.md +122 -122
  107. package/template/.agents/skills/mobile-design/mobile-design-thinking.md +357 -357
  108. package/template/.agents/skills/mobile-design/mobile-navigation.md +458 -458
  109. package/template/.agents/skills/mobile-design/mobile-performance.md +767 -767
  110. package/template/.agents/skills/mobile-design/mobile-testing.md +356 -356
  111. package/template/.agents/skills/mobile-design/mobile-typography.md +433 -433
  112. package/template/.agents/skills/mobile-design/platform-android.md +666 -666
  113. package/template/.agents/skills/mobile-design/platform-ios.md +561 -561
  114. package/template/.agents/skills/mobile-design/scripts/mobile_audit.py +670 -670
  115. package/template/.agents/skills/mobile-design/touch-psychology.md +537 -537
  116. package/template/.agents/skills/nextjs-react-expert/1-async-eliminating-waterfalls.md +312 -312
  117. package/template/.agents/skills/nextjs-react-expert/2-bundle-bundle-size-optimization.md +240 -240
  118. package/template/.agents/skills/nextjs-react-expert/3-server-server-side-performance.md +490 -490
  119. package/template/.agents/skills/nextjs-react-expert/4-client-client-side-data-fetching.md +264 -264
  120. package/template/.agents/skills/nextjs-react-expert/5-rerender-re-render-optimization.md +581 -581
  121. package/template/.agents/skills/nextjs-react-expert/6-rendering-rendering-performance.md +432 -432
  122. package/template/.agents/skills/nextjs-react-expert/7-js-javascript-performance.md +684 -684
  123. package/template/.agents/skills/nextjs-react-expert/8-advanced-advanced-patterns.md +150 -150
  124. package/template/.agents/skills/nextjs-react-expert/SKILL.md +286 -286
  125. package/template/.agents/skills/nextjs-react-expert/scripts/convert_rules.py +222 -222
  126. package/template/.agents/skills/nextjs-react-expert/scripts/react_performance_checker.py +252 -252
  127. package/template/.agents/skills/nodejs-best-practices/SKILL.md +333 -333
  128. package/template/.agents/skills/obsidian-bases/SKILL.md +506 -506
  129. package/template/.agents/skills/obsidian-bases/references/FUNCTIONS_REFERENCE.md +173 -173
  130. package/template/.agents/skills/obsidian-cli/SKILL.md +115 -115
  131. package/template/.agents/skills/obsidian-clipper-template-creator/SKILL.md +73 -73
  132. package/template/.agents/skills/obsidian-clipper-template-creator/assets/clipping-template.json +51 -51
  133. package/template/.agents/skills/obsidian-clipper-template-creator/assets/recipe-template.json +48 -48
  134. package/template/.agents/skills/obsidian-clipper-template-creator/references/analysis-workflow.md +79 -79
  135. package/template/.agents/skills/obsidian-clipper-template-creator/references/bases-workflow.md +44 -44
  136. package/template/.agents/skills/obsidian-clipper-template-creator/references/filters.md +51 -51
  137. package/template/.agents/skills/obsidian-clipper-template-creator/references/json-schema.md +77 -77
  138. package/template/.agents/skills/obsidian-clipper-template-creator/references/logic.md +67 -67
  139. package/template/.agents/skills/obsidian-clipper-template-creator/references/variables.md +64 -64
  140. package/template/.agents/skills/obsidian-markdown/SKILL.md +205 -205
  141. package/template/.agents/skills/obsidian-markdown/references/CALLOUTS.md +58 -58
  142. package/template/.agents/skills/obsidian-markdown/references/EMBEDS.md +63 -63
  143. package/template/.agents/skills/obsidian-markdown/references/PROPERTIES.md +61 -61
  144. package/template/.agents/skills/package-json-generator/SKILL.md +60 -60
  145. package/template/.agents/skills/package-json-generator/scripts/generate.js +120 -120
  146. package/template/.agents/skills/package-json-generator/scripts/run.sh +19 -19
  147. package/template/.agents/skills/parallel-agents/SKILL.md +144 -144
  148. package/template/.agents/skills/performance-optimization/SKILL.md +299 -299
  149. package/template/.agents/skills/performance-optimization/performance-checklist.md +113 -113
  150. package/template/.agents/skills/planning-and-task-breakdown/SKILL.md +239 -239
  151. package/template/.agents/skills/powershell-windows/SKILL.md +167 -167
  152. package/template/.agents/skills/pr/SKILL.md +92 -92
  153. package/template/.agents/skills/pr-review/SKILL.md +142 -142
  154. package/template/.agents/skills/pr-review-analysis/SKILL.md +89 -89
  155. package/template/.agents/skills/prd/SKILL.md +112 -112
  156. package/template/.agents/skills/preview/SKILL.md +116 -116
  157. package/template/.agents/skills/preview-local-check/SKILL.md +59 -59
  158. package/template/.agents/skills/prompt-addons/SKILL.md +99 -99
  159. package/template/.agents/skills/prp-dev-fastapi/SKILL.md +114 -114
  160. package/template/.agents/skills/prp-dev-multiplatform/SKILL.md +36 -36
  161. package/template/.agents/skills/prp-dev-odoo/SKILL.md +71 -71
  162. package/template/.agents/skills/prp-dev-php/SKILL.md +63 -63
  163. package/template/.agents/skills/prp-sa-ba/SKILL.md +52 -52
  164. package/template/.agents/skills/python-patterns/SKILL.md +441 -441
  165. package/template/.agents/skills/red-team-tactics/SKILL.md +199 -199
  166. package/template/.agents/skills/release-git-operations/SKILL.md +97 -97
  167. package/template/.agents/skills/research/SKILL.md +127 -127
  168. package/template/.agents/skills/review/SKILL.md +55 -55
  169. package/template/.agents/skills/review-followup-routing/SKILL.md +98 -98
  170. package/template/.agents/skills/roadmap-strategy/SKILL.md +97 -97
  171. package/template/.agents/skills/security-and-hardening/SKILL.md +349 -349
  172. package/template/.agents/skills/security-and-hardening/security-checklist.md +134 -134
  173. package/template/.agents/skills/security-review/SKILL.md +164 -164
  174. package/template/.agents/skills/seo-fundamentals/SKILL.md +129 -129
  175. package/template/.agents/skills/seo-fundamentals/scripts/seo_checker.py +219 -219
  176. package/template/.agents/skills/server-management/SKILL.md +161 -161
  177. package/template/.agents/skills/shipping-and-launch/SKILL.md +311 -311
  178. package/template/.agents/skills/silent-failure-audit/SKILL.md +73 -73
  179. package/template/.agents/skills/simplify/SKILL.md +74 -74
  180. package/template/.agents/skills/skill-development/SKILL.md +86 -86
  181. package/template/.agents/skills/spec-driven-development/SKILL.md +207 -207
  182. package/template/.agents/skills/spec-orchestration/SKILL.md +91 -91
  183. package/template/.agents/skills/specialist-agent-routing/SKILL.md +107 -107
  184. package/template/.agents/skills/tailwind-patterns/SKILL.md +269 -269
  185. package/template/.agents/skills/test/SKILL.md +77 -77
  186. package/template/.agents/skills/test-driven-development/SKILL.md +422 -422
  187. package/template/.agents/skills/test-driven-development/testing-patterns.md +236 -236
  188. package/template/.agents/skills/test-execution-and-coverage/SKILL.md +56 -56
  189. package/template/.agents/skills/type-design/SKILL.md +84 -84
  190. package/template/.agents/skills/ui-ux-pro-max/SKILL.md +75 -75
  191. package/template/.agents/skills/using-agent-skills/SKILL.md +171 -171
  192. package/template/.agents/skills/verification-orchestration/SKILL.md +68 -68
  193. package/template/.agents/skills/vulnerability-scanner/SKILL.md +276 -276
  194. package/template/.agents/skills/vulnerability-scanner/checklists.md +121 -121
  195. package/template/.agents/skills/vulnerability-scanner/scripts/security_scan.py +458 -458
  196. package/template/.agents/skills/wiki/SKILL.md +148 -148
  197. package/template/.agents/skills/workflow-documentation-sync/SKILL.md +87 -87
  198. package/template/.claude/skills/20-spec/SKILL.md +136 -136
  199. package/template/.claude/skills/30-plan/SKILL.md +206 -206
  200. package/template/.claude/skills/40-implement/SKILL.md +152 -152
  201. package/template/.claude/skills/50-verify/SKILL.md +174 -174
  202. package/template/.claude/skills/60-report/SKILL.md +168 -168
  203. package/template/.claude/skills/70-release/SKILL.md +141 -141
  204. package/template/.claude/skills/9arm-skills/README.md +51 -51
  205. package/template/.claude/skills/9arm-skills/debug-mantra/SKILL.md +86 -86
  206. package/template/.claude/skills/9arm-skills/management-talk/SKILL.md +79 -79
  207. package/template/.claude/skills/9arm-skills/post-mortem/SKILL.md +71 -71
  208. package/template/.claude/skills/9arm-skills/scrutinize/SKILL.md +72 -72
  209. package/template/.claude/skills/agent/SKILL.md +164 -164
  210. package/template/.claude/skills/api-and-interface-design/SKILL.md +294 -294
  211. package/template/.claude/skills/app-builder/SKILL.md +75 -75
  212. package/template/.claude/skills/app-builder/agent-coordination.md +71 -71
  213. package/template/.claude/skills/app-builder/feature-building.md +53 -53
  214. package/template/.claude/skills/app-builder/project-detection.md +34 -34
  215. package/template/.claude/skills/app-builder/scaffolding.md +118 -118
  216. package/template/.claude/skills/app-builder/tech-stack.md +40 -40
  217. package/template/.claude/skills/app-builder/templates/SKILL.md +39 -39
  218. package/template/.claude/skills/app-builder/templates/astro-static/TEMPLATE.md +76 -76
  219. package/template/.claude/skills/app-builder/templates/chrome-extension/TEMPLATE.md +92 -92
  220. package/template/.claude/skills/app-builder/templates/cli-tool/TEMPLATE.md +88 -88
  221. package/template/.claude/skills/app-builder/templates/electron-desktop/TEMPLATE.md +88 -88
  222. package/template/.claude/skills/app-builder/templates/express-api/TEMPLATE.md +83 -83
  223. package/template/.claude/skills/app-builder/templates/flutter-app/TEMPLATE.md +90 -90
  224. package/template/.claude/skills/app-builder/templates/monorepo-turborepo/TEMPLATE.md +90 -90
  225. package/template/.claude/skills/app-builder/templates/nextjs-fullstack/TEMPLATE.md +122 -122
  226. package/template/.claude/skills/app-builder/templates/nextjs-saas/TEMPLATE.md +122 -122
  227. package/template/.claude/skills/app-builder/templates/nextjs-static/TEMPLATE.md +169 -169
  228. package/template/.claude/skills/app-builder/templates/nuxt-app/TEMPLATE.md +134 -134
  229. package/template/.claude/skills/app-builder/templates/python-fastapi/TEMPLATE.md +83 -83
  230. package/template/.claude/skills/app-builder/templates/react-native-app/TEMPLATE.md +119 -119
  231. package/template/.claude/skills/architecture/SKILL.md +55 -55
  232. package/template/.claude/skills/architecture/context-discovery.md +43 -43
  233. package/template/.claude/skills/architecture/examples.md +94 -94
  234. package/template/.claude/skills/architecture/pattern-selection.md +68 -68
  235. package/template/.claude/skills/architecture/patterns-reference.md +50 -50
  236. package/template/.claude/skills/architecture/trade-off-analysis.md +77 -77
  237. package/template/.claude/skills/bash-linux/SKILL.md +199 -199
  238. package/template/.claude/skills/behavioral-modes/SKILL.md +242 -242
  239. package/template/.claude/skills/brainstorm/SKILL.md +157 -157
  240. package/template/.claude/skills/browser-testing-with-devtools/SKILL.md +302 -302
  241. package/template/.claude/skills/changelog/SKILL.md +108 -108
  242. package/template/.claude/skills/check-for-updates/SKILL.md +116 -116
  243. package/template/.claude/skills/ci-cd-and-automation/SKILL.md +390 -390
  244. package/template/.claude/skills/code-review-and-quality/SKILL.md +392 -392
  245. package/template/.claude/skills/code-simplification/SKILL.md +331 -331
  246. package/template/.claude/skills/commit/SKILL.md +62 -62
  247. package/template/.claude/skills/competitor-analysis/SKILL.md +66 -66
  248. package/template/.claude/skills/context-engineering/SKILL.md +290 -290
  249. package/template/.claude/skills/database-design/SKILL.md +52 -52
  250. package/template/.claude/skills/database-design/database-selection.md +43 -43
  251. package/template/.claude/skills/database-design/indexing.md +39 -39
  252. package/template/.claude/skills/database-design/migrations.md +48 -48
  253. package/template/.claude/skills/database-design/optimization.md +36 -36
  254. package/template/.claude/skills/database-design/orm-selection.md +30 -30
  255. package/template/.claude/skills/database-design/schema-design.md +56 -56
  256. package/template/.claude/skills/database-design/scripts/schema_validator.py +172 -172
  257. package/template/.claude/skills/debug/SKILL.md +116 -116
  258. package/template/.claude/skills/debugging-and-error-recovery/SKILL.md +298 -298
  259. package/template/.claude/skills/deploy/SKILL.md +90 -90
  260. package/template/.claude/skills/deployment-procedures/SKILL.md +241 -241
  261. package/template/.claude/skills/deprecation-and-migration/SKILL.md +206 -206
  262. package/template/.claude/skills/devflow-concept-intake/SKILL.md +150 -150
  263. package/template/.claude/skills/devflow-concept-intake/agents/openai.yaml +4 -4
  264. package/template/.claude/skills/devflow-concept-intake/references/adoption-proposal-template.md +60 -60
  265. package/template/.claude/skills/devflow-concept-intake/references/concept-report-template.md +47 -47
  266. package/template/.claude/skills/devflow-concept-intake/references/upstream-tracking-template.md +51 -51
  267. package/template/.claude/skills/devflow-wiki/SKILL.md +75 -75
  268. package/template/.claude/skills/documentation-and-adrs/SKILL.md +278 -278
  269. package/template/.claude/skills/english-to-thai-translator/SKILL.md +125 -125
  270. package/template/.claude/skills/followup/SKILL.md +78 -78
  271. package/template/.claude/skills/frontend-ui-engineering/SKILL.md +326 -326
  272. package/template/.claude/skills/frontend-ui-engineering/accessibility-checklist.md +159 -159
  273. package/template/.claude/skills/git-workflow-and-versioning/SKILL.md +300 -300
  274. package/template/.claude/skills/goal/SKILL.md +132 -132
  275. package/template/.claude/skills/help/SKILL.md +242 -242
  276. package/template/.claude/skills/human-review-decisions/SKILL.md +74 -74
  277. package/template/.claude/skills/i18n-localization/SKILL.md +154 -154
  278. package/template/.claude/skills/i18n-localization/scripts/i18n_checker.py +241 -241
  279. package/template/.claude/skills/idea-refine/SKILL.md +178 -178
  280. package/template/.claude/skills/idea-refine/examples.md +238 -238
  281. package/template/.claude/skills/idea-refine/frameworks.md +99 -99
  282. package/template/.claude/skills/idea-refine/refinement-criteria.md +113 -113
  283. package/template/.claude/skills/idea-refine/scripts/idea-refine.sh +15 -15
  284. package/template/.claude/skills/incremental-implementation/SKILL.md +248 -248
  285. package/template/.claude/skills/insight/SKILL.md +130 -130
  286. package/template/.claude/skills/insight-capture/SKILL.md +84 -84
  287. package/template/.claude/skills/intelligent-routing/SKILL.md +171 -171
  288. package/template/.claude/skills/issue-triage/SKILL.md +106 -106
  289. package/template/.claude/skills/lint-and-validate/SKILL.md +45 -45
  290. package/template/.claude/skills/lint-and-validate/scripts/lint_runner.py +184 -184
  291. package/template/.claude/skills/lint-and-validate/scripts/type_coverage.py +173 -173
  292. package/template/.claude/skills/mcp-builder/SKILL.md +176 -176
  293. package/template/.claude/skills/md2html/SKILL.md +154 -154
  294. package/template/.claude/skills/md2html/components.md +505 -505
  295. package/template/.claude/skills/md2html/template.html +1152 -1152
  296. package/template/.claude/skills/merge/SKILL.md +75 -75
  297. package/template/.claude/skills/mobile-design/SKILL.md +394 -394
  298. package/template/.claude/skills/mobile-design/decision-trees.md +516 -516
  299. package/template/.claude/skills/mobile-design/mobile-backend.md +491 -491
  300. package/template/.claude/skills/mobile-design/mobile-color-system.md +420 -420
  301. package/template/.claude/skills/mobile-design/mobile-debugging.md +122 -122
  302. package/template/.claude/skills/mobile-design/mobile-design-thinking.md +357 -357
  303. package/template/.claude/skills/mobile-design/mobile-navigation.md +458 -458
  304. package/template/.claude/skills/mobile-design/mobile-performance.md +767 -767
  305. package/template/.claude/skills/mobile-design/mobile-testing.md +356 -356
  306. package/template/.claude/skills/mobile-design/mobile-typography.md +433 -433
  307. package/template/.claude/skills/mobile-design/platform-android.md +666 -666
  308. package/template/.claude/skills/mobile-design/platform-ios.md +561 -561
  309. package/template/.claude/skills/mobile-design/scripts/mobile_audit.py +670 -670
  310. package/template/.claude/skills/mobile-design/touch-psychology.md +537 -537
  311. package/template/.claude/skills/nextjs-react-expert/1-async-eliminating-waterfalls.md +312 -312
  312. package/template/.claude/skills/nextjs-react-expert/2-bundle-bundle-size-optimization.md +240 -240
  313. package/template/.claude/skills/nextjs-react-expert/3-server-server-side-performance.md +490 -490
  314. package/template/.claude/skills/nextjs-react-expert/4-client-client-side-data-fetching.md +264 -264
  315. package/template/.claude/skills/nextjs-react-expert/5-rerender-re-render-optimization.md +581 -581
  316. package/template/.claude/skills/nextjs-react-expert/6-rendering-rendering-performance.md +432 -432
  317. package/template/.claude/skills/nextjs-react-expert/7-js-javascript-performance.md +684 -684
  318. package/template/.claude/skills/nextjs-react-expert/8-advanced-advanced-patterns.md +150 -150
  319. package/template/.claude/skills/nextjs-react-expert/SKILL.md +286 -286
  320. package/template/.claude/skills/nextjs-react-expert/scripts/convert_rules.py +222 -222
  321. package/template/.claude/skills/nextjs-react-expert/scripts/react_performance_checker.py +252 -252
  322. package/template/.claude/skills/nodejs-best-practices/SKILL.md +333 -333
  323. package/template/.claude/skills/obsidian-bases/SKILL.md +506 -506
  324. package/template/.claude/skills/obsidian-bases/references/FUNCTIONS_REFERENCE.md +173 -173
  325. package/template/.claude/skills/obsidian-cli/SKILL.md +115 -115
  326. package/template/.claude/skills/obsidian-clipper-template-creator/SKILL.md +73 -73
  327. package/template/.claude/skills/obsidian-clipper-template-creator/assets/clipping-template.json +51 -51
  328. package/template/.claude/skills/obsidian-clipper-template-creator/assets/recipe-template.json +48 -48
  329. package/template/.claude/skills/obsidian-clipper-template-creator/references/analysis-workflow.md +79 -79
  330. package/template/.claude/skills/obsidian-clipper-template-creator/references/bases-workflow.md +44 -44
  331. package/template/.claude/skills/obsidian-clipper-template-creator/references/filters.md +51 -51
  332. package/template/.claude/skills/obsidian-clipper-template-creator/references/json-schema.md +77 -77
  333. package/template/.claude/skills/obsidian-clipper-template-creator/references/logic.md +67 -67
  334. package/template/.claude/skills/obsidian-clipper-template-creator/references/variables.md +64 -64
  335. package/template/.claude/skills/obsidian-markdown/SKILL.md +205 -205
  336. package/template/.claude/skills/obsidian-markdown/references/CALLOUTS.md +58 -58
  337. package/template/.claude/skills/obsidian-markdown/references/EMBEDS.md +63 -63
  338. package/template/.claude/skills/obsidian-markdown/references/PROPERTIES.md +61 -61
  339. package/template/.claude/skills/package-json-generator/SKILL.md +60 -60
  340. package/template/.claude/skills/package-json-generator/scripts/generate.js +120 -120
  341. package/template/.claude/skills/package-json-generator/scripts/run.sh +19 -19
  342. package/template/.claude/skills/parallel-agents/SKILL.md +144 -144
  343. package/template/.claude/skills/performance-optimization/SKILL.md +299 -299
  344. package/template/.claude/skills/performance-optimization/performance-checklist.md +113 -113
  345. package/template/.claude/skills/planning-and-task-breakdown/SKILL.md +239 -239
  346. package/template/.claude/skills/powershell-windows/SKILL.md +167 -167
  347. package/template/.claude/skills/pr/SKILL.md +92 -92
  348. package/template/.claude/skills/pr-review/SKILL.md +142 -142
  349. package/template/.claude/skills/pr-review-analysis/SKILL.md +89 -89
  350. package/template/.claude/skills/prd/SKILL.md +112 -112
  351. package/template/.claude/skills/preview/SKILL.md +116 -116
  352. package/template/.claude/skills/preview-local-check/SKILL.md +59 -59
  353. package/template/.claude/skills/prompt-addons/SKILL.md +99 -99
  354. package/template/.claude/skills/prp-dev-fastapi/SKILL.md +114 -114
  355. package/template/.claude/skills/prp-dev-multiplatform/SKILL.md +36 -36
  356. package/template/.claude/skills/prp-dev-odoo/SKILL.md +71 -71
  357. package/template/.claude/skills/prp-dev-php/SKILL.md +63 -63
  358. package/template/.claude/skills/prp-sa-ba/SKILL.md +52 -52
  359. package/template/.claude/skills/python-patterns/SKILL.md +441 -441
  360. package/template/.claude/skills/red-team-tactics/SKILL.md +199 -199
  361. package/template/.claude/skills/release-git-operations/SKILL.md +97 -97
  362. package/template/.claude/skills/research/SKILL.md +127 -127
  363. package/template/.claude/skills/review/SKILL.md +55 -55
  364. package/template/.claude/skills/review-followup-routing/SKILL.md +98 -98
  365. package/template/.claude/skills/roadmap-strategy/SKILL.md +97 -97
  366. package/template/.claude/skills/security-and-hardening/SKILL.md +349 -349
  367. package/template/.claude/skills/security-and-hardening/security-checklist.md +134 -134
  368. package/template/.claude/skills/security-review/SKILL.md +164 -164
  369. package/template/.claude/skills/seo-fundamentals/SKILL.md +129 -129
  370. package/template/.claude/skills/seo-fundamentals/scripts/seo_checker.py +219 -219
  371. package/template/.claude/skills/server-management/SKILL.md +161 -161
  372. package/template/.claude/skills/shipping-and-launch/SKILL.md +311 -311
  373. package/template/.claude/skills/silent-failure-audit/SKILL.md +73 -73
  374. package/template/.claude/skills/simplify/SKILL.md +74 -74
  375. package/template/.claude/skills/skill-development/SKILL.md +86 -86
  376. package/template/.claude/skills/spec-driven-development/SKILL.md +207 -207
  377. package/template/.claude/skills/spec-orchestration/SKILL.md +91 -91
  378. package/template/.claude/skills/specialist-agent-routing/SKILL.md +107 -107
  379. package/template/.claude/skills/tailwind-patterns/SKILL.md +269 -269
  380. package/template/.claude/skills/test/SKILL.md +77 -77
  381. package/template/.claude/skills/test-driven-development/SKILL.md +422 -422
  382. package/template/.claude/skills/test-driven-development/testing-patterns.md +236 -236
  383. package/template/.claude/skills/test-execution-and-coverage/SKILL.md +56 -56
  384. package/template/.claude/skills/type-design/SKILL.md +84 -84
  385. package/template/.claude/skills/ui-ux-pro-max/SKILL.md +75 -75
  386. package/template/.claude/skills/using-agent-skills/SKILL.md +171 -171
  387. package/template/.claude/skills/verification-orchestration/SKILL.md +68 -68
  388. package/template/.claude/skills/vulnerability-scanner/SKILL.md +276 -276
  389. package/template/.claude/skills/vulnerability-scanner/checklists.md +121 -121
  390. package/template/.claude/skills/vulnerability-scanner/scripts/security_scan.py +458 -458
  391. package/template/.claude/skills/wiki/SKILL.md +148 -148
  392. package/template/.claude/skills/workflow-documentation-sync/SKILL.md +87 -87
  393. package/template/.nexus/nexus-devflow.json +1 -1
  394. package/template/LICENSE +21 -21
@@ -1,670 +1,670 @@
1
- #!/usr/bin/env python3
2
- """
3
- Mobile UX Audit Script - Full Mobile Design Coverage
4
-
5
- Analyzes React Native / Flutter code for compliance with:
6
-
7
- 1. TOUCH PSYCHOLOGY (touch-psychology.md):
8
- - Touch Target Sizes (44pt iOS, 48dp Android, 44px WCAG)
9
- - Touch Target Spacing (8px minimum gap)
10
- - Thumb Zone Placement (primary CTAs at bottom)
11
- - Gesture Alternatives (visible buttons for swipe)
12
- - Haptic Feedback Patterns
13
- - Touch Feedback Timing (<50ms)
14
- - Touch Accessibility (motor impairment support)
15
-
16
- 2. MOBILE PERFORMANCE (mobile-performance.md):
17
- - ScrollView vs FlatList (CRITICAL)
18
- - React.memo for List Items
19
- - useCallback for renderItem
20
- - Stable keyExtractor (NOT index)
21
- - useNativeDriver for Animations
22
- - Memory Leak Prevention (cleanup)
23
- - Console.log Detection
24
- - Inline Function Detection
25
- - Animation Performance (transform/opacity only)
26
-
27
- 3. MOBILE NAVIGATION (mobile-navigation.md):
28
- - Tab Bar Max Items (5)
29
- - Tab State Preservation
30
- - Proper Back Handling
31
- - Deep Link Support
32
- - Navigation Structure
33
-
34
- 4. MOBILE TYPOGRAPHY (mobile-typography.md):
35
- - System Font Usage
36
- - Dynamic Type Support (iOS)
37
- - Text Scaling Constraints
38
- - Mobile Line Height
39
- - Font Size Limits
40
-
41
- 5. MOBILE COLOR SYSTEM (mobile-color-system.md):
42
- - Pure Black Avoidance (#000000)
43
- - OLED Optimization
44
- - Dark Mode Support
45
- - Contrast Ratios
46
-
47
- 6. PLATFORM iOS (platform-ios.md):
48
- - SF Symbols Usage
49
- - iOS Navigation Patterns
50
- - iOS Haptic Types
51
- - iOS-Specific Components
52
-
53
- 7. PLATFORM ANDROID (platform-android.md):
54
- - Material Icons Usage
55
- - Android Navigation Patterns
56
- - Ripple Effects
57
- - Android-Specific Components
58
-
59
- 8. MOBILE BACKEND (mobile-backend.md):
60
- - Secure Storage (NOT AsyncStorage)
61
- - Offline Handling
62
- - Push Notification Support
63
- - API Response Caching
64
-
65
- Total: 50+ mobile-specific checks
66
- """
67
-
68
- import sys
69
- import os
70
- import re
71
- import json
72
- from pathlib import Path
73
-
74
- class MobileAuditor:
75
- def __init__(self):
76
- self.issues = []
77
- self.warnings = []
78
- self.passed_count = 0
79
- self.files_checked = 0
80
-
81
- def audit_file(self, filepath: str) -> None:
82
- try:
83
- with open(filepath, 'r', encoding='utf-8', errors='replace') as f:
84
- content = f.read()
85
- except:
86
- return
87
-
88
- self.files_checked += 1
89
- filename = os.path.basename(filepath)
90
-
91
- # Detect framework
92
- is_react_native = bool(re.search(r'react-native|@react-navigation|React\.Native', content))
93
- is_flutter = bool(re.search(r'import \'package:flutter|MaterialApp|Widget\.build', content))
94
-
95
- if not (is_react_native or is_flutter):
96
- return # Skip non-mobile files
97
-
98
- # --- 1. TOUCH PSYCHOLOGY CHECKS ---
99
-
100
- # 1.1 Touch Target Size Check
101
- # Look for small touch targets
102
- small_sizes = re.findall(r'(?:width|height|size):\s*([0-3]\d)', content)
103
- for size in small_sizes:
104
- if int(size) < 44:
105
- self.issues.append(f"[Touch Target] {filename}: Touch target size {size}px < 44px minimum (iOS: 44pt, Android: 48dp)")
106
-
107
- # 1.2 Touch Target Spacing Check
108
- # Look for inadequate spacing between touchable elements
109
- small_gaps = re.findall(r'(?:margin|gap):\s*([0-7])\s*(?:px|dp)', content)
110
- for gap in small_gaps:
111
- if int(gap) < 8:
112
- self.warnings.append(f"[Touch Spacing] {filename}: Touch target spacing {gap}px < 8px minimum. Accidental taps risk.")
113
-
114
- # 1.3 Thumb Zone Placement Check
115
- # Primary CTAs should be at bottom (easy thumb reach)
116
- primary_buttons = re.findall(r'(?:testID|id):\s*["\'](?:.*(?:primary|cta|submit|confirm)[^"\']*)["\']', content, re.IGNORECASE)
117
- has_bottom_placement = bool(re.search(r'position:\s*["\']?absolute["\']?|bottom:\s*\d+|style.*bottom|justifyContent:\s*["\']?flex-end', content))
118
- if primary_buttons and not has_bottom_placement:
119
- self.warnings.append(f"[Thumb Zone] {filename}: Primary CTA may not be in thumb zone (bottom). Place primary actions at bottom for easy reach.")
120
-
121
- # 1.4 Gesture Alternatives Check
122
- # Swipe actions should have visible button alternatives
123
- has_swipe_gestures = bool(re.search(r'Swipeable|onSwipe|PanGestureHandler|swipe', content))
124
- has_visible_buttons = bool(re.search(r'Button.*(?:delete|archive|more)|TouchableOpacity|Pressable', content))
125
- if has_swipe_gestures and not has_visible_buttons:
126
- self.warnings.append(f"[Gestures] {filename}: Swipe gestures detected without visible button alternatives. Motor impaired users need alternatives.")
127
-
128
- # 1.5 Haptic Feedback Check
129
- # Important actions should have haptic feedback
130
- has_important_actions = bool(re.search(r'(?:onPress|onSubmit|delete|remove|confirm|purchase)', content))
131
- has_haptics = bool(re.search(r'Haptics|Vibration|react-native-haptic-feedback|FeedbackManager', content))
132
- if has_important_actions and not has_haptics:
133
- self.warnings.append(f"[Haptics] {filename}: Important actions without haptic feedback. Consider adding haptic confirmation.")
134
-
135
- # 1.6 Touch Feedback Timing Check
136
- # Touch feedback should be immediate (<50ms)
137
- if is_react_native:
138
- has_pressable = bool(re.search(r'Pressable|TouchableOpacity', content))
139
- has_feedback_state = bool(re.search(r'pressed|style.*opacity|underlay', content))
140
- if has_pressable and not has_feedback_state:
141
- self.warnings.append(f"[Touch Feedback] {filename}: Pressable without visual feedback state. Add opacity/scale change for tap confirmation.")
142
-
143
- # --- 2. MOBILE PERFORMANCE CHECKS ---
144
-
145
- # 2.1 CRITICAL: ScrollView vs FlatList
146
- has_scrollview = bool(re.search(r'<ScrollView|ScrollView\.', content))
147
- has_map_in_scrollview = bool(re.search(r'ScrollView.*\.map\(|ScrollView.*\{.*\.map', content))
148
- if has_scrollview and has_map_in_scrollview:
149
- self.issues.append(f"[Performance CRITICAL] {filename}: ScrollView with .map() detected. Use FlatList for lists to prevent memory explosion.")
150
-
151
- # 2.2 React.memo Check
152
- if is_react_native:
153
- has_list = bool(re.search(r'FlatList|FlashList|SectionList', content))
154
- has_react_memo = bool(re.search(r'React\.memo|memo\(', content))
155
- if has_list and not has_react_memo:
156
- self.warnings.append(f"[Performance] {filename}: FlatList without React.memo on list items. Items will re-render on every parent update.")
157
-
158
- # 2.3 useCallback Check
159
- if is_react_native:
160
- has_flatlist = bool(re.search(r'FlatList|FlashList', content))
161
- has_use_callback = bool(re.search(r'useCallback', content))
162
- if has_flatlist and not has_use_callback:
163
- self.warnings.append(f"[Performance] {filename}: FlatList renderItem without useCallback. New function created every render.")
164
-
165
- # 2.4 keyExtractor Check (CRITICAL)
166
- if is_react_native:
167
- has_flatlist = bool(re.search(r'FlatList', content))
168
- has_key_extractor = bool(re.search(r'keyExtractor', content))
169
- uses_index_key = bool(re.search(r'key=\{.*index.*\}|key:\s*index', content))
170
- if has_flatlist and not has_key_extractor:
171
- self.issues.append(f"[Performance CRITICAL] {filename}: FlatList without keyExtractor. Index-based keys cause bugs on reorder/delete.")
172
- if uses_index_key:
173
- self.issues.append(f"[Performance CRITICAL] {filename}: Using index as key. This causes bugs when list changes. Use unique ID from data.")
174
-
175
- # 2.5 useNativeDriver Check
176
- if is_react_native:
177
- has_animated = bool(re.search(r'Animated\.', content))
178
- has_native_driver = bool(re.search(r'useNativeDriver:\s*true', content))
179
- has_native_driver_false = bool(re.search(r'useNativeDriver:\s*false', content))
180
- if has_animated and has_native_driver_false:
181
- self.warnings.append(f"[Performance] {filename}: Animation with useNativeDriver: false. Use true for 60fps (only supports transform/opacity).")
182
- if has_animated and not has_native_driver:
183
- self.warnings.append(f"[Performance] {filename}: Animated component without useNativeDriver. Add useNativeDriver: true for 60fps.")
184
-
185
- # 2.6 Memory Leak Check
186
- if is_react_native:
187
- has_effect = bool(re.search(r'useEffect', content))
188
- has_cleanup = bool(re.search(r'return\s*\(\)\s*=>|return\s+function', content))
189
- has_subscriptions = bool(re.search(r'addEventListener|subscribe|\.focus\(\)|\.off\(', content))
190
- if has_effect and has_subscriptions and not has_cleanup:
191
- self.issues.append(f"[Memory Leak] {filename}: useEffect with subscriptions but no cleanup function. Memory leak on unmount.")
192
-
193
- # 2.7 Console.log Detection
194
- console_logs = len(re.findall(r'console\.log|console\.warn|console\.error|console\.debug', content))
195
- if console_logs > 5:
196
- self.warnings.append(f"[Performance] {filename}: {console_logs} console.log statements detected. Remove before production (blocks JS thread).")
197
-
198
- # 2.8 Inline Function Detection
199
- if is_react_native:
200
- inline_functions = re.findall(r'(?:onPress|onPressIn|onPressOut|renderItem):\s*\([^)]*\)\s*=>', content)
201
- if len(inline_functions) > 3:
202
- self.warnings.append(f"[Performance] {filename}: {len(inline_functions)} inline arrow functions in props. Creates new function every render. Use useCallback.")
203
-
204
- # 2.9 Animation Properties Check
205
- # Warn if animating expensive properties
206
- animating_layout = bool(re.search(r'Animated\.timing.*(?:width|height|margin|padding)', content))
207
- if animating_layout:
208
- self.issues.append(f"[Performance] {filename}: Animating layout properties (width/height/margin). Use transform/opacity for 60fps.")
209
-
210
- # --- 3. MOBILE NAVIGATION CHECKS ---
211
-
212
- # 3.1 Tab Bar Max Items Check
213
- tab_bar_items = len(re.findall(r'Tab\.Screen|createBottomTabNavigator|BottomTab', content))
214
- if tab_bar_items > 5:
215
- self.warnings.append(f"[Navigation] {filename}: {tab_bar_items} tab bar items (max 5 recommended). More than 5 becomes hard to tap.")
216
-
217
- # 3.2 Tab State Preservation Check
218
- has_tab_nav = bool(re.search(r'createBottomTabNavigator|Tab\.Navigator', content))
219
- if has_tab_nav:
220
- # Look for lazy prop (false preserves state)
221
- has_lazy_false = bool(re.search(r'lazy:\s*false', content))
222
- if not has_lazy_false:
223
- self.warnings.append(f"[Navigation] {filename}: Tab navigation without lazy: false. Tabs may lose state on switch.")
224
-
225
- # 3.3 Back Handling Check
226
- has_back_listener = bool(re.search(r'BackHandler|useFocusEffect|navigation\.addListener', content))
227
- has_custom_back = bool(re.search(r'onBackPress|handleBackPress', content))
228
- if has_custom_back and not has_back_listener:
229
- self.warnings.append(f"[Navigation] {filename}: Custom back handling without BackHandler listener. May not work correctly.")
230
-
231
- # 3.4 Deep Link Support Check
232
- has_linking = bool(re.search(r'Linking\.|Linking\.openURL|deepLink|universalLink', content))
233
- has_config = bool(re.search(r'apollo-link|react-native-screens|navigation\.link', content))
234
- if not has_linking and not has_config:
235
- self.passed_count += 1
236
- else:
237
- if has_linking and not has_config:
238
- self.warnings.append(f"[Navigation] {filename}: Deep linking detected but may lack proper configuration. Test notification/share flows.")
239
-
240
- # --- 4. MOBILE TYPOGRAPHY CHECKS ---
241
-
242
- # 4.1 System Font Check
243
- if is_react_native:
244
- has_custom_font = bool(re.search(r"fontFamily:\s*[\"'][^\"']+", content))
245
- has_system_font = bool(re.search(r"fontFamily:\s*[\"']?(?:System|San Francisco|Roboto|-apple-system)", content))
246
- if has_custom_font and not has_system_font:
247
- self.warnings.append(f"[Typography] {filename}: Custom font detected. Consider system fonts (iOS: SF Pro, Android: Roboto) for native feel.")
248
-
249
- # 4.2 Text Scaling Check (iOS Dynamic Type)
250
- if is_react_native:
251
- has_font_sizes = bool(re.search(r'fontSize:', content))
252
- has_scaling = bool(re.search(r'allowFontScaling:\s*true|responsiveFontSize|useWindowDimensions', content))
253
- if has_font_sizes and not has_scaling:
254
- self.warnings.append(f"[Typography] {filename}: Fixed font sizes without scaling support. Consider allowFontScaling for accessibility.")
255
-
256
- # 4.3 Mobile Line Height Check
257
- line_heights = re.findall(r'lineHeight:\s*([\d.]+)', content)
258
- for lh in line_heights:
259
- if float(lh) > 1.8:
260
- self.warnings.append(f"[Typography] {filename}: lineHeight {lh} too high for mobile. Mobile text needs tighter spacing (1.3-1.5).")
261
-
262
- # 4.4 Font Size Limits
263
- font_sizes = re.findall(r'fontSize:\s*([\d.]+)', content)
264
- for fs in font_sizes:
265
- size = float(fs)
266
- if size < 12:
267
- self.warnings.append(f"[Typography] {filename}: fontSize {size}px below 12px minimum readability.")
268
- elif size > 32:
269
- self.warnings.append(f"[Typography] {filename}: fontSize {size}px very large. Consider using responsive scaling.")
270
-
271
- # --- 5. MOBILE COLOR SYSTEM CHECKS ---
272
-
273
- # 5.1 Pure Black Avoidance
274
- if re.search(r'#000000|color:\s*black|backgroundColor:\s*["\']?black', content):
275
- self.warnings.append(f"[Color] {filename}: Pure black (#000000) detected. Use dark gray (#1C1C1E iOS, #121212 Android) for better OLED/battery.")
276
-
277
- # 5.2 Dark Mode Support
278
- has_color_schemes = bool(re.search(r'useColorScheme|colorScheme|appearance:\s*["\']?dark', content))
279
- has_dark_mode_style = bool(re.search(r'\\\?.*dark|style:\s*.*dark|isDark', content))
280
- if not has_color_schemes and not has_dark_mode_style:
281
- self.warnings.append(f"[Color] {filename}: No dark mode support detected. Consider useColorScheme for system dark mode.")
282
-
283
- # --- 6. PLATFORM iOS CHECKS ---
284
-
285
- if is_react_native:
286
- # 6.1 SF Symbols Check
287
- has_ios_icons = bool(re.search(r'@expo/vector-icons|ionicons', content))
288
- has_sf_symbols = bool(re.search(r'sf-symbol|SF Symbols', content))
289
- if has_ios_icons and not has_sf_symbols:
290
- self.passed_count += 1
291
-
292
- # 6.2 iOS Haptic Types
293
- has_haptic_import = bool(re.search(r'expo-haptics|react-native-haptic-feedback', content))
294
- has_haptic_types = bool(re.search(r'ImpactFeedback|NotificationFeedback|SelectionFeedback', content))
295
- if has_haptic_import and not has_haptic_types:
296
- self.warnings.append(f"[iOS Haptics] {filename}: Haptic library imported but not using typed haptics (Impact/Notification/Selection).")
297
-
298
- # 6.3 iOS Safe Area
299
- has_safe_area = bool(re.search(r'SafeAreaView|useSafeAreaInsets|safeArea', content))
300
- if not has_safe_area:
301
- self.warnings.append(f"[iOS] {filename}: No SafeArea detected. Content may be hidden by notch/home indicator.")
302
-
303
- # --- 7. PLATFORM ANDROID CHECKS ---
304
-
305
- if is_react_native:
306
- # 7.1 Material Icons Check
307
- has_material_icons = bool(re.search(r'@expo/vector-icons|MaterialIcons', content))
308
- if has_material_icons:
309
- self.passed_count += 1
310
-
311
- # 7.2 Ripple Effect
312
- has_ripple = bool(re.search(r'ripple|android_ripple|foregroundRipple', content))
313
- has_pressable = bool(re.search(r'Pressable|Touchable', content))
314
- if has_pressable and not has_ripple:
315
- self.warnings.append(f"[Android] {filename}: Touchable without ripple effect. Android users expect ripple feedback.")
316
-
317
- # 7.3 Hardware Back Button
318
- if is_react_native:
319
- has_back_button = bool(re.search(r'BackHandler|useBackHandler', content))
320
- has_navigation = bool(re.search(r'@react-navigation', content))
321
- if has_navigation and not has_back_button:
322
- self.warnings.append(f"[Android] {filename}: React Navigation detected without BackHandler listener. Android hardware back may not work correctly.")
323
-
324
- # --- 8. MOBILE BACKEND CHECKS ---
325
-
326
- # 8.1 Secure Storage Check
327
- has_async_storage = bool(re.search(r'AsyncStorage|@react-native-async-storage', content))
328
- has_secure_storage = bool(re.search(r'SecureStore|Keychain|EncryptedSharedPreferences', content))
329
- has_token_storage = bool(re.search(r'token|jwt|auth.*storage', content, re.IGNORECASE))
330
- if has_token_storage and has_async_storage and not has_secure_storage:
331
- self.issues.append(f"[Security] {filename}: Storing auth tokens in AsyncStorage (insecure). Use SecureStore (iOS) / EncryptedSharedPreferences (Android).")
332
-
333
- # 8.2 Offline Handling Check
334
- has_network = bool(re.search(r'fetch|axios|netinfo|@react-native-community/netinfo', content))
335
- has_offline = bool(re.search(r'offline|isConnected|netInfo|cache.*offline', content))
336
- if has_network and not has_offline:
337
- self.warnings.append(f"[Offline] {filename}: Network requests detected without offline handling. Consider NetInfo for connection status.")
338
-
339
- # 8.3 Push Notification Support
340
- has_push = bool(re.search(r'Notifications|pushNotification|Firebase\.messaging|PushNotificationIOS', content))
341
- has_push_handler = bool(re.search(r'onNotification|addNotificationListener|notification\.open', content))
342
- if has_push and not has_push_handler:
343
- self.warnings.append(f"[Push] {filename}: Push notifications imported but no handler found. May miss notifications.")
344
-
345
- # --- 9. EXTENDED MOBILE TYPOGRAPHY CHECKS ---
346
-
347
- # 9.1 iOS Type Scale Check
348
- if is_react_native:
349
- # Check for iOS text styles that match HIG
350
- has_large_title = bool(re.search(r'fontSize:\s*34|largeTitle|font-weight:\s*["\']?bold', content))
351
- has_title_1 = bool(re.search(r'fontSize:\s*28', content))
352
- has_headline = bool(re.search(r'fontSize:\s*17.*semibold|headline', content))
353
- has_body = bool(re.search(r'fontSize:\s*17.*regular|body', content))
354
-
355
- # Check if following iOS scale roughly
356
- font_sizes = re.findall(r'fontSize:\s*([\d.]+)', content)
357
- ios_scale_sizes = [34, 28, 22, 20, 17, 16, 15, 13, 12, 11]
358
- matching_ios = sum(1 for size in font_sizes if any(abs(float(size) - ios_size) < 1 for ios_size in ios_scale_sizes))
359
-
360
- if len(font_sizes) > 3 and matching_ios < len(font_sizes) / 2:
361
- self.warnings.append(f"[iOS Typography] {filename}: Font sizes don't match iOS type scale. Consider iOS text styles for native feel.")
362
-
363
- # 9.2 Android Material Type Scale Check
364
- if is_react_native:
365
- # Check for Material 3 text styles
366
- has_display = bool(re.search(r'fontSize:\s*[456][0-9]|display', content))
367
- has_headline_material = bool(re.search(r'fontSize:\s*[23][0-9]|headline', content))
368
- has_title_material = bool(re.search(r'fontSize:\s*2[12][0-9].*medium|title', content))
369
- has_body_material = bool(re.search(r'fontSize:\s*1[456].*regular|body', content))
370
- has_label = bool(re.search(r'fontSize:\s*1[1234].*medium|label', content))
371
-
372
- # Check if using sp (scale-independent pixels)
373
- uses_sp = bool(re.search(r'\d+\s*sp\b', content))
374
- if has_display or has_headline_material:
375
- if not uses_sp:
376
- self.warnings.append(f"[Android Typography] {filename}: Material typography detected without sp units. Use sp for text to respect user font size preferences.")
377
-
378
- # 9.3 Modular Scale Check
379
- # Check if font sizes follow modular scale
380
- font_sizes = re.findall(r'fontSize:\s*(\d+(?:\.\d+)?)', content)
381
- if len(font_sizes) > 3:
382
- sorted_sizes = sorted(set([float(s) for s in font_sizes]))
383
- ratios = []
384
- for i in range(1, len(sorted_sizes)):
385
- if sorted_sizes[i-1] > 0:
386
- ratios.append(sorted_sizes[i] / sorted_sizes[i-1])
387
-
388
- # Common ratios: 1.125, 1.2, 1.25, 1.333, 1.5
389
- common_ratios = {1.125, 1.2, 1.25, 1.333, 1.5}
390
- for ratio in ratios[:3]:
391
- if not any(abs(ratio - cr) < 0.03 for cr in common_ratios):
392
- self.warnings.append(f"[Typography] {filename}: Font sizes may not follow modular scale (ratio: {ratio:.2f}). Consider consistent ratio.")
393
- break
394
-
395
- # 9.4 Line Length Check (Mobile-specific)
396
- # Mobile text should be 40-60 characters max
397
- if is_react_native:
398
- has_long_text = bool(re.search(r'<Text[^>]*>[^<]{40,}', content))
399
- has_max_width = bool(re.search(r'maxWidth|max-w-\d+|width:\s*["\']?\d+', content))
400
- if has_long_text and not has_max_width:
401
- self.warnings.append(f"[Mobile Typography] {filename}: Text without max-width constraint. Mobile text should be 40-60 characters per line for readability.")
402
-
403
- # 9.5 Font Weight Pattern Check
404
- # Check for font weight distribution
405
- if is_react_native:
406
- font_weights = re.findall(r'fontWeight:\s*["\']?(\d+|normal|bold|medium|light)', content)
407
- weight_map = {'normal': '400', 'light': '300', 'medium': '500', 'bold': '700'}
408
- numeric_weights = []
409
- for w in font_weights:
410
- val = weight_map.get(w.lower(), w)
411
- try:
412
- numeric_weights.append(int(val))
413
- except:
414
- pass
415
-
416
- # Check if overusing bold (mobile should be regular-dominant)
417
- bold_count = sum(1 for w in numeric_weights if w >= 700)
418
- regular_count = sum(1 for w in numeric_weights if 400 <= w < 500)
419
- if bold_count > regular_count:
420
- self.warnings.append(f"[Mobile Typography] {filename}: More bold weights than regular. Mobile typography should be regular-dominant for readability.")
421
-
422
- # --- 10. EXTENDED MOBILE COLOR SYSTEM CHECKS ---
423
-
424
- # 10.1 OLED Optimization Check
425
- # Check for near-black colors instead of pure black
426
- if re.search(r'#121212|#1A1A1A|#0D0D0D', content):
427
- self.passed_count += 1 # Good OLED optimization
428
- elif re.search(r'backgroundColor:\s*["\']?#000000', content):
429
- # Using pure black for background is OK for OLED
430
- pass
431
- elif re.search(r'backgroundColor:\s*["\']?#[0-9A-Fa-f]{6}', content):
432
- # Check if using light colors in dark mode (bad for OLED)
433
- self.warnings.append(f"[Mobile Color] {filename}: Consider OLED-optimized dark backgrounds (#121212 Android, #000000 iOS) for battery savings.")
434
-
435
- # 10.2 Saturated Color Detection (Battery)
436
- # Highly saturated colors consume more power on OLED
437
- hex_colors = re.findall(r'#([0-9A-Fa-f]{2})([0-9A-Fa-f]{2})([0-9A-Fa-f]{2})', content)
438
- saturated_count = 0
439
- for r, g, b in hex_colors:
440
- # Convert to RGB 0-255
441
- try:
442
- r_val, g_val, b_val = int(r, 16), int(g, 16), int(b, 16)
443
- max_val = max(r_val, g_val, b_val)
444
- min_val = min(r_val, g_val, b_val)
445
- # Saturation = (max - min) / max
446
- if max_val > 0:
447
- saturation = (max_val - min_val) / max_val
448
- if saturation > 0.8: # Highly saturated
449
- saturated_count += 1
450
- except:
451
- pass
452
-
453
- if saturated_count > 10:
454
- self.warnings.append(f"[Mobile Color] {filename}: {saturated_count} highly saturated colors detected. Desaturated colors save battery on OLED screens.")
455
-
456
- # 10.3 Outdoor Visibility Check
457
- # Low contrast combinations fail in outdoor sunlight
458
- light_colors = re.findall(r'#[0-9A-Fa-f]{6}|rgba?\([^)]+\)', content)
459
- # Check for potential low contrast (light gray on white, dark gray on black)
460
- potential_low_contrast = bool(re.search(r'#[EeEeEeEe].*#ffffff|#999999.*#ffffff|#333333.*#000000|#666666.*#000000', content))
461
- if potential_low_contrast:
462
- self.warnings.append(f"[Mobile Color] {filename}: Possible low contrast combination detected. Critical for outdoor visibility. Ensure WCAG AAA (7:1) for mobile.")
463
-
464
- # 10.4 Dark Mode Text Color Check
465
- # In dark mode, text should not be pure white
466
- has_dark_mode = bool(re.search(r'dark:\s*|isDark|useColorScheme|colorScheme:\s*["\']?dark', content))
467
- if has_dark_mode:
468
- has_pure_white_text = bool(re.search(r'color:\s*["\']?#ffffff|#fff["\']?\}|textColor:\s*["\']?white', content))
469
- if has_pure_white_text:
470
- self.warnings.append(f"[Mobile Color] {filename}: Pure white text (#FFFFFF) in dark mode. Use #E8E8E8 or light gray for better readability.")
471
-
472
- # --- 11. EXTENDED PLATFORM IOS CHECKS ---
473
-
474
- if is_react_native:
475
- # 11.1 SF Pro Font Detection
476
- has_sf_pro = bool(re.search(r'SF Pro|SFPro|fontFamily:\s*["\']?[-\s]*SF', content))
477
- has_custom_font = bool(re.search(r'fontFamily:\s*["\'][^"\']+', content))
478
- if has_custom_font and not has_sf_pro:
479
- self.warnings.append(f"[iOS] {filename}: Custom font without SF Pro fallback. Consider SF Pro Text for body, SF Pro Display for headings.")
480
-
481
- # 11.2 iOS System Colors Check
482
- # Check for semantic color usage
483
- has_label = bool(re.search(r'color:\s*["\']?label|\.label', content))
484
- has_secondaryLabel = bool(re.search(r'secondaryLabel|\.secondaryLabel', content))
485
- has_systemBackground = bool(re.search(r'systemBackground|\.systemBackground', content))
486
-
487
- has_hardcoded_gray = bool(re.search(r'#[78]0{4}', content))
488
- if has_hardcoded_gray and not (has_label or has_secondaryLabel):
489
- self.warnings.append(f"[iOS] {filename}: Hardcoded gray colors detected. Consider iOS semantic colors (label, secondaryLabel) for automatic dark mode.")
490
-
491
- # 11.3 iOS Accent Colors Check
492
- ios_blue = bool(re.search(r'#007AFF|#0A84FF|systemBlue', content))
493
- ios_green = bool(re.search(r'#34C759|#30D158|systemGreen', content))
494
- ios_red = bool(re.search(r'#FF3B30|#FF453A|systemRed', content))
495
-
496
- has_custom_primary = bool(re.search(r'primaryColor|theme.*primary|colors\.primary', content))
497
- if has_custom_primary and not (ios_blue or ios_green or ios_red):
498
- self.warnings.append(f"[iOS] {filename}: Custom primary color without iOS system color fallback. Consider systemBlue for consistent iOS feel.")
499
-
500
- # 11.4 iOS Navigation Patterns Check
501
- has_navigation_bar = bool(re.search(r'navigationOptions|headerStyle|cardStyle', content))
502
- has_header_title = bool(re.search(r'title:\s*["\']|headerTitle|navigation\.setOptions', content))
503
- if has_navigation_bar and not has_header_title:
504
- self.warnings.append(f"[iOS] {filename}: Navigation bar detected without title. iOS apps should have clear context in nav bar.")
505
-
506
- # 11.5 iOS Component Patterns Check
507
- # Check for iOS-specific components
508
- has_alert = bool(re.search(r'Alert\.alert|showAlert', content))
509
- has_action_sheet = bool(re.search(r'ActionSheet|ActionSheetIOS|showActionSheetWithOptions', content))
510
- has_activity_indicator = bool(re.search(r'ActivityIndicator|ActivityIndic', content))
511
-
512
- if has_alert or has_action_sheet or has_activity_indicator:
513
- self.passed_count += 1 # Good iOS component usage
514
-
515
- # --- 12. EXTENDED PLATFORM ANDROID CHECKS ---
516
-
517
- if is_react_native:
518
- # 12.1 Roboto Font Detection
519
- has_roboto = bool(re.search(r'Roboto|fontFamily:\s*["\']?[-\s]*Roboto', content))
520
- has_custom_font = bool(re.search(r'fontFamily:\s*["\'][^"\']+', content))
521
- if has_custom_font and not has_roboto:
522
- self.warnings.append(f"[Android] {filename}: Custom font without Roboto fallback. Roboto is optimized for Android displays.")
523
-
524
- # 12.2 Material 3 Dynamic Color Check
525
- has_material_colors = bool(re.search(r'MD3|MaterialYou|dynamicColor|useColorScheme', content))
526
- has_theme_provider = bool(re.search(r'MaterialTheme|ThemeProvider|PaperProvider|ThemeProvider', content))
527
- if not has_material_colors and not has_theme_provider:
528
- self.warnings.append(f"[Android] {filename}: No Material 3 dynamic color detected. Consider Material 3 theming for personalized feel.")
529
-
530
- # 12.3 Material Elevation Check
531
- # Check for elevation values (Material 3 uses elevation for depth)
532
- has_elevation = bool(re.search(r'elevation:\s*\d+|shadowOpacity|shadowRadius|android:elevation', content))
533
- has_box_shadow = bool(re.search(r'boxShadow:', content))
534
- if has_box_shadow and not has_elevation:
535
- self.warnings.append(f"[Android] {filename}: CSS box-shadow detected without elevation. Consider Material elevation system for consistent depth.")
536
-
537
- # 12.4 Material Component Patterns Check
538
- # Check for Material components
539
- has_ripple = bool(re.search(r'ripple|android_ripple|foregroundRipple', content))
540
- has_card = bool(re.search(r'Card|Paper|elevation.*\d+', content))
541
- has_fab = bool(re.search(r'FAB|FloatingActionButton|fab', content))
542
- has_snackbar = bool(re.search(r'Snackbar|showSnackBar|Toast', content))
543
-
544
- material_component_count = sum([has_ripple, has_card, has_fab, has_snackbar])
545
- if material_component_count >= 2:
546
- self.passed_count += 1 # Good Material design usage
547
-
548
- # 12.5 Android Navigation Patterns Check
549
- has_top_app_bar = bool(re.search(r'TopAppBar|AppBar|CollapsingToolbar', content))
550
- has_bottom_nav = bool(re.search(r'BottomNavigation|BottomNav', content))
551
- has_navigation_rail = bool(re.search(r'NavigationRail', content))
552
-
553
- if has_bottom_nav:
554
- self.passed_count += 1 # Good Android pattern
555
- elif has_top_app_bar and not (has_bottom_nav or has_navigation_rail):
556
- self.warnings.append(f"[Android] {filename}: TopAppBar without bottom navigation. Consider BottomNavigation for thumb-friendly access.")
557
-
558
- # --- 13. MOBILE TESTING CHECKS ---
559
-
560
- # 13.1 Testing Tool Detection
561
- has_rntl = bool(re.search(r'react-native-testing-library|@testing-library', content))
562
- has_detox = bool(re.search(r'detox|element\(|by\.text|by\.id', content))
563
- has_maestro = bool(re.search(r'maestro|\.yaml$', content))
564
- has_jest = bool(re.search(r'jest|describe\(|test\(|it\(', content))
565
-
566
- testing_tools = []
567
- if has_jest: testing_tools.append('Jest')
568
- if has_rntl: testing_tools.append('RNTL')
569
- if has_detox: testing_tools.append('Detox')
570
- if has_maestro: testing_tools.append('Maestro')
571
-
572
- if len(testing_tools) == 0:
573
- self.warnings.append(f"[Testing] {filename}: No testing framework detected. Consider Jest (unit) + Detox/Maestro (E2E) for mobile.")
574
-
575
- # 13.2 Test Pyramid Balance Check
576
- test_files = len(re.findall(r'\.test\.(tsx|ts|js|jsx)|\.spec\.', content))
577
- e2e_tests = len(re.findall(r'detox|maestro|e2e|spec\.e2e', content.lower()))
578
-
579
- if test_files > 0 and e2e_tests == 0:
580
- self.warnings.append(f"[Testing] {filename}: Unit tests found but no E2E tests. Mobile needs E2E on real devices for complete coverage.")
581
-
582
- # 13.3 Accessibility Label Check (Mobile-specific)
583
- if is_react_native:
584
- has_pressable = bool(re.search(r'Pressable|TouchableOpacity|TouchableHighlight', content))
585
- has_a11y_label = bool(re.search(r'accessibilityLabel|aria-label|testID', content))
586
- if has_pressable and not has_a11y_label:
587
- self.warnings.append(f"[A11y Mobile] {filename}: Touchable element without accessibilityLabel. Screen readers need labels for all interactive elements.")
588
-
589
- # --- 14. MOBILE DEBUGGING CHECKS ---
590
-
591
- # 14.1 Performance Profiling Check
592
- has_performance = bool(re.search(r'Performance|systrace|profile|Flipper', content))
593
- has_console_log = len(re.findall(r'console\.(log|warn|error|debug|info)', content))
594
- has_debugger = bool(re.search(r'debugger|__DEV__|React\.DevTools', content))
595
-
596
- if has_console_log > 10:
597
- self.warnings.append(f"[Debugging] {filename}: {has_console_log} console.log statements. Remove before production; they block JS thread.")
598
-
599
- if has_performance:
600
- self.passed_count += 1 # Good performance monitoring
601
-
602
- # 14.2 Error Boundary Check
603
- has_error_boundary = bool(re.search(r'ErrorBoundary|componentDidCatch|getDerivedStateFromError', content))
604
- if not has_error_boundary and is_react_native:
605
- self.warnings.append(f"[Debugging] {filename}: No ErrorBoundary detected. Consider adding ErrorBoundary to prevent app crashes.")
606
-
607
- # 14.3 Hermes Check (React Native specific)
608
- if is_react_native:
609
- # Check if using Hermes engine (should be default in modern RN)
610
- # This is more of a configuration check, not code pattern
611
- self.passed_count += 1 # Hermes is default in RN 0.70+
612
-
613
- def audit_directory(self, directory: str) -> None:
614
- extensions = {'.tsx', '.ts', '.jsx', '.js', '.dart'}
615
- for root, dirs, files in os.walk(directory):
616
- dirs[:] = [d for d in dirs if d not in {'node_modules', '.git', 'dist', 'build', '.next', 'ios', 'android', 'build', '.idea'}]
617
- for file in files:
618
- if Path(file).suffix in extensions:
619
- self.audit_file(os.path.join(root, file))
620
-
621
- def get_report(self):
622
- return {
623
- "files_checked": self.files_checked,
624
- "issues": self.issues,
625
- "warnings": self.warnings,
626
- "passed_checks": self.passed_count,
627
- "compliant": len(self.issues) == 0
628
- }
629
-
630
-
631
- def main():
632
- if len(sys.argv) < 2:
633
- print("Usage: python mobile_audit.py <directory>")
634
- sys.exit(1)
635
-
636
- path = sys.argv[1]
637
- is_json = "--json" in sys.argv
638
-
639
- auditor = MobileAuditor()
640
- if os.path.isfile(path):
641
- auditor.audit_file(path)
642
- else:
643
- auditor.audit_directory(path)
644
-
645
- report = auditor.get_report()
646
-
647
- if is_json:
648
- print(json.dumps(report, indent=2))
649
- else:
650
- print(f"\n[MOBILE AUDIT] {report['files_checked']} mobile files checked")
651
- print("-" * 50)
652
- if report['issues']:
653
- print(f"[!] ISSUES ({len(report['issues'])}):")
654
- for i in report['issues'][:10]:
655
- print(f" - {i}")
656
- if report['warnings']:
657
- print(f"[*] WARNINGS ({len(report['warnings'])}):")
658
- for w in report['warnings'][:15]:
659
- print(f" - {w}")
660
- print(f"[+] PASSED CHECKS: {report['passed_checks']}")
661
- status = "PASS" if report['compliant'] else "FAIL"
662
- print(f"STATUS: {status}")
663
-
664
- sys.exit(0 if report['compliant'] else 1)
665
-
666
-
667
- if __name__ == "__main__":
668
- # Fix missing import
669
- import re
670
- main()
1
+ #!/usr/bin/env python3
2
+ """
3
+ Mobile UX Audit Script - Full Mobile Design Coverage
4
+
5
+ Analyzes React Native / Flutter code for compliance with:
6
+
7
+ 1. TOUCH PSYCHOLOGY (touch-psychology.md):
8
+ - Touch Target Sizes (44pt iOS, 48dp Android, 44px WCAG)
9
+ - Touch Target Spacing (8px minimum gap)
10
+ - Thumb Zone Placement (primary CTAs at bottom)
11
+ - Gesture Alternatives (visible buttons for swipe)
12
+ - Haptic Feedback Patterns
13
+ - Touch Feedback Timing (<50ms)
14
+ - Touch Accessibility (motor impairment support)
15
+
16
+ 2. MOBILE PERFORMANCE (mobile-performance.md):
17
+ - ScrollView vs FlatList (CRITICAL)
18
+ - React.memo for List Items
19
+ - useCallback for renderItem
20
+ - Stable keyExtractor (NOT index)
21
+ - useNativeDriver for Animations
22
+ - Memory Leak Prevention (cleanup)
23
+ - Console.log Detection
24
+ - Inline Function Detection
25
+ - Animation Performance (transform/opacity only)
26
+
27
+ 3. MOBILE NAVIGATION (mobile-navigation.md):
28
+ - Tab Bar Max Items (5)
29
+ - Tab State Preservation
30
+ - Proper Back Handling
31
+ - Deep Link Support
32
+ - Navigation Structure
33
+
34
+ 4. MOBILE TYPOGRAPHY (mobile-typography.md):
35
+ - System Font Usage
36
+ - Dynamic Type Support (iOS)
37
+ - Text Scaling Constraints
38
+ - Mobile Line Height
39
+ - Font Size Limits
40
+
41
+ 5. MOBILE COLOR SYSTEM (mobile-color-system.md):
42
+ - Pure Black Avoidance (#000000)
43
+ - OLED Optimization
44
+ - Dark Mode Support
45
+ - Contrast Ratios
46
+
47
+ 6. PLATFORM iOS (platform-ios.md):
48
+ - SF Symbols Usage
49
+ - iOS Navigation Patterns
50
+ - iOS Haptic Types
51
+ - iOS-Specific Components
52
+
53
+ 7. PLATFORM ANDROID (platform-android.md):
54
+ - Material Icons Usage
55
+ - Android Navigation Patterns
56
+ - Ripple Effects
57
+ - Android-Specific Components
58
+
59
+ 8. MOBILE BACKEND (mobile-backend.md):
60
+ - Secure Storage (NOT AsyncStorage)
61
+ - Offline Handling
62
+ - Push Notification Support
63
+ - API Response Caching
64
+
65
+ Total: 50+ mobile-specific checks
66
+ """
67
+
68
+ import sys
69
+ import os
70
+ import re
71
+ import json
72
+ from pathlib import Path
73
+
74
+ class MobileAuditor:
75
+ def __init__(self):
76
+ self.issues = []
77
+ self.warnings = []
78
+ self.passed_count = 0
79
+ self.files_checked = 0
80
+
81
+ def audit_file(self, filepath: str) -> None:
82
+ try:
83
+ with open(filepath, 'r', encoding='utf-8', errors='replace') as f:
84
+ content = f.read()
85
+ except:
86
+ return
87
+
88
+ self.files_checked += 1
89
+ filename = os.path.basename(filepath)
90
+
91
+ # Detect framework
92
+ is_react_native = bool(re.search(r'react-native|@react-navigation|React\.Native', content))
93
+ is_flutter = bool(re.search(r'import \'package:flutter|MaterialApp|Widget\.build', content))
94
+
95
+ if not (is_react_native or is_flutter):
96
+ return # Skip non-mobile files
97
+
98
+ # --- 1. TOUCH PSYCHOLOGY CHECKS ---
99
+
100
+ # 1.1 Touch Target Size Check
101
+ # Look for small touch targets
102
+ small_sizes = re.findall(r'(?:width|height|size):\s*([0-3]\d)', content)
103
+ for size in small_sizes:
104
+ if int(size) < 44:
105
+ self.issues.append(f"[Touch Target] {filename}: Touch target size {size}px < 44px minimum (iOS: 44pt, Android: 48dp)")
106
+
107
+ # 1.2 Touch Target Spacing Check
108
+ # Look for inadequate spacing between touchable elements
109
+ small_gaps = re.findall(r'(?:margin|gap):\s*([0-7])\s*(?:px|dp)', content)
110
+ for gap in small_gaps:
111
+ if int(gap) < 8:
112
+ self.warnings.append(f"[Touch Spacing] {filename}: Touch target spacing {gap}px < 8px minimum. Accidental taps risk.")
113
+
114
+ # 1.3 Thumb Zone Placement Check
115
+ # Primary CTAs should be at bottom (easy thumb reach)
116
+ primary_buttons = re.findall(r'(?:testID|id):\s*["\'](?:.*(?:primary|cta|submit|confirm)[^"\']*)["\']', content, re.IGNORECASE)
117
+ has_bottom_placement = bool(re.search(r'position:\s*["\']?absolute["\']?|bottom:\s*\d+|style.*bottom|justifyContent:\s*["\']?flex-end', content))
118
+ if primary_buttons and not has_bottom_placement:
119
+ self.warnings.append(f"[Thumb Zone] {filename}: Primary CTA may not be in thumb zone (bottom). Place primary actions at bottom for easy reach.")
120
+
121
+ # 1.4 Gesture Alternatives Check
122
+ # Swipe actions should have visible button alternatives
123
+ has_swipe_gestures = bool(re.search(r'Swipeable|onSwipe|PanGestureHandler|swipe', content))
124
+ has_visible_buttons = bool(re.search(r'Button.*(?:delete|archive|more)|TouchableOpacity|Pressable', content))
125
+ if has_swipe_gestures and not has_visible_buttons:
126
+ self.warnings.append(f"[Gestures] {filename}: Swipe gestures detected without visible button alternatives. Motor impaired users need alternatives.")
127
+
128
+ # 1.5 Haptic Feedback Check
129
+ # Important actions should have haptic feedback
130
+ has_important_actions = bool(re.search(r'(?:onPress|onSubmit|delete|remove|confirm|purchase)', content))
131
+ has_haptics = bool(re.search(r'Haptics|Vibration|react-native-haptic-feedback|FeedbackManager', content))
132
+ if has_important_actions and not has_haptics:
133
+ self.warnings.append(f"[Haptics] {filename}: Important actions without haptic feedback. Consider adding haptic confirmation.")
134
+
135
+ # 1.6 Touch Feedback Timing Check
136
+ # Touch feedback should be immediate (<50ms)
137
+ if is_react_native:
138
+ has_pressable = bool(re.search(r'Pressable|TouchableOpacity', content))
139
+ has_feedback_state = bool(re.search(r'pressed|style.*opacity|underlay', content))
140
+ if has_pressable and not has_feedback_state:
141
+ self.warnings.append(f"[Touch Feedback] {filename}: Pressable without visual feedback state. Add opacity/scale change for tap confirmation.")
142
+
143
+ # --- 2. MOBILE PERFORMANCE CHECKS ---
144
+
145
+ # 2.1 CRITICAL: ScrollView vs FlatList
146
+ has_scrollview = bool(re.search(r'<ScrollView|ScrollView\.', content))
147
+ has_map_in_scrollview = bool(re.search(r'ScrollView.*\.map\(|ScrollView.*\{.*\.map', content))
148
+ if has_scrollview and has_map_in_scrollview:
149
+ self.issues.append(f"[Performance CRITICAL] {filename}: ScrollView with .map() detected. Use FlatList for lists to prevent memory explosion.")
150
+
151
+ # 2.2 React.memo Check
152
+ if is_react_native:
153
+ has_list = bool(re.search(r'FlatList|FlashList|SectionList', content))
154
+ has_react_memo = bool(re.search(r'React\.memo|memo\(', content))
155
+ if has_list and not has_react_memo:
156
+ self.warnings.append(f"[Performance] {filename}: FlatList without React.memo on list items. Items will re-render on every parent update.")
157
+
158
+ # 2.3 useCallback Check
159
+ if is_react_native:
160
+ has_flatlist = bool(re.search(r'FlatList|FlashList', content))
161
+ has_use_callback = bool(re.search(r'useCallback', content))
162
+ if has_flatlist and not has_use_callback:
163
+ self.warnings.append(f"[Performance] {filename}: FlatList renderItem without useCallback. New function created every render.")
164
+
165
+ # 2.4 keyExtractor Check (CRITICAL)
166
+ if is_react_native:
167
+ has_flatlist = bool(re.search(r'FlatList', content))
168
+ has_key_extractor = bool(re.search(r'keyExtractor', content))
169
+ uses_index_key = bool(re.search(r'key=\{.*index.*\}|key:\s*index', content))
170
+ if has_flatlist and not has_key_extractor:
171
+ self.issues.append(f"[Performance CRITICAL] {filename}: FlatList without keyExtractor. Index-based keys cause bugs on reorder/delete.")
172
+ if uses_index_key:
173
+ self.issues.append(f"[Performance CRITICAL] {filename}: Using index as key. This causes bugs when list changes. Use unique ID from data.")
174
+
175
+ # 2.5 useNativeDriver Check
176
+ if is_react_native:
177
+ has_animated = bool(re.search(r'Animated\.', content))
178
+ has_native_driver = bool(re.search(r'useNativeDriver:\s*true', content))
179
+ has_native_driver_false = bool(re.search(r'useNativeDriver:\s*false', content))
180
+ if has_animated and has_native_driver_false:
181
+ self.warnings.append(f"[Performance] {filename}: Animation with useNativeDriver: false. Use true for 60fps (only supports transform/opacity).")
182
+ if has_animated and not has_native_driver:
183
+ self.warnings.append(f"[Performance] {filename}: Animated component without useNativeDriver. Add useNativeDriver: true for 60fps.")
184
+
185
+ # 2.6 Memory Leak Check
186
+ if is_react_native:
187
+ has_effect = bool(re.search(r'useEffect', content))
188
+ has_cleanup = bool(re.search(r'return\s*\(\)\s*=>|return\s+function', content))
189
+ has_subscriptions = bool(re.search(r'addEventListener|subscribe|\.focus\(\)|\.off\(', content))
190
+ if has_effect and has_subscriptions and not has_cleanup:
191
+ self.issues.append(f"[Memory Leak] {filename}: useEffect with subscriptions but no cleanup function. Memory leak on unmount.")
192
+
193
+ # 2.7 Console.log Detection
194
+ console_logs = len(re.findall(r'console\.log|console\.warn|console\.error|console\.debug', content))
195
+ if console_logs > 5:
196
+ self.warnings.append(f"[Performance] {filename}: {console_logs} console.log statements detected. Remove before production (blocks JS thread).")
197
+
198
+ # 2.8 Inline Function Detection
199
+ if is_react_native:
200
+ inline_functions = re.findall(r'(?:onPress|onPressIn|onPressOut|renderItem):\s*\([^)]*\)\s*=>', content)
201
+ if len(inline_functions) > 3:
202
+ self.warnings.append(f"[Performance] {filename}: {len(inline_functions)} inline arrow functions in props. Creates new function every render. Use useCallback.")
203
+
204
+ # 2.9 Animation Properties Check
205
+ # Warn if animating expensive properties
206
+ animating_layout = bool(re.search(r'Animated\.timing.*(?:width|height|margin|padding)', content))
207
+ if animating_layout:
208
+ self.issues.append(f"[Performance] {filename}: Animating layout properties (width/height/margin). Use transform/opacity for 60fps.")
209
+
210
+ # --- 3. MOBILE NAVIGATION CHECKS ---
211
+
212
+ # 3.1 Tab Bar Max Items Check
213
+ tab_bar_items = len(re.findall(r'Tab\.Screen|createBottomTabNavigator|BottomTab', content))
214
+ if tab_bar_items > 5:
215
+ self.warnings.append(f"[Navigation] {filename}: {tab_bar_items} tab bar items (max 5 recommended). More than 5 becomes hard to tap.")
216
+
217
+ # 3.2 Tab State Preservation Check
218
+ has_tab_nav = bool(re.search(r'createBottomTabNavigator|Tab\.Navigator', content))
219
+ if has_tab_nav:
220
+ # Look for lazy prop (false preserves state)
221
+ has_lazy_false = bool(re.search(r'lazy:\s*false', content))
222
+ if not has_lazy_false:
223
+ self.warnings.append(f"[Navigation] {filename}: Tab navigation without lazy: false. Tabs may lose state on switch.")
224
+
225
+ # 3.3 Back Handling Check
226
+ has_back_listener = bool(re.search(r'BackHandler|useFocusEffect|navigation\.addListener', content))
227
+ has_custom_back = bool(re.search(r'onBackPress|handleBackPress', content))
228
+ if has_custom_back and not has_back_listener:
229
+ self.warnings.append(f"[Navigation] {filename}: Custom back handling without BackHandler listener. May not work correctly.")
230
+
231
+ # 3.4 Deep Link Support Check
232
+ has_linking = bool(re.search(r'Linking\.|Linking\.openURL|deepLink|universalLink', content))
233
+ has_config = bool(re.search(r'apollo-link|react-native-screens|navigation\.link', content))
234
+ if not has_linking and not has_config:
235
+ self.passed_count += 1
236
+ else:
237
+ if has_linking and not has_config:
238
+ self.warnings.append(f"[Navigation] {filename}: Deep linking detected but may lack proper configuration. Test notification/share flows.")
239
+
240
+ # --- 4. MOBILE TYPOGRAPHY CHECKS ---
241
+
242
+ # 4.1 System Font Check
243
+ if is_react_native:
244
+ has_custom_font = bool(re.search(r"fontFamily:\s*[\"'][^\"']+", content))
245
+ has_system_font = bool(re.search(r"fontFamily:\s*[\"']?(?:System|San Francisco|Roboto|-apple-system)", content))
246
+ if has_custom_font and not has_system_font:
247
+ self.warnings.append(f"[Typography] {filename}: Custom font detected. Consider system fonts (iOS: SF Pro, Android: Roboto) for native feel.")
248
+
249
+ # 4.2 Text Scaling Check (iOS Dynamic Type)
250
+ if is_react_native:
251
+ has_font_sizes = bool(re.search(r'fontSize:', content))
252
+ has_scaling = bool(re.search(r'allowFontScaling:\s*true|responsiveFontSize|useWindowDimensions', content))
253
+ if has_font_sizes and not has_scaling:
254
+ self.warnings.append(f"[Typography] {filename}: Fixed font sizes without scaling support. Consider allowFontScaling for accessibility.")
255
+
256
+ # 4.3 Mobile Line Height Check
257
+ line_heights = re.findall(r'lineHeight:\s*([\d.]+)', content)
258
+ for lh in line_heights:
259
+ if float(lh) > 1.8:
260
+ self.warnings.append(f"[Typography] {filename}: lineHeight {lh} too high for mobile. Mobile text needs tighter spacing (1.3-1.5).")
261
+
262
+ # 4.4 Font Size Limits
263
+ font_sizes = re.findall(r'fontSize:\s*([\d.]+)', content)
264
+ for fs in font_sizes:
265
+ size = float(fs)
266
+ if size < 12:
267
+ self.warnings.append(f"[Typography] {filename}: fontSize {size}px below 12px minimum readability.")
268
+ elif size > 32:
269
+ self.warnings.append(f"[Typography] {filename}: fontSize {size}px very large. Consider using responsive scaling.")
270
+
271
+ # --- 5. MOBILE COLOR SYSTEM CHECKS ---
272
+
273
+ # 5.1 Pure Black Avoidance
274
+ if re.search(r'#000000|color:\s*black|backgroundColor:\s*["\']?black', content):
275
+ self.warnings.append(f"[Color] {filename}: Pure black (#000000) detected. Use dark gray (#1C1C1E iOS, #121212 Android) for better OLED/battery.")
276
+
277
+ # 5.2 Dark Mode Support
278
+ has_color_schemes = bool(re.search(r'useColorScheme|colorScheme|appearance:\s*["\']?dark', content))
279
+ has_dark_mode_style = bool(re.search(r'\\\?.*dark|style:\s*.*dark|isDark', content))
280
+ if not has_color_schemes and not has_dark_mode_style:
281
+ self.warnings.append(f"[Color] {filename}: No dark mode support detected. Consider useColorScheme for system dark mode.")
282
+
283
+ # --- 6. PLATFORM iOS CHECKS ---
284
+
285
+ if is_react_native:
286
+ # 6.1 SF Symbols Check
287
+ has_ios_icons = bool(re.search(r'@expo/vector-icons|ionicons', content))
288
+ has_sf_symbols = bool(re.search(r'sf-symbol|SF Symbols', content))
289
+ if has_ios_icons and not has_sf_symbols:
290
+ self.passed_count += 1
291
+
292
+ # 6.2 iOS Haptic Types
293
+ has_haptic_import = bool(re.search(r'expo-haptics|react-native-haptic-feedback', content))
294
+ has_haptic_types = bool(re.search(r'ImpactFeedback|NotificationFeedback|SelectionFeedback', content))
295
+ if has_haptic_import and not has_haptic_types:
296
+ self.warnings.append(f"[iOS Haptics] {filename}: Haptic library imported but not using typed haptics (Impact/Notification/Selection).")
297
+
298
+ # 6.3 iOS Safe Area
299
+ has_safe_area = bool(re.search(r'SafeAreaView|useSafeAreaInsets|safeArea', content))
300
+ if not has_safe_area:
301
+ self.warnings.append(f"[iOS] {filename}: No SafeArea detected. Content may be hidden by notch/home indicator.")
302
+
303
+ # --- 7. PLATFORM ANDROID CHECKS ---
304
+
305
+ if is_react_native:
306
+ # 7.1 Material Icons Check
307
+ has_material_icons = bool(re.search(r'@expo/vector-icons|MaterialIcons', content))
308
+ if has_material_icons:
309
+ self.passed_count += 1
310
+
311
+ # 7.2 Ripple Effect
312
+ has_ripple = bool(re.search(r'ripple|android_ripple|foregroundRipple', content))
313
+ has_pressable = bool(re.search(r'Pressable|Touchable', content))
314
+ if has_pressable and not has_ripple:
315
+ self.warnings.append(f"[Android] {filename}: Touchable without ripple effect. Android users expect ripple feedback.")
316
+
317
+ # 7.3 Hardware Back Button
318
+ if is_react_native:
319
+ has_back_button = bool(re.search(r'BackHandler|useBackHandler', content))
320
+ has_navigation = bool(re.search(r'@react-navigation', content))
321
+ if has_navigation and not has_back_button:
322
+ self.warnings.append(f"[Android] {filename}: React Navigation detected without BackHandler listener. Android hardware back may not work correctly.")
323
+
324
+ # --- 8. MOBILE BACKEND CHECKS ---
325
+
326
+ # 8.1 Secure Storage Check
327
+ has_async_storage = bool(re.search(r'AsyncStorage|@react-native-async-storage', content))
328
+ has_secure_storage = bool(re.search(r'SecureStore|Keychain|EncryptedSharedPreferences', content))
329
+ has_token_storage = bool(re.search(r'token|jwt|auth.*storage', content, re.IGNORECASE))
330
+ if has_token_storage and has_async_storage and not has_secure_storage:
331
+ self.issues.append(f"[Security] {filename}: Storing auth tokens in AsyncStorage (insecure). Use SecureStore (iOS) / EncryptedSharedPreferences (Android).")
332
+
333
+ # 8.2 Offline Handling Check
334
+ has_network = bool(re.search(r'fetch|axios|netinfo|@react-native-community/netinfo', content))
335
+ has_offline = bool(re.search(r'offline|isConnected|netInfo|cache.*offline', content))
336
+ if has_network and not has_offline:
337
+ self.warnings.append(f"[Offline] {filename}: Network requests detected without offline handling. Consider NetInfo for connection status.")
338
+
339
+ # 8.3 Push Notification Support
340
+ has_push = bool(re.search(r'Notifications|pushNotification|Firebase\.messaging|PushNotificationIOS', content))
341
+ has_push_handler = bool(re.search(r'onNotification|addNotificationListener|notification\.open', content))
342
+ if has_push and not has_push_handler:
343
+ self.warnings.append(f"[Push] {filename}: Push notifications imported but no handler found. May miss notifications.")
344
+
345
+ # --- 9. EXTENDED MOBILE TYPOGRAPHY CHECKS ---
346
+
347
+ # 9.1 iOS Type Scale Check
348
+ if is_react_native:
349
+ # Check for iOS text styles that match HIG
350
+ has_large_title = bool(re.search(r'fontSize:\s*34|largeTitle|font-weight:\s*["\']?bold', content))
351
+ has_title_1 = bool(re.search(r'fontSize:\s*28', content))
352
+ has_headline = bool(re.search(r'fontSize:\s*17.*semibold|headline', content))
353
+ has_body = bool(re.search(r'fontSize:\s*17.*regular|body', content))
354
+
355
+ # Check if following iOS scale roughly
356
+ font_sizes = re.findall(r'fontSize:\s*([\d.]+)', content)
357
+ ios_scale_sizes = [34, 28, 22, 20, 17, 16, 15, 13, 12, 11]
358
+ matching_ios = sum(1 for size in font_sizes if any(abs(float(size) - ios_size) < 1 for ios_size in ios_scale_sizes))
359
+
360
+ if len(font_sizes) > 3 and matching_ios < len(font_sizes) / 2:
361
+ self.warnings.append(f"[iOS Typography] {filename}: Font sizes don't match iOS type scale. Consider iOS text styles for native feel.")
362
+
363
+ # 9.2 Android Material Type Scale Check
364
+ if is_react_native:
365
+ # Check for Material 3 text styles
366
+ has_display = bool(re.search(r'fontSize:\s*[456][0-9]|display', content))
367
+ has_headline_material = bool(re.search(r'fontSize:\s*[23][0-9]|headline', content))
368
+ has_title_material = bool(re.search(r'fontSize:\s*2[12][0-9].*medium|title', content))
369
+ has_body_material = bool(re.search(r'fontSize:\s*1[456].*regular|body', content))
370
+ has_label = bool(re.search(r'fontSize:\s*1[1234].*medium|label', content))
371
+
372
+ # Check if using sp (scale-independent pixels)
373
+ uses_sp = bool(re.search(r'\d+\s*sp\b', content))
374
+ if has_display or has_headline_material:
375
+ if not uses_sp:
376
+ self.warnings.append(f"[Android Typography] {filename}: Material typography detected without sp units. Use sp for text to respect user font size preferences.")
377
+
378
+ # 9.3 Modular Scale Check
379
+ # Check if font sizes follow modular scale
380
+ font_sizes = re.findall(r'fontSize:\s*(\d+(?:\.\d+)?)', content)
381
+ if len(font_sizes) > 3:
382
+ sorted_sizes = sorted(set([float(s) for s in font_sizes]))
383
+ ratios = []
384
+ for i in range(1, len(sorted_sizes)):
385
+ if sorted_sizes[i-1] > 0:
386
+ ratios.append(sorted_sizes[i] / sorted_sizes[i-1])
387
+
388
+ # Common ratios: 1.125, 1.2, 1.25, 1.333, 1.5
389
+ common_ratios = {1.125, 1.2, 1.25, 1.333, 1.5}
390
+ for ratio in ratios[:3]:
391
+ if not any(abs(ratio - cr) < 0.03 for cr in common_ratios):
392
+ self.warnings.append(f"[Typography] {filename}: Font sizes may not follow modular scale (ratio: {ratio:.2f}). Consider consistent ratio.")
393
+ break
394
+
395
+ # 9.4 Line Length Check (Mobile-specific)
396
+ # Mobile text should be 40-60 characters max
397
+ if is_react_native:
398
+ has_long_text = bool(re.search(r'<Text[^>]*>[^<]{40,}', content))
399
+ has_max_width = bool(re.search(r'maxWidth|max-w-\d+|width:\s*["\']?\d+', content))
400
+ if has_long_text and not has_max_width:
401
+ self.warnings.append(f"[Mobile Typography] {filename}: Text without max-width constraint. Mobile text should be 40-60 characters per line for readability.")
402
+
403
+ # 9.5 Font Weight Pattern Check
404
+ # Check for font weight distribution
405
+ if is_react_native:
406
+ font_weights = re.findall(r'fontWeight:\s*["\']?(\d+|normal|bold|medium|light)', content)
407
+ weight_map = {'normal': '400', 'light': '300', 'medium': '500', 'bold': '700'}
408
+ numeric_weights = []
409
+ for w in font_weights:
410
+ val = weight_map.get(w.lower(), w)
411
+ try:
412
+ numeric_weights.append(int(val))
413
+ except:
414
+ pass
415
+
416
+ # Check if overusing bold (mobile should be regular-dominant)
417
+ bold_count = sum(1 for w in numeric_weights if w >= 700)
418
+ regular_count = sum(1 for w in numeric_weights if 400 <= w < 500)
419
+ if bold_count > regular_count:
420
+ self.warnings.append(f"[Mobile Typography] {filename}: More bold weights than regular. Mobile typography should be regular-dominant for readability.")
421
+
422
+ # --- 10. EXTENDED MOBILE COLOR SYSTEM CHECKS ---
423
+
424
+ # 10.1 OLED Optimization Check
425
+ # Check for near-black colors instead of pure black
426
+ if re.search(r'#121212|#1A1A1A|#0D0D0D', content):
427
+ self.passed_count += 1 # Good OLED optimization
428
+ elif re.search(r'backgroundColor:\s*["\']?#000000', content):
429
+ # Using pure black for background is OK for OLED
430
+ pass
431
+ elif re.search(r'backgroundColor:\s*["\']?#[0-9A-Fa-f]{6}', content):
432
+ # Check if using light colors in dark mode (bad for OLED)
433
+ self.warnings.append(f"[Mobile Color] {filename}: Consider OLED-optimized dark backgrounds (#121212 Android, #000000 iOS) for battery savings.")
434
+
435
+ # 10.2 Saturated Color Detection (Battery)
436
+ # Highly saturated colors consume more power on OLED
437
+ hex_colors = re.findall(r'#([0-9A-Fa-f]{2})([0-9A-Fa-f]{2})([0-9A-Fa-f]{2})', content)
438
+ saturated_count = 0
439
+ for r, g, b in hex_colors:
440
+ # Convert to RGB 0-255
441
+ try:
442
+ r_val, g_val, b_val = int(r, 16), int(g, 16), int(b, 16)
443
+ max_val = max(r_val, g_val, b_val)
444
+ min_val = min(r_val, g_val, b_val)
445
+ # Saturation = (max - min) / max
446
+ if max_val > 0:
447
+ saturation = (max_val - min_val) / max_val
448
+ if saturation > 0.8: # Highly saturated
449
+ saturated_count += 1
450
+ except:
451
+ pass
452
+
453
+ if saturated_count > 10:
454
+ self.warnings.append(f"[Mobile Color] {filename}: {saturated_count} highly saturated colors detected. Desaturated colors save battery on OLED screens.")
455
+
456
+ # 10.3 Outdoor Visibility Check
457
+ # Low contrast combinations fail in outdoor sunlight
458
+ light_colors = re.findall(r'#[0-9A-Fa-f]{6}|rgba?\([^)]+\)', content)
459
+ # Check for potential low contrast (light gray on white, dark gray on black)
460
+ potential_low_contrast = bool(re.search(r'#[EeEeEeEe].*#ffffff|#999999.*#ffffff|#333333.*#000000|#666666.*#000000', content))
461
+ if potential_low_contrast:
462
+ self.warnings.append(f"[Mobile Color] {filename}: Possible low contrast combination detected. Critical for outdoor visibility. Ensure WCAG AAA (7:1) for mobile.")
463
+
464
+ # 10.4 Dark Mode Text Color Check
465
+ # In dark mode, text should not be pure white
466
+ has_dark_mode = bool(re.search(r'dark:\s*|isDark|useColorScheme|colorScheme:\s*["\']?dark', content))
467
+ if has_dark_mode:
468
+ has_pure_white_text = bool(re.search(r'color:\s*["\']?#ffffff|#fff["\']?\}|textColor:\s*["\']?white', content))
469
+ if has_pure_white_text:
470
+ self.warnings.append(f"[Mobile Color] {filename}: Pure white text (#FFFFFF) in dark mode. Use #E8E8E8 or light gray for better readability.")
471
+
472
+ # --- 11. EXTENDED PLATFORM IOS CHECKS ---
473
+
474
+ if is_react_native:
475
+ # 11.1 SF Pro Font Detection
476
+ has_sf_pro = bool(re.search(r'SF Pro|SFPro|fontFamily:\s*["\']?[-\s]*SF', content))
477
+ has_custom_font = bool(re.search(r'fontFamily:\s*["\'][^"\']+', content))
478
+ if has_custom_font and not has_sf_pro:
479
+ self.warnings.append(f"[iOS] {filename}: Custom font without SF Pro fallback. Consider SF Pro Text for body, SF Pro Display for headings.")
480
+
481
+ # 11.2 iOS System Colors Check
482
+ # Check for semantic color usage
483
+ has_label = bool(re.search(r'color:\s*["\']?label|\.label', content))
484
+ has_secondaryLabel = bool(re.search(r'secondaryLabel|\.secondaryLabel', content))
485
+ has_systemBackground = bool(re.search(r'systemBackground|\.systemBackground', content))
486
+
487
+ has_hardcoded_gray = bool(re.search(r'#[78]0{4}', content))
488
+ if has_hardcoded_gray and not (has_label or has_secondaryLabel):
489
+ self.warnings.append(f"[iOS] {filename}: Hardcoded gray colors detected. Consider iOS semantic colors (label, secondaryLabel) for automatic dark mode.")
490
+
491
+ # 11.3 iOS Accent Colors Check
492
+ ios_blue = bool(re.search(r'#007AFF|#0A84FF|systemBlue', content))
493
+ ios_green = bool(re.search(r'#34C759|#30D158|systemGreen', content))
494
+ ios_red = bool(re.search(r'#FF3B30|#FF453A|systemRed', content))
495
+
496
+ has_custom_primary = bool(re.search(r'primaryColor|theme.*primary|colors\.primary', content))
497
+ if has_custom_primary and not (ios_blue or ios_green or ios_red):
498
+ self.warnings.append(f"[iOS] {filename}: Custom primary color without iOS system color fallback. Consider systemBlue for consistent iOS feel.")
499
+
500
+ # 11.4 iOS Navigation Patterns Check
501
+ has_navigation_bar = bool(re.search(r'navigationOptions|headerStyle|cardStyle', content))
502
+ has_header_title = bool(re.search(r'title:\s*["\']|headerTitle|navigation\.setOptions', content))
503
+ if has_navigation_bar and not has_header_title:
504
+ self.warnings.append(f"[iOS] {filename}: Navigation bar detected without title. iOS apps should have clear context in nav bar.")
505
+
506
+ # 11.5 iOS Component Patterns Check
507
+ # Check for iOS-specific components
508
+ has_alert = bool(re.search(r'Alert\.alert|showAlert', content))
509
+ has_action_sheet = bool(re.search(r'ActionSheet|ActionSheetIOS|showActionSheetWithOptions', content))
510
+ has_activity_indicator = bool(re.search(r'ActivityIndicator|ActivityIndic', content))
511
+
512
+ if has_alert or has_action_sheet or has_activity_indicator:
513
+ self.passed_count += 1 # Good iOS component usage
514
+
515
+ # --- 12. EXTENDED PLATFORM ANDROID CHECKS ---
516
+
517
+ if is_react_native:
518
+ # 12.1 Roboto Font Detection
519
+ has_roboto = bool(re.search(r'Roboto|fontFamily:\s*["\']?[-\s]*Roboto', content))
520
+ has_custom_font = bool(re.search(r'fontFamily:\s*["\'][^"\']+', content))
521
+ if has_custom_font and not has_roboto:
522
+ self.warnings.append(f"[Android] {filename}: Custom font without Roboto fallback. Roboto is optimized for Android displays.")
523
+
524
+ # 12.2 Material 3 Dynamic Color Check
525
+ has_material_colors = bool(re.search(r'MD3|MaterialYou|dynamicColor|useColorScheme', content))
526
+ has_theme_provider = bool(re.search(r'MaterialTheme|ThemeProvider|PaperProvider|ThemeProvider', content))
527
+ if not has_material_colors and not has_theme_provider:
528
+ self.warnings.append(f"[Android] {filename}: No Material 3 dynamic color detected. Consider Material 3 theming for personalized feel.")
529
+
530
+ # 12.3 Material Elevation Check
531
+ # Check for elevation values (Material 3 uses elevation for depth)
532
+ has_elevation = bool(re.search(r'elevation:\s*\d+|shadowOpacity|shadowRadius|android:elevation', content))
533
+ has_box_shadow = bool(re.search(r'boxShadow:', content))
534
+ if has_box_shadow and not has_elevation:
535
+ self.warnings.append(f"[Android] {filename}: CSS box-shadow detected without elevation. Consider Material elevation system for consistent depth.")
536
+
537
+ # 12.4 Material Component Patterns Check
538
+ # Check for Material components
539
+ has_ripple = bool(re.search(r'ripple|android_ripple|foregroundRipple', content))
540
+ has_card = bool(re.search(r'Card|Paper|elevation.*\d+', content))
541
+ has_fab = bool(re.search(r'FAB|FloatingActionButton|fab', content))
542
+ has_snackbar = bool(re.search(r'Snackbar|showSnackBar|Toast', content))
543
+
544
+ material_component_count = sum([has_ripple, has_card, has_fab, has_snackbar])
545
+ if material_component_count >= 2:
546
+ self.passed_count += 1 # Good Material design usage
547
+
548
+ # 12.5 Android Navigation Patterns Check
549
+ has_top_app_bar = bool(re.search(r'TopAppBar|AppBar|CollapsingToolbar', content))
550
+ has_bottom_nav = bool(re.search(r'BottomNavigation|BottomNav', content))
551
+ has_navigation_rail = bool(re.search(r'NavigationRail', content))
552
+
553
+ if has_bottom_nav:
554
+ self.passed_count += 1 # Good Android pattern
555
+ elif has_top_app_bar and not (has_bottom_nav or has_navigation_rail):
556
+ self.warnings.append(f"[Android] {filename}: TopAppBar without bottom navigation. Consider BottomNavigation for thumb-friendly access.")
557
+
558
+ # --- 13. MOBILE TESTING CHECKS ---
559
+
560
+ # 13.1 Testing Tool Detection
561
+ has_rntl = bool(re.search(r'react-native-testing-library|@testing-library', content))
562
+ has_detox = bool(re.search(r'detox|element\(|by\.text|by\.id', content))
563
+ has_maestro = bool(re.search(r'maestro|\.yaml$', content))
564
+ has_jest = bool(re.search(r'jest|describe\(|test\(|it\(', content))
565
+
566
+ testing_tools = []
567
+ if has_jest: testing_tools.append('Jest')
568
+ if has_rntl: testing_tools.append('RNTL')
569
+ if has_detox: testing_tools.append('Detox')
570
+ if has_maestro: testing_tools.append('Maestro')
571
+
572
+ if len(testing_tools) == 0:
573
+ self.warnings.append(f"[Testing] {filename}: No testing framework detected. Consider Jest (unit) + Detox/Maestro (E2E) for mobile.")
574
+
575
+ # 13.2 Test Pyramid Balance Check
576
+ test_files = len(re.findall(r'\.test\.(tsx|ts|js|jsx)|\.spec\.', content))
577
+ e2e_tests = len(re.findall(r'detox|maestro|e2e|spec\.e2e', content.lower()))
578
+
579
+ if test_files > 0 and e2e_tests == 0:
580
+ self.warnings.append(f"[Testing] {filename}: Unit tests found but no E2E tests. Mobile needs E2E on real devices for complete coverage.")
581
+
582
+ # 13.3 Accessibility Label Check (Mobile-specific)
583
+ if is_react_native:
584
+ has_pressable = bool(re.search(r'Pressable|TouchableOpacity|TouchableHighlight', content))
585
+ has_a11y_label = bool(re.search(r'accessibilityLabel|aria-label|testID', content))
586
+ if has_pressable and not has_a11y_label:
587
+ self.warnings.append(f"[A11y Mobile] {filename}: Touchable element without accessibilityLabel. Screen readers need labels for all interactive elements.")
588
+
589
+ # --- 14. MOBILE DEBUGGING CHECKS ---
590
+
591
+ # 14.1 Performance Profiling Check
592
+ has_performance = bool(re.search(r'Performance|systrace|profile|Flipper', content))
593
+ has_console_log = len(re.findall(r'console\.(log|warn|error|debug|info)', content))
594
+ has_debugger = bool(re.search(r'debugger|__DEV__|React\.DevTools', content))
595
+
596
+ if has_console_log > 10:
597
+ self.warnings.append(f"[Debugging] {filename}: {has_console_log} console.log statements. Remove before production; they block JS thread.")
598
+
599
+ if has_performance:
600
+ self.passed_count += 1 # Good performance monitoring
601
+
602
+ # 14.2 Error Boundary Check
603
+ has_error_boundary = bool(re.search(r'ErrorBoundary|componentDidCatch|getDerivedStateFromError', content))
604
+ if not has_error_boundary and is_react_native:
605
+ self.warnings.append(f"[Debugging] {filename}: No ErrorBoundary detected. Consider adding ErrorBoundary to prevent app crashes.")
606
+
607
+ # 14.3 Hermes Check (React Native specific)
608
+ if is_react_native:
609
+ # Check if using Hermes engine (should be default in modern RN)
610
+ # This is more of a configuration check, not code pattern
611
+ self.passed_count += 1 # Hermes is default in RN 0.70+
612
+
613
+ def audit_directory(self, directory: str) -> None:
614
+ extensions = {'.tsx', '.ts', '.jsx', '.js', '.dart'}
615
+ for root, dirs, files in os.walk(directory):
616
+ dirs[:] = [d for d in dirs if d not in {'node_modules', '.git', 'dist', 'build', '.next', 'ios', 'android', 'build', '.idea'}]
617
+ for file in files:
618
+ if Path(file).suffix in extensions:
619
+ self.audit_file(os.path.join(root, file))
620
+
621
+ def get_report(self):
622
+ return {
623
+ "files_checked": self.files_checked,
624
+ "issues": self.issues,
625
+ "warnings": self.warnings,
626
+ "passed_checks": self.passed_count,
627
+ "compliant": len(self.issues) == 0
628
+ }
629
+
630
+
631
+ def main():
632
+ if len(sys.argv) < 2:
633
+ print("Usage: python mobile_audit.py <directory>")
634
+ sys.exit(1)
635
+
636
+ path = sys.argv[1]
637
+ is_json = "--json" in sys.argv
638
+
639
+ auditor = MobileAuditor()
640
+ if os.path.isfile(path):
641
+ auditor.audit_file(path)
642
+ else:
643
+ auditor.audit_directory(path)
644
+
645
+ report = auditor.get_report()
646
+
647
+ if is_json:
648
+ print(json.dumps(report, indent=2))
649
+ else:
650
+ print(f"\n[MOBILE AUDIT] {report['files_checked']} mobile files checked")
651
+ print("-" * 50)
652
+ if report['issues']:
653
+ print(f"[!] ISSUES ({len(report['issues'])}):")
654
+ for i in report['issues'][:10]:
655
+ print(f" - {i}")
656
+ if report['warnings']:
657
+ print(f"[*] WARNINGS ({len(report['warnings'])}):")
658
+ for w in report['warnings'][:15]:
659
+ print(f" - {w}")
660
+ print(f"[+] PASSED CHECKS: {report['passed_checks']}")
661
+ status = "PASS" if report['compliant'] else "FAIL"
662
+ print(f"STATUS: {status}")
663
+
664
+ sys.exit(0 if report['compliant'] else 1)
665
+
666
+
667
+ if __name__ == "__main__":
668
+ # Fix missing import
669
+ import re
670
+ main()